• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "createns"
18 #include <log/log.h>
19 
20 #include <cutils/properties.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <inttypes.h>
24 #include <sched.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/mount.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 
32 #include <limits>
33 #include <string>
34 #include <vector>
35 
36 static const char kNamespacePath[] = "/data/vendor/var/run/netns/";
37 static const char kProcNsNet[] = "/proc/self/ns/net";
38 
39 class Fd {
40 public:
Fd(int fd)41     explicit Fd(int fd) : mFd(fd) { }
42     Fd(const Fd&) = delete;
~Fd()43     ~Fd() {
44         if (mFd != -1) {
45             ::close(mFd);
46             mFd = -1;
47         }
48     }
49 
get() const50     int get() const { return mFd; }
51     Fd& operator=(const Fd&) = delete;
52 private:
53     int mFd;
54 };
55 
usage(const char * program)56 static void usage(const char* program) {
57     ALOGE("%s <namespace>", program);
58 }
59 
removeFile(const char * file)60 static bool removeFile(const char* file) {
61     if (::unlink(file) == -1) {
62         ALOGE("Failed to unlink file '%s': %s", file, strerror(errno));
63         return false;
64     }
65     return true;
66 }
67 
getNamespacePath(const char * name)68 static std::string getNamespacePath(const char* name) {
69     size_t len = strlen(name);
70     if (len == 0) {
71         ALOGE("Must provide a namespace argument that is not empty");
72         return std::string();
73     }
74 
75     if (std::numeric_limits<size_t>::max() - sizeof(kNamespacePath) < len) {
76         // The argument is so big the resulting string can't fit in size_t
77         ALOGE("Namespace argument too long");
78         return std::string();
79     }
80 
81     std::vector<char> nsPath(sizeof(kNamespacePath) + len);
82     size_t totalSize = strlcpy(nsPath.data(), kNamespacePath, nsPath.size());
83     if (totalSize >= nsPath.size()) {
84         // The resulting string had to be concatenated to fit, this is a logic
85         // error in the code above that determines the size of the data.
86         ALOGE("Could not create namespace path");
87         return std::string();
88     }
89     totalSize = strlcat(nsPath.data(), name, nsPath.size());
90     if (totalSize >= nsPath.size()) {
91         // The resulting string had to be concatenated to fit, this is a logic
92         // error in the code above that determines the size of the data.
93         ALOGE("Could not append to namespace path");
94         return std::string();
95     }
96     return nsPath.data();
97 }
98 
writeNamespacePid(const char * name,pid_t pid)99 static bool writeNamespacePid(const char* name, pid_t pid) {
100     std::string path = getNamespacePath(name);
101     if (path.empty()) {
102         return false;
103     }
104     path += ".pid";
105 
106     Fd fd(::open(path.c_str(),
107                  O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC,
108                  S_IRUSR | S_IWUSR | S_IRGRP));
109     if (fd.get() == -1) {
110         ALOGE("Unable to create file '%s': %s", path.c_str(), strerror(errno));
111         return false;
112     }
113 
114     // In order to safely print a pid_t we use int64_t with a known format
115     // specifier. Ensure that a pid_t will fit in a pid_t. According to POSIX
116     // pid_t is signed.
117     static_assert(sizeof(pid_t) <= sizeof(int64_t),
118                   "pid_t is larger than int64_t");
119     char pidString[32];
120     int printed = snprintf(pidString,
121                            sizeof(pidString),
122                            "%" PRId64,
123                            static_cast<int64_t>(pid));
124     if (printed <= 0) {
125         ALOGE("Unabled to created PID string for writing");
126         removeFile(path.c_str());
127         return false;
128     }
129 
130     const char* toPrint = pidString;
131     int remaining = printed;
132     for (;;) {
133         int result = ::write(fd.get(), toPrint, remaining);
134         if (result < 0) {
135             if (errno == EINTR) {
136                 continue;
137             }
138             ALOGE("Unable to write pid to file %s: %s",
139                   path.c_str(), strerror(errno));
140             removeFile(path.c_str());
141             return false;
142         } else if (result < printed) {
143             remaining -= result;
144             toPrint += result;
145         } else {
146             break;
147         }
148     }
149     return true;
150 }
151 
main(int argc,char * argv[])152 int main(int argc, char* argv[]) {
153     if (argc != 2) {
154         usage(argv[0]);
155         return 1;
156     }
157     if (::unshare(CLONE_NEWNET) != 0) {
158         ALOGE("Failed to create network namespace '%s': %s",
159               argv[1],
160               strerror(errno));
161         return 1;
162     }
163 
164     std::string path = getNamespacePath(argv[1]);
165     if (path.empty()) {
166         return 1;
167     }
168     {
169         // Open and then immediately close the fd
170         Fd fd(::open(path.c_str(), O_CREAT|O_RDONLY, S_IRUSR | S_IRGRP));
171         if (fd.get() == -1) {
172             ALOGE("Failed to open file %s: %s", path.c_str(), strerror(errno));
173             return 1;
174         }
175     }
176     if (::mount(kProcNsNet, path.c_str(), nullptr, MS_BIND, nullptr) != 0) {
177         ALOGE("Failed to bind %s to %s: %s",
178               kProcNsNet,
179               path.c_str(),
180               strerror(errno));
181         // Clean up on failure
182         removeFile(path.c_str());
183         return 1;
184     }
185 
186     if (!writeNamespacePid(argv[1], ::getpid())) {
187         return 1;
188     }
189     property_set("vendor.qemu.networknamespace", "ready");
190 
191     for (;;) {
192         pause();
193     }
194 
195     return 0;
196 }
197 
198