C++程序  |  81行  |  2.9 KB

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "namespace.h"

#include <errno.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <sched.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#include "log.h"

static const char kNetNsDir[] = "/data/vendor/var/run/netns";

// Set the current namespace to that of the /proc/<pid>/ns/net provided in
// |path|. This may be a link or mount point for that same file, anything that
// when opened will be an fd usable by setns is fine.
static bool setNamespaceFromPath(const char* path) {
    int nsFd = open(path, O_RDONLY | O_CLOEXEC);
    if (nsFd == -1) {
        loge("Cannot open network namespace at '%s': %s\n",
             path, strerror(errno));
        return false;
    }

    if (setns(nsFd, CLONE_NEWNET) == -1) {
        loge("Cannot set network namespace at '%s': %s\n",
             path, strerror(errno));
        close(nsFd);
        return false;
    }
    close(nsFd);
    return true;
}

bool setNetworkNamespace(const char* ns) {
    // There is a file in the net namespace dir (usually /var/run/netns) with
    // the same name as the namespace. This file is bound to /proc/<pid>/ns/net
    // by the 'ip' command when the namespace is created. This allows us to
    // access the file of a process running in that network namespace without
    // knowing its pid, knowing the namespace name is enough.
    //
    // We are going to call setns which requires a file descriptor to that proc
    // file in /proc/<pid>/net. The process has to already be running in that
    // namespace. Since the file in the net namespace dir has been bound to
    // such a file already we just have to open /var/run/netns/<namespace> and
    // we have the required file descriptor.
    char nsPath[PATH_MAX];
    snprintf(nsPath, sizeof(nsPath), "%s/%s", kNetNsDir, ns);
    return setNamespaceFromPath(nsPath);
}

bool setNetworkNamespace(pid_t pid) {
    // If we know the pid we can create the path to the /proc file right away
    // and use that when we call setns.
    char nsPath[PATH_MAX];
    static_assert(sizeof(pid_t) <= sizeof(unsigned long long),
                  "Cast requires sizeof(pid_t) <= sizeof(unsigned long long)");
    snprintf(nsPath, sizeof(nsPath), "/proc/%llu/ns/net/",
             static_cast<unsigned long long>(pid));
    return setNamespaceFromPath(nsPath);
}