1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <errno.h> 4 #include <fcntl.h> 5 #include <limits.h> 6 #include <sched.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <sys/ioctl.h> 11 #include <sys/socket.h> 12 #include <sys/stat.h> 13 #include <sys/types.h> 14 #include <sys/wait.h> 15 #include <unistd.h> 16 #include <linux/if.h> 17 #include <linux/sockios.h> 18 #include <linux/nsfs.h> 19 #include <arpa/inet.h> 20 #include "../kselftest_harness.h" 21 #include "../filesystems/utils.h" 22 #include "wrappers.h" 23 24 #ifndef SIOCGSKNS 25 #define SIOCGSKNS 0x894C 26 #endif 27 28 #ifndef FD_NSFS_ROOT 29 #define FD_NSFS_ROOT -10003 30 #endif 31 32 #ifndef FILEID_NSFS 33 #define FILEID_NSFS 0xf1 34 #endif 35 36 /* 37 * Test basic SIOCGSKNS functionality. 38 * Create a socket and verify SIOCGSKNS returns the correct network namespace. 39 */ 40 TEST(siocgskns_basic) 41 { 42 int sock_fd, netns_fd, current_netns_fd; 43 struct stat st1, st2; 44 45 /* Create a TCP socket */ 46 sock_fd = socket(AF_INET, SOCK_STREAM, 0); 47 ASSERT_GE(sock_fd, 0); 48 49 /* Use SIOCGSKNS to get network namespace */ 50 netns_fd = ioctl(sock_fd, SIOCGSKNS); 51 if (netns_fd < 0) { 52 close(sock_fd); 53 if (errno == ENOTTY || errno == EINVAL) 54 SKIP(return, "SIOCGSKNS not supported"); 55 ASSERT_GE(netns_fd, 0); 56 } 57 58 /* Get current network namespace */ 59 current_netns_fd = open("/proc/self/ns/net", O_RDONLY); 60 ASSERT_GE(current_netns_fd, 0); 61 62 /* Verify they match */ 63 ASSERT_EQ(fstat(netns_fd, &st1), 0); 64 ASSERT_EQ(fstat(current_netns_fd, &st2), 0); 65 ASSERT_EQ(st1.st_ino, st2.st_ino); 66 67 close(sock_fd); 68 close(netns_fd); 69 close(current_netns_fd); 70 } 71 72 /* 73 * Test that socket file descriptors keep network namespaces active. 74 * Create a network namespace, create a socket in it, then exit the namespace. 75 * The namespace should remain active while the socket FD is held. 76 */ 77 TEST(siocgskns_keeps_netns_active) 78 { 79 int sock_fd, netns_fd, test_fd; 80 int ipc_sockets[2]; 81 pid_t pid; 82 int status; 83 struct stat st; 84 85 EXPECT_EQ(socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets), 0); 86 87 pid = fork(); 88 ASSERT_GE(pid, 0); 89 90 if (pid == 0) { 91 /* Child: create new netns and socket */ 92 close(ipc_sockets[0]); 93 94 if (unshare(CLONE_NEWNET) < 0) { 95 TH_LOG("unshare(CLONE_NEWNET) failed: %s", strerror(errno)); 96 close(ipc_sockets[1]); 97 exit(1); 98 } 99 100 /* Create a socket in the new network namespace */ 101 sock_fd = socket(AF_INET, SOCK_DGRAM, 0); 102 if (sock_fd < 0) { 103 TH_LOG("socket() failed: %s", strerror(errno)); 104 close(ipc_sockets[1]); 105 exit(1); 106 } 107 108 /* Send socket FD to parent via SCM_RIGHTS */ 109 struct msghdr msg = {0}; 110 struct iovec iov = {0}; 111 char buf[1] = {'X'}; 112 char cmsg_buf[CMSG_SPACE(sizeof(int))]; 113 114 iov.iov_base = buf; 115 iov.iov_len = 1; 116 msg.msg_iov = &iov; 117 msg.msg_iovlen = 1; 118 msg.msg_control = cmsg_buf; 119 msg.msg_controllen = sizeof(cmsg_buf); 120 121 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 122 cmsg->cmsg_level = SOL_SOCKET; 123 cmsg->cmsg_type = SCM_RIGHTS; 124 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 125 memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(int)); 126 127 if (sendmsg(ipc_sockets[1], &msg, 0) < 0) { 128 close(sock_fd); 129 close(ipc_sockets[1]); 130 exit(1); 131 } 132 133 close(sock_fd); 134 close(ipc_sockets[1]); 135 exit(0); 136 } 137 138 /* Parent: receive socket FD */ 139 close(ipc_sockets[1]); 140 141 struct msghdr msg = {0}; 142 struct iovec iov = {0}; 143 char buf[1]; 144 char cmsg_buf[CMSG_SPACE(sizeof(int))]; 145 146 iov.iov_base = buf; 147 iov.iov_len = 1; 148 msg.msg_iov = &iov; 149 msg.msg_iovlen = 1; 150 msg.msg_control = cmsg_buf; 151 msg.msg_controllen = sizeof(cmsg_buf); 152 153 ssize_t n = recvmsg(ipc_sockets[0], &msg, 0); 154 close(ipc_sockets[0]); 155 ASSERT_EQ(n, 1); 156 157 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 158 ASSERT_NE(cmsg, NULL); 159 ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS); 160 161 memcpy(&sock_fd, CMSG_DATA(cmsg), sizeof(int)); 162 163 /* Wait for child to exit */ 164 waitpid(pid, &status, 0); 165 ASSERT_TRUE(WIFEXITED(status)); 166 if (WEXITSTATUS(status) != 0) 167 SKIP(close(sock_fd); return, "Child failed to create namespace"); 168 169 /* Get network namespace from socket */ 170 netns_fd = ioctl(sock_fd, SIOCGSKNS); 171 if (netns_fd < 0) { 172 close(sock_fd); 173 if (errno == ENOTTY || errno == EINVAL) 174 SKIP(return, "SIOCGSKNS not supported"); 175 ASSERT_GE(netns_fd, 0); 176 } 177 178 ASSERT_EQ(fstat(netns_fd, &st), 0); 179 180 /* 181 * Namespace should still be active because socket FD keeps it alive. 182 * Try to access it via /proc/self/fd/<fd>. 183 */ 184 char path[64]; 185 snprintf(path, sizeof(path), "/proc/self/fd/%d", netns_fd); 186 test_fd = open(path, O_RDONLY); 187 ASSERT_GE(test_fd, 0); 188 close(test_fd); 189 close(netns_fd); 190 191 /* Close socket - namespace should become inactive */ 192 close(sock_fd); 193 194 /* Try SIOCGSKNS again - should fail since socket is closed */ 195 ASSERT_LT(ioctl(sock_fd, SIOCGSKNS), 0); 196 } 197 198 TEST_HARNESS_MAIN 199