1*14f98438SChristian Brauner // SPDX-License-Identifier: GPL-2.0
2*14f98438SChristian Brauner
3*14f98438SChristian Brauner #include <assert.h>
4*14f98438SChristian Brauner #include <fcntl.h>
5*14f98438SChristian Brauner #include <inttypes.h>
6*14f98438SChristian Brauner #include <libgen.h>
7*14f98438SChristian Brauner #include <limits.h>
8*14f98438SChristian Brauner #include <pthread.h>
9*14f98438SChristian Brauner #include <string.h>
10*14f98438SChristian Brauner #include <sys/mount.h>
11*14f98438SChristian Brauner #include <poll.h>
12*14f98438SChristian Brauner #include <sys/epoll.h>
13*14f98438SChristian Brauner #include <sys/resource.h>
14*14f98438SChristian Brauner #include <sys/stat.h>
15*14f98438SChristian Brauner #include <sys/socket.h>
16*14f98438SChristian Brauner #include <sys/un.h>
17*14f98438SChristian Brauner #include <unistd.h>
18*14f98438SChristian Brauner #include <linux/fs.h>
19*14f98438SChristian Brauner #include <linux/limits.h>
20*14f98438SChristian Brauner #include <linux/nsfs.h>
21*14f98438SChristian Brauner #include "../kselftest_harness.h"
22*14f98438SChristian Brauner
TEST(nsid_mntns_basic)23*14f98438SChristian Brauner TEST(nsid_mntns_basic)
24*14f98438SChristian Brauner {
25*14f98438SChristian Brauner __u64 mnt_ns_id = 0;
26*14f98438SChristian Brauner int fd_mntns;
27*14f98438SChristian Brauner int ret;
28*14f98438SChristian Brauner
29*14f98438SChristian Brauner /* Open the current mount namespace */
30*14f98438SChristian Brauner fd_mntns = open("/proc/self/ns/mnt", O_RDONLY);
31*14f98438SChristian Brauner ASSERT_GE(fd_mntns, 0);
32*14f98438SChristian Brauner
33*14f98438SChristian Brauner /* Get the mount namespace ID */
34*14f98438SChristian Brauner ret = ioctl(fd_mntns, NS_GET_MNTNS_ID, &mnt_ns_id);
35*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
36*14f98438SChristian Brauner ASSERT_NE(mnt_ns_id, 0);
37*14f98438SChristian Brauner
38*14f98438SChristian Brauner /* Verify we can get the same ID again */
39*14f98438SChristian Brauner __u64 mnt_ns_id2 = 0;
40*14f98438SChristian Brauner ret = ioctl(fd_mntns, NS_GET_ID, &mnt_ns_id2);
41*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
42*14f98438SChristian Brauner ASSERT_EQ(mnt_ns_id, mnt_ns_id2);
43*14f98438SChristian Brauner
44*14f98438SChristian Brauner close(fd_mntns);
45*14f98438SChristian Brauner }
46*14f98438SChristian Brauner
TEST(nsid_mntns_separate)47*14f98438SChristian Brauner TEST(nsid_mntns_separate)
48*14f98438SChristian Brauner {
49*14f98438SChristian Brauner __u64 parent_mnt_ns_id = 0;
50*14f98438SChristian Brauner __u64 child_mnt_ns_id = 0;
51*14f98438SChristian Brauner int fd_parent_mntns, fd_child_mntns;
52*14f98438SChristian Brauner int ret;
53*14f98438SChristian Brauner pid_t pid;
54*14f98438SChristian Brauner int pipefd[2];
55*14f98438SChristian Brauner
56*14f98438SChristian Brauner /* Get parent's mount namespace ID */
57*14f98438SChristian Brauner fd_parent_mntns = open("/proc/self/ns/mnt", O_RDONLY);
58*14f98438SChristian Brauner ASSERT_GE(fd_parent_mntns, 0);
59*14f98438SChristian Brauner ret = ioctl(fd_parent_mntns, NS_GET_ID, &parent_mnt_ns_id);
60*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
61*14f98438SChristian Brauner ASSERT_NE(parent_mnt_ns_id, 0);
62*14f98438SChristian Brauner
63*14f98438SChristian Brauner /* Create a pipe for synchronization */
64*14f98438SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
65*14f98438SChristian Brauner
66*14f98438SChristian Brauner pid = fork();
67*14f98438SChristian Brauner ASSERT_GE(pid, 0);
68*14f98438SChristian Brauner
69*14f98438SChristian Brauner if (pid == 0) {
70*14f98438SChristian Brauner /* Child process */
71*14f98438SChristian Brauner close(pipefd[0]);
72*14f98438SChristian Brauner
73*14f98438SChristian Brauner /* Create new mount namespace */
74*14f98438SChristian Brauner ret = unshare(CLONE_NEWNS);
75*14f98438SChristian Brauner if (ret != 0) {
76*14f98438SChristian Brauner /* Skip test if we don't have permission */
77*14f98438SChristian Brauner if (errno == EPERM || errno == EACCES) {
78*14f98438SChristian Brauner write(pipefd[1], "S", 1); /* Signal skip */
79*14f98438SChristian Brauner _exit(0);
80*14f98438SChristian Brauner }
81*14f98438SChristian Brauner _exit(1);
82*14f98438SChristian Brauner }
83*14f98438SChristian Brauner
84*14f98438SChristian Brauner /* Signal success */
85*14f98438SChristian Brauner write(pipefd[1], "Y", 1);
86*14f98438SChristian Brauner close(pipefd[1]);
87*14f98438SChristian Brauner
88*14f98438SChristian Brauner /* Keep namespace alive */
89*14f98438SChristian Brauner pause();
90*14f98438SChristian Brauner _exit(0);
91*14f98438SChristian Brauner }
92*14f98438SChristian Brauner
93*14f98438SChristian Brauner /* Parent process */
94*14f98438SChristian Brauner close(pipefd[1]);
95*14f98438SChristian Brauner
96*14f98438SChristian Brauner char buf;
97*14f98438SChristian Brauner ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
98*14f98438SChristian Brauner close(pipefd[0]);
99*14f98438SChristian Brauner
100*14f98438SChristian Brauner if (buf == 'S') {
101*14f98438SChristian Brauner /* Child couldn't create namespace, skip test */
102*14f98438SChristian Brauner kill(pid, SIGTERM);
103*14f98438SChristian Brauner waitpid(pid, NULL, 0);
104*14f98438SChristian Brauner close(fd_parent_mntns);
105*14f98438SChristian Brauner SKIP(return, "No permission to create mount namespace");
106*14f98438SChristian Brauner }
107*14f98438SChristian Brauner
108*14f98438SChristian Brauner ASSERT_EQ(buf, 'Y');
109*14f98438SChristian Brauner
110*14f98438SChristian Brauner /* Open child's mount namespace */
111*14f98438SChristian Brauner char path[256];
112*14f98438SChristian Brauner snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
113*14f98438SChristian Brauner fd_child_mntns = open(path, O_RDONLY);
114*14f98438SChristian Brauner ASSERT_GE(fd_child_mntns, 0);
115*14f98438SChristian Brauner
116*14f98438SChristian Brauner /* Get child's mount namespace ID */
117*14f98438SChristian Brauner ret = ioctl(fd_child_mntns, NS_GET_ID, &child_mnt_ns_id);
118*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
119*14f98438SChristian Brauner ASSERT_NE(child_mnt_ns_id, 0);
120*14f98438SChristian Brauner
121*14f98438SChristian Brauner /* Parent and child should have different mount namespace IDs */
122*14f98438SChristian Brauner ASSERT_NE(parent_mnt_ns_id, child_mnt_ns_id);
123*14f98438SChristian Brauner
124*14f98438SChristian Brauner close(fd_parent_mntns);
125*14f98438SChristian Brauner close(fd_child_mntns);
126*14f98438SChristian Brauner
127*14f98438SChristian Brauner /* Clean up child process */
128*14f98438SChristian Brauner kill(pid, SIGTERM);
129*14f98438SChristian Brauner waitpid(pid, NULL, 0);
130*14f98438SChristian Brauner }
131*14f98438SChristian Brauner
TEST(nsid_cgroupns_basic)132*14f98438SChristian Brauner TEST(nsid_cgroupns_basic)
133*14f98438SChristian Brauner {
134*14f98438SChristian Brauner __u64 cgroup_ns_id = 0;
135*14f98438SChristian Brauner int fd_cgroupns;
136*14f98438SChristian Brauner int ret;
137*14f98438SChristian Brauner
138*14f98438SChristian Brauner /* Open the current cgroup namespace */
139*14f98438SChristian Brauner fd_cgroupns = open("/proc/self/ns/cgroup", O_RDONLY);
140*14f98438SChristian Brauner ASSERT_GE(fd_cgroupns, 0);
141*14f98438SChristian Brauner
142*14f98438SChristian Brauner /* Get the cgroup namespace ID */
143*14f98438SChristian Brauner ret = ioctl(fd_cgroupns, NS_GET_ID, &cgroup_ns_id);
144*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
145*14f98438SChristian Brauner ASSERT_NE(cgroup_ns_id, 0);
146*14f98438SChristian Brauner
147*14f98438SChristian Brauner /* Verify we can get the same ID again */
148*14f98438SChristian Brauner __u64 cgroup_ns_id2 = 0;
149*14f98438SChristian Brauner ret = ioctl(fd_cgroupns, NS_GET_ID, &cgroup_ns_id2);
150*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
151*14f98438SChristian Brauner ASSERT_EQ(cgroup_ns_id, cgroup_ns_id2);
152*14f98438SChristian Brauner
153*14f98438SChristian Brauner close(fd_cgroupns);
154*14f98438SChristian Brauner }
155*14f98438SChristian Brauner
TEST(nsid_cgroupns_separate)156*14f98438SChristian Brauner TEST(nsid_cgroupns_separate)
157*14f98438SChristian Brauner {
158*14f98438SChristian Brauner __u64 parent_cgroup_ns_id = 0;
159*14f98438SChristian Brauner __u64 child_cgroup_ns_id = 0;
160*14f98438SChristian Brauner int fd_parent_cgroupns, fd_child_cgroupns;
161*14f98438SChristian Brauner int ret;
162*14f98438SChristian Brauner pid_t pid;
163*14f98438SChristian Brauner int pipefd[2];
164*14f98438SChristian Brauner
165*14f98438SChristian Brauner /* Get parent's cgroup namespace ID */
166*14f98438SChristian Brauner fd_parent_cgroupns = open("/proc/self/ns/cgroup", O_RDONLY);
167*14f98438SChristian Brauner ASSERT_GE(fd_parent_cgroupns, 0);
168*14f98438SChristian Brauner ret = ioctl(fd_parent_cgroupns, NS_GET_ID, &parent_cgroup_ns_id);
169*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
170*14f98438SChristian Brauner ASSERT_NE(parent_cgroup_ns_id, 0);
171*14f98438SChristian Brauner
172*14f98438SChristian Brauner /* Create a pipe for synchronization */
173*14f98438SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
174*14f98438SChristian Brauner
175*14f98438SChristian Brauner pid = fork();
176*14f98438SChristian Brauner ASSERT_GE(pid, 0);
177*14f98438SChristian Brauner
178*14f98438SChristian Brauner if (pid == 0) {
179*14f98438SChristian Brauner /* Child process */
180*14f98438SChristian Brauner close(pipefd[0]);
181*14f98438SChristian Brauner
182*14f98438SChristian Brauner /* Create new cgroup namespace */
183*14f98438SChristian Brauner ret = unshare(CLONE_NEWCGROUP);
184*14f98438SChristian Brauner if (ret != 0) {
185*14f98438SChristian Brauner /* Skip test if we don't have permission */
186*14f98438SChristian Brauner if (errno == EPERM || errno == EACCES) {
187*14f98438SChristian Brauner write(pipefd[1], "S", 1); /* Signal skip */
188*14f98438SChristian Brauner _exit(0);
189*14f98438SChristian Brauner }
190*14f98438SChristian Brauner _exit(1);
191*14f98438SChristian Brauner }
192*14f98438SChristian Brauner
193*14f98438SChristian Brauner /* Signal success */
194*14f98438SChristian Brauner write(pipefd[1], "Y", 1);
195*14f98438SChristian Brauner close(pipefd[1]);
196*14f98438SChristian Brauner
197*14f98438SChristian Brauner /* Keep namespace alive */
198*14f98438SChristian Brauner pause();
199*14f98438SChristian Brauner _exit(0);
200*14f98438SChristian Brauner }
201*14f98438SChristian Brauner
202*14f98438SChristian Brauner /* Parent process */
203*14f98438SChristian Brauner close(pipefd[1]);
204*14f98438SChristian Brauner
205*14f98438SChristian Brauner char buf;
206*14f98438SChristian Brauner ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
207*14f98438SChristian Brauner close(pipefd[0]);
208*14f98438SChristian Brauner
209*14f98438SChristian Brauner if (buf == 'S') {
210*14f98438SChristian Brauner /* Child couldn't create namespace, skip test */
211*14f98438SChristian Brauner kill(pid, SIGTERM);
212*14f98438SChristian Brauner waitpid(pid, NULL, 0);
213*14f98438SChristian Brauner close(fd_parent_cgroupns);
214*14f98438SChristian Brauner SKIP(return, "No permission to create cgroup namespace");
215*14f98438SChristian Brauner }
216*14f98438SChristian Brauner
217*14f98438SChristian Brauner ASSERT_EQ(buf, 'Y');
218*14f98438SChristian Brauner
219*14f98438SChristian Brauner /* Open child's cgroup namespace */
220*14f98438SChristian Brauner char path[256];
221*14f98438SChristian Brauner snprintf(path, sizeof(path), "/proc/%d/ns/cgroup", pid);
222*14f98438SChristian Brauner fd_child_cgroupns = open(path, O_RDONLY);
223*14f98438SChristian Brauner ASSERT_GE(fd_child_cgroupns, 0);
224*14f98438SChristian Brauner
225*14f98438SChristian Brauner /* Get child's cgroup namespace ID */
226*14f98438SChristian Brauner ret = ioctl(fd_child_cgroupns, NS_GET_ID, &child_cgroup_ns_id);
227*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
228*14f98438SChristian Brauner ASSERT_NE(child_cgroup_ns_id, 0);
229*14f98438SChristian Brauner
230*14f98438SChristian Brauner /* Parent and child should have different cgroup namespace IDs */
231*14f98438SChristian Brauner ASSERT_NE(parent_cgroup_ns_id, child_cgroup_ns_id);
232*14f98438SChristian Brauner
233*14f98438SChristian Brauner close(fd_parent_cgroupns);
234*14f98438SChristian Brauner close(fd_child_cgroupns);
235*14f98438SChristian Brauner
236*14f98438SChristian Brauner /* Clean up child process */
237*14f98438SChristian Brauner kill(pid, SIGTERM);
238*14f98438SChristian Brauner waitpid(pid, NULL, 0);
239*14f98438SChristian Brauner }
240*14f98438SChristian Brauner
TEST(nsid_ipcns_basic)241*14f98438SChristian Brauner TEST(nsid_ipcns_basic)
242*14f98438SChristian Brauner {
243*14f98438SChristian Brauner __u64 ipc_ns_id = 0;
244*14f98438SChristian Brauner int fd_ipcns;
245*14f98438SChristian Brauner int ret;
246*14f98438SChristian Brauner
247*14f98438SChristian Brauner /* Open the current IPC namespace */
248*14f98438SChristian Brauner fd_ipcns = open("/proc/self/ns/ipc", O_RDONLY);
249*14f98438SChristian Brauner ASSERT_GE(fd_ipcns, 0);
250*14f98438SChristian Brauner
251*14f98438SChristian Brauner /* Get the IPC namespace ID */
252*14f98438SChristian Brauner ret = ioctl(fd_ipcns, NS_GET_ID, &ipc_ns_id);
253*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
254*14f98438SChristian Brauner ASSERT_NE(ipc_ns_id, 0);
255*14f98438SChristian Brauner
256*14f98438SChristian Brauner /* Verify we can get the same ID again */
257*14f98438SChristian Brauner __u64 ipc_ns_id2 = 0;
258*14f98438SChristian Brauner ret = ioctl(fd_ipcns, NS_GET_ID, &ipc_ns_id2);
259*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
260*14f98438SChristian Brauner ASSERT_EQ(ipc_ns_id, ipc_ns_id2);
261*14f98438SChristian Brauner
262*14f98438SChristian Brauner close(fd_ipcns);
263*14f98438SChristian Brauner }
264*14f98438SChristian Brauner
TEST(nsid_ipcns_separate)265*14f98438SChristian Brauner TEST(nsid_ipcns_separate)
266*14f98438SChristian Brauner {
267*14f98438SChristian Brauner __u64 parent_ipc_ns_id = 0;
268*14f98438SChristian Brauner __u64 child_ipc_ns_id = 0;
269*14f98438SChristian Brauner int fd_parent_ipcns, fd_child_ipcns;
270*14f98438SChristian Brauner int ret;
271*14f98438SChristian Brauner pid_t pid;
272*14f98438SChristian Brauner int pipefd[2];
273*14f98438SChristian Brauner
274*14f98438SChristian Brauner /* Get parent's IPC namespace ID */
275*14f98438SChristian Brauner fd_parent_ipcns = open("/proc/self/ns/ipc", O_RDONLY);
276*14f98438SChristian Brauner ASSERT_GE(fd_parent_ipcns, 0);
277*14f98438SChristian Brauner ret = ioctl(fd_parent_ipcns, NS_GET_ID, &parent_ipc_ns_id);
278*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
279*14f98438SChristian Brauner ASSERT_NE(parent_ipc_ns_id, 0);
280*14f98438SChristian Brauner
281*14f98438SChristian Brauner /* Create a pipe for synchronization */
282*14f98438SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
283*14f98438SChristian Brauner
284*14f98438SChristian Brauner pid = fork();
285*14f98438SChristian Brauner ASSERT_GE(pid, 0);
286*14f98438SChristian Brauner
287*14f98438SChristian Brauner if (pid == 0) {
288*14f98438SChristian Brauner /* Child process */
289*14f98438SChristian Brauner close(pipefd[0]);
290*14f98438SChristian Brauner
291*14f98438SChristian Brauner /* Create new IPC namespace */
292*14f98438SChristian Brauner ret = unshare(CLONE_NEWIPC);
293*14f98438SChristian Brauner if (ret != 0) {
294*14f98438SChristian Brauner /* Skip test if we don't have permission */
295*14f98438SChristian Brauner if (errno == EPERM || errno == EACCES) {
296*14f98438SChristian Brauner write(pipefd[1], "S", 1); /* Signal skip */
297*14f98438SChristian Brauner _exit(0);
298*14f98438SChristian Brauner }
299*14f98438SChristian Brauner _exit(1);
300*14f98438SChristian Brauner }
301*14f98438SChristian Brauner
302*14f98438SChristian Brauner /* Signal success */
303*14f98438SChristian Brauner write(pipefd[1], "Y", 1);
304*14f98438SChristian Brauner close(pipefd[1]);
305*14f98438SChristian Brauner
306*14f98438SChristian Brauner /* Keep namespace alive */
307*14f98438SChristian Brauner pause();
308*14f98438SChristian Brauner _exit(0);
309*14f98438SChristian Brauner }
310*14f98438SChristian Brauner
311*14f98438SChristian Brauner /* Parent process */
312*14f98438SChristian Brauner close(pipefd[1]);
313*14f98438SChristian Brauner
314*14f98438SChristian Brauner char buf;
315*14f98438SChristian Brauner ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
316*14f98438SChristian Brauner close(pipefd[0]);
317*14f98438SChristian Brauner
318*14f98438SChristian Brauner if (buf == 'S') {
319*14f98438SChristian Brauner /* Child couldn't create namespace, skip test */
320*14f98438SChristian Brauner kill(pid, SIGTERM);
321*14f98438SChristian Brauner waitpid(pid, NULL, 0);
322*14f98438SChristian Brauner close(fd_parent_ipcns);
323*14f98438SChristian Brauner SKIP(return, "No permission to create IPC namespace");
324*14f98438SChristian Brauner }
325*14f98438SChristian Brauner
326*14f98438SChristian Brauner ASSERT_EQ(buf, 'Y');
327*14f98438SChristian Brauner
328*14f98438SChristian Brauner /* Open child's IPC namespace */
329*14f98438SChristian Brauner char path[256];
330*14f98438SChristian Brauner snprintf(path, sizeof(path), "/proc/%d/ns/ipc", pid);
331*14f98438SChristian Brauner fd_child_ipcns = open(path, O_RDONLY);
332*14f98438SChristian Brauner ASSERT_GE(fd_child_ipcns, 0);
333*14f98438SChristian Brauner
334*14f98438SChristian Brauner /* Get child's IPC namespace ID */
335*14f98438SChristian Brauner ret = ioctl(fd_child_ipcns, NS_GET_ID, &child_ipc_ns_id);
336*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
337*14f98438SChristian Brauner ASSERT_NE(child_ipc_ns_id, 0);
338*14f98438SChristian Brauner
339*14f98438SChristian Brauner /* Parent and child should have different IPC namespace IDs */
340*14f98438SChristian Brauner ASSERT_NE(parent_ipc_ns_id, child_ipc_ns_id);
341*14f98438SChristian Brauner
342*14f98438SChristian Brauner close(fd_parent_ipcns);
343*14f98438SChristian Brauner close(fd_child_ipcns);
344*14f98438SChristian Brauner
345*14f98438SChristian Brauner /* Clean up child process */
346*14f98438SChristian Brauner kill(pid, SIGTERM);
347*14f98438SChristian Brauner waitpid(pid, NULL, 0);
348*14f98438SChristian Brauner }
349*14f98438SChristian Brauner
TEST(nsid_utsns_basic)350*14f98438SChristian Brauner TEST(nsid_utsns_basic)
351*14f98438SChristian Brauner {
352*14f98438SChristian Brauner __u64 uts_ns_id = 0;
353*14f98438SChristian Brauner int fd_utsns;
354*14f98438SChristian Brauner int ret;
355*14f98438SChristian Brauner
356*14f98438SChristian Brauner /* Open the current UTS namespace */
357*14f98438SChristian Brauner fd_utsns = open("/proc/self/ns/uts", O_RDONLY);
358*14f98438SChristian Brauner ASSERT_GE(fd_utsns, 0);
359*14f98438SChristian Brauner
360*14f98438SChristian Brauner /* Get the UTS namespace ID */
361*14f98438SChristian Brauner ret = ioctl(fd_utsns, NS_GET_ID, &uts_ns_id);
362*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
363*14f98438SChristian Brauner ASSERT_NE(uts_ns_id, 0);
364*14f98438SChristian Brauner
365*14f98438SChristian Brauner /* Verify we can get the same ID again */
366*14f98438SChristian Brauner __u64 uts_ns_id2 = 0;
367*14f98438SChristian Brauner ret = ioctl(fd_utsns, NS_GET_ID, &uts_ns_id2);
368*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
369*14f98438SChristian Brauner ASSERT_EQ(uts_ns_id, uts_ns_id2);
370*14f98438SChristian Brauner
371*14f98438SChristian Brauner close(fd_utsns);
372*14f98438SChristian Brauner }
373*14f98438SChristian Brauner
TEST(nsid_utsns_separate)374*14f98438SChristian Brauner TEST(nsid_utsns_separate)
375*14f98438SChristian Brauner {
376*14f98438SChristian Brauner __u64 parent_uts_ns_id = 0;
377*14f98438SChristian Brauner __u64 child_uts_ns_id = 0;
378*14f98438SChristian Brauner int fd_parent_utsns, fd_child_utsns;
379*14f98438SChristian Brauner int ret;
380*14f98438SChristian Brauner pid_t pid;
381*14f98438SChristian Brauner int pipefd[2];
382*14f98438SChristian Brauner
383*14f98438SChristian Brauner /* Get parent's UTS namespace ID */
384*14f98438SChristian Brauner fd_parent_utsns = open("/proc/self/ns/uts", O_RDONLY);
385*14f98438SChristian Brauner ASSERT_GE(fd_parent_utsns, 0);
386*14f98438SChristian Brauner ret = ioctl(fd_parent_utsns, NS_GET_ID, &parent_uts_ns_id);
387*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
388*14f98438SChristian Brauner ASSERT_NE(parent_uts_ns_id, 0);
389*14f98438SChristian Brauner
390*14f98438SChristian Brauner /* Create a pipe for synchronization */
391*14f98438SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
392*14f98438SChristian Brauner
393*14f98438SChristian Brauner pid = fork();
394*14f98438SChristian Brauner ASSERT_GE(pid, 0);
395*14f98438SChristian Brauner
396*14f98438SChristian Brauner if (pid == 0) {
397*14f98438SChristian Brauner /* Child process */
398*14f98438SChristian Brauner close(pipefd[0]);
399*14f98438SChristian Brauner
400*14f98438SChristian Brauner /* Create new UTS namespace */
401*14f98438SChristian Brauner ret = unshare(CLONE_NEWUTS);
402*14f98438SChristian Brauner if (ret != 0) {
403*14f98438SChristian Brauner /* Skip test if we don't have permission */
404*14f98438SChristian Brauner if (errno == EPERM || errno == EACCES) {
405*14f98438SChristian Brauner write(pipefd[1], "S", 1); /* Signal skip */
406*14f98438SChristian Brauner _exit(0);
407*14f98438SChristian Brauner }
408*14f98438SChristian Brauner _exit(1);
409*14f98438SChristian Brauner }
410*14f98438SChristian Brauner
411*14f98438SChristian Brauner /* Signal success */
412*14f98438SChristian Brauner write(pipefd[1], "Y", 1);
413*14f98438SChristian Brauner close(pipefd[1]);
414*14f98438SChristian Brauner
415*14f98438SChristian Brauner /* Keep namespace alive */
416*14f98438SChristian Brauner pause();
417*14f98438SChristian Brauner _exit(0);
418*14f98438SChristian Brauner }
419*14f98438SChristian Brauner
420*14f98438SChristian Brauner /* Parent process */
421*14f98438SChristian Brauner close(pipefd[1]);
422*14f98438SChristian Brauner
423*14f98438SChristian Brauner char buf;
424*14f98438SChristian Brauner ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
425*14f98438SChristian Brauner close(pipefd[0]);
426*14f98438SChristian Brauner
427*14f98438SChristian Brauner if (buf == 'S') {
428*14f98438SChristian Brauner /* Child couldn't create namespace, skip test */
429*14f98438SChristian Brauner kill(pid, SIGTERM);
430*14f98438SChristian Brauner waitpid(pid, NULL, 0);
431*14f98438SChristian Brauner close(fd_parent_utsns);
432*14f98438SChristian Brauner SKIP(return, "No permission to create UTS namespace");
433*14f98438SChristian Brauner }
434*14f98438SChristian Brauner
435*14f98438SChristian Brauner ASSERT_EQ(buf, 'Y');
436*14f98438SChristian Brauner
437*14f98438SChristian Brauner /* Open child's UTS namespace */
438*14f98438SChristian Brauner char path[256];
439*14f98438SChristian Brauner snprintf(path, sizeof(path), "/proc/%d/ns/uts", pid);
440*14f98438SChristian Brauner fd_child_utsns = open(path, O_RDONLY);
441*14f98438SChristian Brauner ASSERT_GE(fd_child_utsns, 0);
442*14f98438SChristian Brauner
443*14f98438SChristian Brauner /* Get child's UTS namespace ID */
444*14f98438SChristian Brauner ret = ioctl(fd_child_utsns, NS_GET_ID, &child_uts_ns_id);
445*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
446*14f98438SChristian Brauner ASSERT_NE(child_uts_ns_id, 0);
447*14f98438SChristian Brauner
448*14f98438SChristian Brauner /* Parent and child should have different UTS namespace IDs */
449*14f98438SChristian Brauner ASSERT_NE(parent_uts_ns_id, child_uts_ns_id);
450*14f98438SChristian Brauner
451*14f98438SChristian Brauner close(fd_parent_utsns);
452*14f98438SChristian Brauner close(fd_child_utsns);
453*14f98438SChristian Brauner
454*14f98438SChristian Brauner /* Clean up child process */
455*14f98438SChristian Brauner kill(pid, SIGTERM);
456*14f98438SChristian Brauner waitpid(pid, NULL, 0);
457*14f98438SChristian Brauner }
458*14f98438SChristian Brauner
TEST(nsid_userns_basic)459*14f98438SChristian Brauner TEST(nsid_userns_basic)
460*14f98438SChristian Brauner {
461*14f98438SChristian Brauner __u64 user_ns_id = 0;
462*14f98438SChristian Brauner int fd_userns;
463*14f98438SChristian Brauner int ret;
464*14f98438SChristian Brauner
465*14f98438SChristian Brauner /* Open the current user namespace */
466*14f98438SChristian Brauner fd_userns = open("/proc/self/ns/user", O_RDONLY);
467*14f98438SChristian Brauner ASSERT_GE(fd_userns, 0);
468*14f98438SChristian Brauner
469*14f98438SChristian Brauner /* Get the user namespace ID */
470*14f98438SChristian Brauner ret = ioctl(fd_userns, NS_GET_ID, &user_ns_id);
471*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
472*14f98438SChristian Brauner ASSERT_NE(user_ns_id, 0);
473*14f98438SChristian Brauner
474*14f98438SChristian Brauner /* Verify we can get the same ID again */
475*14f98438SChristian Brauner __u64 user_ns_id2 = 0;
476*14f98438SChristian Brauner ret = ioctl(fd_userns, NS_GET_ID, &user_ns_id2);
477*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
478*14f98438SChristian Brauner ASSERT_EQ(user_ns_id, user_ns_id2);
479*14f98438SChristian Brauner
480*14f98438SChristian Brauner close(fd_userns);
481*14f98438SChristian Brauner }
482*14f98438SChristian Brauner
TEST(nsid_userns_separate)483*14f98438SChristian Brauner TEST(nsid_userns_separate)
484*14f98438SChristian Brauner {
485*14f98438SChristian Brauner __u64 parent_user_ns_id = 0;
486*14f98438SChristian Brauner __u64 child_user_ns_id = 0;
487*14f98438SChristian Brauner int fd_parent_userns, fd_child_userns;
488*14f98438SChristian Brauner int ret;
489*14f98438SChristian Brauner pid_t pid;
490*14f98438SChristian Brauner int pipefd[2];
491*14f98438SChristian Brauner
492*14f98438SChristian Brauner /* Get parent's user namespace ID */
493*14f98438SChristian Brauner fd_parent_userns = open("/proc/self/ns/user", O_RDONLY);
494*14f98438SChristian Brauner ASSERT_GE(fd_parent_userns, 0);
495*14f98438SChristian Brauner ret = ioctl(fd_parent_userns, NS_GET_ID, &parent_user_ns_id);
496*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
497*14f98438SChristian Brauner ASSERT_NE(parent_user_ns_id, 0);
498*14f98438SChristian Brauner
499*14f98438SChristian Brauner /* Create a pipe for synchronization */
500*14f98438SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
501*14f98438SChristian Brauner
502*14f98438SChristian Brauner pid = fork();
503*14f98438SChristian Brauner ASSERT_GE(pid, 0);
504*14f98438SChristian Brauner
505*14f98438SChristian Brauner if (pid == 0) {
506*14f98438SChristian Brauner /* Child process */
507*14f98438SChristian Brauner close(pipefd[0]);
508*14f98438SChristian Brauner
509*14f98438SChristian Brauner /* Create new user namespace */
510*14f98438SChristian Brauner ret = unshare(CLONE_NEWUSER);
511*14f98438SChristian Brauner if (ret != 0) {
512*14f98438SChristian Brauner /* Skip test if we don't have permission */
513*14f98438SChristian Brauner if (errno == EPERM || errno == EACCES) {
514*14f98438SChristian Brauner write(pipefd[1], "S", 1); /* Signal skip */
515*14f98438SChristian Brauner _exit(0);
516*14f98438SChristian Brauner }
517*14f98438SChristian Brauner _exit(1);
518*14f98438SChristian Brauner }
519*14f98438SChristian Brauner
520*14f98438SChristian Brauner /* Signal success */
521*14f98438SChristian Brauner write(pipefd[1], "Y", 1);
522*14f98438SChristian Brauner close(pipefd[1]);
523*14f98438SChristian Brauner
524*14f98438SChristian Brauner /* Keep namespace alive */
525*14f98438SChristian Brauner pause();
526*14f98438SChristian Brauner _exit(0);
527*14f98438SChristian Brauner }
528*14f98438SChristian Brauner
529*14f98438SChristian Brauner /* Parent process */
530*14f98438SChristian Brauner close(pipefd[1]);
531*14f98438SChristian Brauner
532*14f98438SChristian Brauner char buf;
533*14f98438SChristian Brauner ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
534*14f98438SChristian Brauner close(pipefd[0]);
535*14f98438SChristian Brauner
536*14f98438SChristian Brauner if (buf == 'S') {
537*14f98438SChristian Brauner /* Child couldn't create namespace, skip test */
538*14f98438SChristian Brauner kill(pid, SIGTERM);
539*14f98438SChristian Brauner waitpid(pid, NULL, 0);
540*14f98438SChristian Brauner close(fd_parent_userns);
541*14f98438SChristian Brauner SKIP(return, "No permission to create user namespace");
542*14f98438SChristian Brauner }
543*14f98438SChristian Brauner
544*14f98438SChristian Brauner ASSERT_EQ(buf, 'Y');
545*14f98438SChristian Brauner
546*14f98438SChristian Brauner /* Open child's user namespace */
547*14f98438SChristian Brauner char path[256];
548*14f98438SChristian Brauner snprintf(path, sizeof(path), "/proc/%d/ns/user", pid);
549*14f98438SChristian Brauner fd_child_userns = open(path, O_RDONLY);
550*14f98438SChristian Brauner ASSERT_GE(fd_child_userns, 0);
551*14f98438SChristian Brauner
552*14f98438SChristian Brauner /* Get child's user namespace ID */
553*14f98438SChristian Brauner ret = ioctl(fd_child_userns, NS_GET_ID, &child_user_ns_id);
554*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
555*14f98438SChristian Brauner ASSERT_NE(child_user_ns_id, 0);
556*14f98438SChristian Brauner
557*14f98438SChristian Brauner /* Parent and child should have different user namespace IDs */
558*14f98438SChristian Brauner ASSERT_NE(parent_user_ns_id, child_user_ns_id);
559*14f98438SChristian Brauner
560*14f98438SChristian Brauner close(fd_parent_userns);
561*14f98438SChristian Brauner close(fd_child_userns);
562*14f98438SChristian Brauner
563*14f98438SChristian Brauner /* Clean up child process */
564*14f98438SChristian Brauner kill(pid, SIGTERM);
565*14f98438SChristian Brauner waitpid(pid, NULL, 0);
566*14f98438SChristian Brauner }
567*14f98438SChristian Brauner
TEST(nsid_timens_basic)568*14f98438SChristian Brauner TEST(nsid_timens_basic)
569*14f98438SChristian Brauner {
570*14f98438SChristian Brauner __u64 time_ns_id = 0;
571*14f98438SChristian Brauner int fd_timens;
572*14f98438SChristian Brauner int ret;
573*14f98438SChristian Brauner
574*14f98438SChristian Brauner /* Open the current time namespace */
575*14f98438SChristian Brauner fd_timens = open("/proc/self/ns/time", O_RDONLY);
576*14f98438SChristian Brauner if (fd_timens < 0) {
577*14f98438SChristian Brauner SKIP(return, "Time namespaces not supported");
578*14f98438SChristian Brauner }
579*14f98438SChristian Brauner
580*14f98438SChristian Brauner /* Get the time namespace ID */
581*14f98438SChristian Brauner ret = ioctl(fd_timens, NS_GET_ID, &time_ns_id);
582*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
583*14f98438SChristian Brauner ASSERT_NE(time_ns_id, 0);
584*14f98438SChristian Brauner
585*14f98438SChristian Brauner /* Verify we can get the same ID again */
586*14f98438SChristian Brauner __u64 time_ns_id2 = 0;
587*14f98438SChristian Brauner ret = ioctl(fd_timens, NS_GET_ID, &time_ns_id2);
588*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
589*14f98438SChristian Brauner ASSERT_EQ(time_ns_id, time_ns_id2);
590*14f98438SChristian Brauner
591*14f98438SChristian Brauner close(fd_timens);
592*14f98438SChristian Brauner }
593*14f98438SChristian Brauner
TEST(nsid_timens_separate)594*14f98438SChristian Brauner TEST(nsid_timens_separate)
595*14f98438SChristian Brauner {
596*14f98438SChristian Brauner __u64 parent_time_ns_id = 0;
597*14f98438SChristian Brauner __u64 child_time_ns_id = 0;
598*14f98438SChristian Brauner int fd_parent_timens, fd_child_timens;
599*14f98438SChristian Brauner int ret;
600*14f98438SChristian Brauner pid_t pid;
601*14f98438SChristian Brauner int pipefd[2];
602*14f98438SChristian Brauner
603*14f98438SChristian Brauner /* Open the current time namespace */
604*14f98438SChristian Brauner fd_parent_timens = open("/proc/self/ns/time", O_RDONLY);
605*14f98438SChristian Brauner if (fd_parent_timens < 0) {
606*14f98438SChristian Brauner SKIP(return, "Time namespaces not supported");
607*14f98438SChristian Brauner }
608*14f98438SChristian Brauner
609*14f98438SChristian Brauner /* Get parent's time namespace ID */
610*14f98438SChristian Brauner ret = ioctl(fd_parent_timens, NS_GET_ID, &parent_time_ns_id);
611*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
612*14f98438SChristian Brauner ASSERT_NE(parent_time_ns_id, 0);
613*14f98438SChristian Brauner
614*14f98438SChristian Brauner /* Create a pipe for synchronization */
615*14f98438SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
616*14f98438SChristian Brauner
617*14f98438SChristian Brauner pid = fork();
618*14f98438SChristian Brauner ASSERT_GE(pid, 0);
619*14f98438SChristian Brauner
620*14f98438SChristian Brauner if (pid == 0) {
621*14f98438SChristian Brauner /* Child process */
622*14f98438SChristian Brauner close(pipefd[0]);
623*14f98438SChristian Brauner
624*14f98438SChristian Brauner /* Create new time namespace */
625*14f98438SChristian Brauner ret = unshare(CLONE_NEWTIME);
626*14f98438SChristian Brauner if (ret != 0) {
627*14f98438SChristian Brauner /* Skip test if we don't have permission */
628*14f98438SChristian Brauner if (errno == EPERM || errno == EACCES || errno == EINVAL) {
629*14f98438SChristian Brauner write(pipefd[1], "S", 1); /* Signal skip */
630*14f98438SChristian Brauner _exit(0);
631*14f98438SChristian Brauner }
632*14f98438SChristian Brauner _exit(1);
633*14f98438SChristian Brauner }
634*14f98438SChristian Brauner
635*14f98438SChristian Brauner /* Fork a grandchild to actually enter the new namespace */
636*14f98438SChristian Brauner pid_t grandchild = fork();
637*14f98438SChristian Brauner if (grandchild == 0) {
638*14f98438SChristian Brauner /* Grandchild is in the new namespace */
639*14f98438SChristian Brauner write(pipefd[1], "Y", 1);
640*14f98438SChristian Brauner close(pipefd[1]);
641*14f98438SChristian Brauner pause();
642*14f98438SChristian Brauner _exit(0);
643*14f98438SChristian Brauner } else if (grandchild > 0) {
644*14f98438SChristian Brauner /* Child writes grandchild PID and waits */
645*14f98438SChristian Brauner write(pipefd[1], "Y", 1);
646*14f98438SChristian Brauner write(pipefd[1], &grandchild, sizeof(grandchild));
647*14f98438SChristian Brauner close(pipefd[1]);
648*14f98438SChristian Brauner pause(); /* Keep the parent alive to maintain the grandchild */
649*14f98438SChristian Brauner _exit(0);
650*14f98438SChristian Brauner } else {
651*14f98438SChristian Brauner _exit(1);
652*14f98438SChristian Brauner }
653*14f98438SChristian Brauner }
654*14f98438SChristian Brauner
655*14f98438SChristian Brauner /* Parent process */
656*14f98438SChristian Brauner close(pipefd[1]);
657*14f98438SChristian Brauner
658*14f98438SChristian Brauner char buf;
659*14f98438SChristian Brauner ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
660*14f98438SChristian Brauner
661*14f98438SChristian Brauner if (buf == 'S') {
662*14f98438SChristian Brauner /* Child couldn't create namespace, skip test */
663*14f98438SChristian Brauner kill(pid, SIGTERM);
664*14f98438SChristian Brauner waitpid(pid, NULL, 0);
665*14f98438SChristian Brauner close(fd_parent_timens);
666*14f98438SChristian Brauner close(pipefd[0]);
667*14f98438SChristian Brauner SKIP(return, "Cannot create time namespace");
668*14f98438SChristian Brauner }
669*14f98438SChristian Brauner
670*14f98438SChristian Brauner ASSERT_EQ(buf, 'Y');
671*14f98438SChristian Brauner
672*14f98438SChristian Brauner pid_t grandchild_pid;
673*14f98438SChristian Brauner ASSERT_EQ(read(pipefd[0], &grandchild_pid, sizeof(grandchild_pid)), sizeof(grandchild_pid));
674*14f98438SChristian Brauner close(pipefd[0]);
675*14f98438SChristian Brauner
676*14f98438SChristian Brauner /* Open grandchild's time namespace */
677*14f98438SChristian Brauner char path[256];
678*14f98438SChristian Brauner snprintf(path, sizeof(path), "/proc/%d/ns/time", grandchild_pid);
679*14f98438SChristian Brauner fd_child_timens = open(path, O_RDONLY);
680*14f98438SChristian Brauner ASSERT_GE(fd_child_timens, 0);
681*14f98438SChristian Brauner
682*14f98438SChristian Brauner /* Get child's time namespace ID */
683*14f98438SChristian Brauner ret = ioctl(fd_child_timens, NS_GET_ID, &child_time_ns_id);
684*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
685*14f98438SChristian Brauner ASSERT_NE(child_time_ns_id, 0);
686*14f98438SChristian Brauner
687*14f98438SChristian Brauner /* Parent and child should have different time namespace IDs */
688*14f98438SChristian Brauner ASSERT_NE(parent_time_ns_id, child_time_ns_id);
689*14f98438SChristian Brauner
690*14f98438SChristian Brauner close(fd_parent_timens);
691*14f98438SChristian Brauner close(fd_child_timens);
692*14f98438SChristian Brauner
693*14f98438SChristian Brauner /* Clean up child process */
694*14f98438SChristian Brauner kill(pid, SIGTERM);
695*14f98438SChristian Brauner waitpid(pid, NULL, 0);
696*14f98438SChristian Brauner }
697*14f98438SChristian Brauner
TEST(nsid_pidns_basic)698*14f98438SChristian Brauner TEST(nsid_pidns_basic)
699*14f98438SChristian Brauner {
700*14f98438SChristian Brauner __u64 pid_ns_id = 0;
701*14f98438SChristian Brauner int fd_pidns;
702*14f98438SChristian Brauner int ret;
703*14f98438SChristian Brauner
704*14f98438SChristian Brauner /* Open the current PID namespace */
705*14f98438SChristian Brauner fd_pidns = open("/proc/self/ns/pid", O_RDONLY);
706*14f98438SChristian Brauner ASSERT_GE(fd_pidns, 0);
707*14f98438SChristian Brauner
708*14f98438SChristian Brauner /* Get the PID namespace ID */
709*14f98438SChristian Brauner ret = ioctl(fd_pidns, NS_GET_ID, &pid_ns_id);
710*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
711*14f98438SChristian Brauner ASSERT_NE(pid_ns_id, 0);
712*14f98438SChristian Brauner
713*14f98438SChristian Brauner /* Verify we can get the same ID again */
714*14f98438SChristian Brauner __u64 pid_ns_id2 = 0;
715*14f98438SChristian Brauner ret = ioctl(fd_pidns, NS_GET_ID, &pid_ns_id2);
716*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
717*14f98438SChristian Brauner ASSERT_EQ(pid_ns_id, pid_ns_id2);
718*14f98438SChristian Brauner
719*14f98438SChristian Brauner close(fd_pidns);
720*14f98438SChristian Brauner }
721*14f98438SChristian Brauner
TEST(nsid_pidns_separate)722*14f98438SChristian Brauner TEST(nsid_pidns_separate)
723*14f98438SChristian Brauner {
724*14f98438SChristian Brauner __u64 parent_pid_ns_id = 0;
725*14f98438SChristian Brauner __u64 child_pid_ns_id = 0;
726*14f98438SChristian Brauner int fd_parent_pidns, fd_child_pidns;
727*14f98438SChristian Brauner int ret;
728*14f98438SChristian Brauner pid_t pid;
729*14f98438SChristian Brauner int pipefd[2];
730*14f98438SChristian Brauner
731*14f98438SChristian Brauner /* Get parent's PID namespace ID */
732*14f98438SChristian Brauner fd_parent_pidns = open("/proc/self/ns/pid", O_RDONLY);
733*14f98438SChristian Brauner ASSERT_GE(fd_parent_pidns, 0);
734*14f98438SChristian Brauner ret = ioctl(fd_parent_pidns, NS_GET_ID, &parent_pid_ns_id);
735*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
736*14f98438SChristian Brauner ASSERT_NE(parent_pid_ns_id, 0);
737*14f98438SChristian Brauner
738*14f98438SChristian Brauner /* Create a pipe for synchronization */
739*14f98438SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
740*14f98438SChristian Brauner
741*14f98438SChristian Brauner pid = fork();
742*14f98438SChristian Brauner ASSERT_GE(pid, 0);
743*14f98438SChristian Brauner
744*14f98438SChristian Brauner if (pid == 0) {
745*14f98438SChristian Brauner /* Child process */
746*14f98438SChristian Brauner close(pipefd[0]);
747*14f98438SChristian Brauner
748*14f98438SChristian Brauner /* Create new PID namespace */
749*14f98438SChristian Brauner ret = unshare(CLONE_NEWPID);
750*14f98438SChristian Brauner if (ret != 0) {
751*14f98438SChristian Brauner /* Skip test if we don't have permission */
752*14f98438SChristian Brauner if (errno == EPERM || errno == EACCES) {
753*14f98438SChristian Brauner write(pipefd[1], "S", 1); /* Signal skip */
754*14f98438SChristian Brauner _exit(0);
755*14f98438SChristian Brauner }
756*14f98438SChristian Brauner _exit(1);
757*14f98438SChristian Brauner }
758*14f98438SChristian Brauner
759*14f98438SChristian Brauner /* Fork a grandchild to actually enter the new namespace */
760*14f98438SChristian Brauner pid_t grandchild = fork();
761*14f98438SChristian Brauner if (grandchild == 0) {
762*14f98438SChristian Brauner /* Grandchild is in the new namespace */
763*14f98438SChristian Brauner write(pipefd[1], "Y", 1);
764*14f98438SChristian Brauner close(pipefd[1]);
765*14f98438SChristian Brauner pause();
766*14f98438SChristian Brauner _exit(0);
767*14f98438SChristian Brauner } else if (grandchild > 0) {
768*14f98438SChristian Brauner /* Child writes grandchild PID and waits */
769*14f98438SChristian Brauner write(pipefd[1], "Y", 1);
770*14f98438SChristian Brauner write(pipefd[1], &grandchild, sizeof(grandchild));
771*14f98438SChristian Brauner close(pipefd[1]);
772*14f98438SChristian Brauner pause(); /* Keep the parent alive to maintain the grandchild */
773*14f98438SChristian Brauner _exit(0);
774*14f98438SChristian Brauner } else {
775*14f98438SChristian Brauner _exit(1);
776*14f98438SChristian Brauner }
777*14f98438SChristian Brauner }
778*14f98438SChristian Brauner
779*14f98438SChristian Brauner /* Parent process */
780*14f98438SChristian Brauner close(pipefd[1]);
781*14f98438SChristian Brauner
782*14f98438SChristian Brauner char buf;
783*14f98438SChristian Brauner ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
784*14f98438SChristian Brauner
785*14f98438SChristian Brauner if (buf == 'S') {
786*14f98438SChristian Brauner /* Child couldn't create namespace, skip test */
787*14f98438SChristian Brauner kill(pid, SIGTERM);
788*14f98438SChristian Brauner waitpid(pid, NULL, 0);
789*14f98438SChristian Brauner close(fd_parent_pidns);
790*14f98438SChristian Brauner close(pipefd[0]);
791*14f98438SChristian Brauner SKIP(return, "No permission to create PID namespace");
792*14f98438SChristian Brauner }
793*14f98438SChristian Brauner
794*14f98438SChristian Brauner ASSERT_EQ(buf, 'Y');
795*14f98438SChristian Brauner
796*14f98438SChristian Brauner pid_t grandchild_pid;
797*14f98438SChristian Brauner ASSERT_EQ(read(pipefd[0], &grandchild_pid, sizeof(grandchild_pid)), sizeof(grandchild_pid));
798*14f98438SChristian Brauner close(pipefd[0]);
799*14f98438SChristian Brauner
800*14f98438SChristian Brauner /* Open grandchild's PID namespace */
801*14f98438SChristian Brauner char path[256];
802*14f98438SChristian Brauner snprintf(path, sizeof(path), "/proc/%d/ns/pid", grandchild_pid);
803*14f98438SChristian Brauner fd_child_pidns = open(path, O_RDONLY);
804*14f98438SChristian Brauner ASSERT_GE(fd_child_pidns, 0);
805*14f98438SChristian Brauner
806*14f98438SChristian Brauner /* Get child's PID namespace ID */
807*14f98438SChristian Brauner ret = ioctl(fd_child_pidns, NS_GET_ID, &child_pid_ns_id);
808*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
809*14f98438SChristian Brauner ASSERT_NE(child_pid_ns_id, 0);
810*14f98438SChristian Brauner
811*14f98438SChristian Brauner /* Parent and child should have different PID namespace IDs */
812*14f98438SChristian Brauner ASSERT_NE(parent_pid_ns_id, child_pid_ns_id);
813*14f98438SChristian Brauner
814*14f98438SChristian Brauner close(fd_parent_pidns);
815*14f98438SChristian Brauner close(fd_child_pidns);
816*14f98438SChristian Brauner
817*14f98438SChristian Brauner /* Clean up child process */
818*14f98438SChristian Brauner kill(pid, SIGTERM);
819*14f98438SChristian Brauner waitpid(pid, NULL, 0);
820*14f98438SChristian Brauner }
821*14f98438SChristian Brauner
TEST(nsid_netns_basic)822*14f98438SChristian Brauner TEST(nsid_netns_basic)
823*14f98438SChristian Brauner {
824*14f98438SChristian Brauner __u64 net_ns_id = 0;
825*14f98438SChristian Brauner __u64 netns_cookie = 0;
826*14f98438SChristian Brauner int fd_netns;
827*14f98438SChristian Brauner int sock;
828*14f98438SChristian Brauner socklen_t optlen;
829*14f98438SChristian Brauner int ret;
830*14f98438SChristian Brauner
831*14f98438SChristian Brauner /* Open the current network namespace */
832*14f98438SChristian Brauner fd_netns = open("/proc/self/ns/net", O_RDONLY);
833*14f98438SChristian Brauner ASSERT_GE(fd_netns, 0);
834*14f98438SChristian Brauner
835*14f98438SChristian Brauner /* Get the network namespace ID via ioctl */
836*14f98438SChristian Brauner ret = ioctl(fd_netns, NS_GET_ID, &net_ns_id);
837*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
838*14f98438SChristian Brauner ASSERT_NE(net_ns_id, 0);
839*14f98438SChristian Brauner
840*14f98438SChristian Brauner /* Create a socket to get the SO_NETNS_COOKIE */
841*14f98438SChristian Brauner sock = socket(AF_UNIX, SOCK_STREAM, 0);
842*14f98438SChristian Brauner ASSERT_GE(sock, 0);
843*14f98438SChristian Brauner
844*14f98438SChristian Brauner /* Get the network namespace cookie via socket option */
845*14f98438SChristian Brauner optlen = sizeof(netns_cookie);
846*14f98438SChristian Brauner ret = getsockopt(sock, SOL_SOCKET, SO_NETNS_COOKIE, &netns_cookie, &optlen);
847*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
848*14f98438SChristian Brauner ASSERT_EQ(optlen, sizeof(netns_cookie));
849*14f98438SChristian Brauner
850*14f98438SChristian Brauner /* The namespace ID and cookie should be identical */
851*14f98438SChristian Brauner ASSERT_EQ(net_ns_id, netns_cookie);
852*14f98438SChristian Brauner
853*14f98438SChristian Brauner /* Verify we can get the same ID again */
854*14f98438SChristian Brauner __u64 net_ns_id2 = 0;
855*14f98438SChristian Brauner ret = ioctl(fd_netns, NS_GET_ID, &net_ns_id2);
856*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
857*14f98438SChristian Brauner ASSERT_EQ(net_ns_id, net_ns_id2);
858*14f98438SChristian Brauner
859*14f98438SChristian Brauner close(sock);
860*14f98438SChristian Brauner close(fd_netns);
861*14f98438SChristian Brauner }
862*14f98438SChristian Brauner
TEST(nsid_netns_separate)863*14f98438SChristian Brauner TEST(nsid_netns_separate)
864*14f98438SChristian Brauner {
865*14f98438SChristian Brauner __u64 parent_net_ns_id = 0;
866*14f98438SChristian Brauner __u64 parent_netns_cookie = 0;
867*14f98438SChristian Brauner __u64 child_net_ns_id = 0;
868*14f98438SChristian Brauner __u64 child_netns_cookie = 0;
869*14f98438SChristian Brauner int fd_parent_netns, fd_child_netns;
870*14f98438SChristian Brauner int parent_sock, child_sock;
871*14f98438SChristian Brauner socklen_t optlen;
872*14f98438SChristian Brauner int ret;
873*14f98438SChristian Brauner pid_t pid;
874*14f98438SChristian Brauner int pipefd[2];
875*14f98438SChristian Brauner
876*14f98438SChristian Brauner /* Get parent's network namespace ID */
877*14f98438SChristian Brauner fd_parent_netns = open("/proc/self/ns/net", O_RDONLY);
878*14f98438SChristian Brauner ASSERT_GE(fd_parent_netns, 0);
879*14f98438SChristian Brauner ret = ioctl(fd_parent_netns, NS_GET_ID, &parent_net_ns_id);
880*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
881*14f98438SChristian Brauner ASSERT_NE(parent_net_ns_id, 0);
882*14f98438SChristian Brauner
883*14f98438SChristian Brauner /* Get parent's network namespace cookie */
884*14f98438SChristian Brauner parent_sock = socket(AF_UNIX, SOCK_STREAM, 0);
885*14f98438SChristian Brauner ASSERT_GE(parent_sock, 0);
886*14f98438SChristian Brauner optlen = sizeof(parent_netns_cookie);
887*14f98438SChristian Brauner ret = getsockopt(parent_sock, SOL_SOCKET, SO_NETNS_COOKIE, &parent_netns_cookie, &optlen);
888*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
889*14f98438SChristian Brauner
890*14f98438SChristian Brauner /* Verify parent's ID and cookie match */
891*14f98438SChristian Brauner ASSERT_EQ(parent_net_ns_id, parent_netns_cookie);
892*14f98438SChristian Brauner
893*14f98438SChristian Brauner /* Create a pipe for synchronization */
894*14f98438SChristian Brauner ASSERT_EQ(pipe(pipefd), 0);
895*14f98438SChristian Brauner
896*14f98438SChristian Brauner pid = fork();
897*14f98438SChristian Brauner ASSERT_GE(pid, 0);
898*14f98438SChristian Brauner
899*14f98438SChristian Brauner if (pid == 0) {
900*14f98438SChristian Brauner /* Child process */
901*14f98438SChristian Brauner close(pipefd[0]);
902*14f98438SChristian Brauner
903*14f98438SChristian Brauner /* Create new network namespace */
904*14f98438SChristian Brauner ret = unshare(CLONE_NEWNET);
905*14f98438SChristian Brauner if (ret != 0) {
906*14f98438SChristian Brauner /* Skip test if we don't have permission */
907*14f98438SChristian Brauner if (errno == EPERM || errno == EACCES) {
908*14f98438SChristian Brauner write(pipefd[1], "S", 1); /* Signal skip */
909*14f98438SChristian Brauner _exit(0);
910*14f98438SChristian Brauner }
911*14f98438SChristian Brauner _exit(1);
912*14f98438SChristian Brauner }
913*14f98438SChristian Brauner
914*14f98438SChristian Brauner /* Signal success */
915*14f98438SChristian Brauner write(pipefd[1], "Y", 1);
916*14f98438SChristian Brauner close(pipefd[1]);
917*14f98438SChristian Brauner
918*14f98438SChristian Brauner /* Keep namespace alive */
919*14f98438SChristian Brauner pause();
920*14f98438SChristian Brauner _exit(0);
921*14f98438SChristian Brauner }
922*14f98438SChristian Brauner
923*14f98438SChristian Brauner /* Parent process */
924*14f98438SChristian Brauner close(pipefd[1]);
925*14f98438SChristian Brauner
926*14f98438SChristian Brauner char buf;
927*14f98438SChristian Brauner ASSERT_EQ(read(pipefd[0], &buf, 1), 1);
928*14f98438SChristian Brauner close(pipefd[0]);
929*14f98438SChristian Brauner
930*14f98438SChristian Brauner if (buf == 'S') {
931*14f98438SChristian Brauner /* Child couldn't create namespace, skip test */
932*14f98438SChristian Brauner kill(pid, SIGTERM);
933*14f98438SChristian Brauner waitpid(pid, NULL, 0);
934*14f98438SChristian Brauner close(fd_parent_netns);
935*14f98438SChristian Brauner close(parent_sock);
936*14f98438SChristian Brauner SKIP(return, "No permission to create network namespace");
937*14f98438SChristian Brauner }
938*14f98438SChristian Brauner
939*14f98438SChristian Brauner ASSERT_EQ(buf, 'Y');
940*14f98438SChristian Brauner
941*14f98438SChristian Brauner /* Open child's network namespace */
942*14f98438SChristian Brauner char path[256];
943*14f98438SChristian Brauner snprintf(path, sizeof(path), "/proc/%d/ns/net", pid);
944*14f98438SChristian Brauner fd_child_netns = open(path, O_RDONLY);
945*14f98438SChristian Brauner ASSERT_GE(fd_child_netns, 0);
946*14f98438SChristian Brauner
947*14f98438SChristian Brauner /* Get child's network namespace ID */
948*14f98438SChristian Brauner ret = ioctl(fd_child_netns, NS_GET_ID, &child_net_ns_id);
949*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
950*14f98438SChristian Brauner ASSERT_NE(child_net_ns_id, 0);
951*14f98438SChristian Brauner
952*14f98438SChristian Brauner /* Create socket in child's namespace to get cookie */
953*14f98438SChristian Brauner ret = setns(fd_child_netns, CLONE_NEWNET);
954*14f98438SChristian Brauner if (ret == 0) {
955*14f98438SChristian Brauner child_sock = socket(AF_UNIX, SOCK_STREAM, 0);
956*14f98438SChristian Brauner ASSERT_GE(child_sock, 0);
957*14f98438SChristian Brauner
958*14f98438SChristian Brauner optlen = sizeof(child_netns_cookie);
959*14f98438SChristian Brauner ret = getsockopt(child_sock, SOL_SOCKET, SO_NETNS_COOKIE, &child_netns_cookie, &optlen);
960*14f98438SChristian Brauner ASSERT_EQ(ret, 0);
961*14f98438SChristian Brauner
962*14f98438SChristian Brauner /* Verify child's ID and cookie match */
963*14f98438SChristian Brauner ASSERT_EQ(child_net_ns_id, child_netns_cookie);
964*14f98438SChristian Brauner
965*14f98438SChristian Brauner close(child_sock);
966*14f98438SChristian Brauner
967*14f98438SChristian Brauner /* Return to parent namespace */
968*14f98438SChristian Brauner setns(fd_parent_netns, CLONE_NEWNET);
969*14f98438SChristian Brauner }
970*14f98438SChristian Brauner
971*14f98438SChristian Brauner /* Parent and child should have different network namespace IDs */
972*14f98438SChristian Brauner ASSERT_NE(parent_net_ns_id, child_net_ns_id);
973*14f98438SChristian Brauner if (child_netns_cookie != 0) {
974*14f98438SChristian Brauner ASSERT_NE(parent_netns_cookie, child_netns_cookie);
975*14f98438SChristian Brauner }
976*14f98438SChristian Brauner
977*14f98438SChristian Brauner close(fd_parent_netns);
978*14f98438SChristian Brauner close(fd_child_netns);
979*14f98438SChristian Brauner close(parent_sock);
980*14f98438SChristian Brauner
981*14f98438SChristian Brauner /* Clean up child process */
982*14f98438SChristian Brauner kill(pid, SIGTERM);
983*14f98438SChristian Brauner waitpid(pid, NULL, 0);
984*14f98438SChristian Brauner }
985*14f98438SChristian Brauner
986*14f98438SChristian Brauner TEST_HARNESS_MAIN
987