/* Verifier of /proc/net and net namespace consistency Author : Per Hallsmark First setup a net namespace and add a veth so we get something to test with ip netns add netns_other ip link add if_default type veth peer name if_other ip link set if_other netns netns_other Run test app without first reading some info from /proc/net in default namespace ./netns_procfs_test Run test app again, this time reading some info from /proc/net in default namespace before changing net namespace ./netns_procfs_test cached On a bugfree kernel, the output after "After net namespace change" should be same as in first test run, meaning we should see if_other in the /proc/net/dev dump and we should see if_other in the directory. The issue is not visible if it is different processes doing the readings since then the inode's aren't cached. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NETNS_RUN_DIR "/var/run/netns" static int get_nsfd(const char *ns) { char path[strlen(NETNS_RUN_DIR) + strlen(ns) + 2]; int fd; snprintf(path, sizeof(path), "%s/%s", NETNS_RUN_DIR, ns); fd = open(path, O_RDONLY, 0); return fd; } static int dump_file(const char *fn, const int checkforbug) { int fd; char buf[512]; ssize_t len; char *wholebuf = NULL; ssize_t wholelen = 0; int status = 0; printf("==== %s ====\n", fn); fd = open(fn, O_RDONLY); if (fd == -1) { printf("Couldn't open %s (%s)\n", fn, strerror(errno)); return -1; } do { len = read(fd, buf, sizeof(buf)-1); buf[len] = 0; wholebuf = realloc(wholebuf, wholelen + len + 1); sprintf(&wholebuf[wholelen], "%s", buf); wholelen += len; } while (len > 0); printf("%s\n", wholebuf); if (checkforbug && strstr(wholebuf, "if_other")) { status = 1; } free(wholebuf); close(fd); return status; } static int dump_dir(const char *dn, const int checkforbug) { DIR *dir; struct dirent *dir_entry; int status = 0; printf("==== files in %s ====\n", dn); dir = opendir(dn); if (!dir) { printf("Couldn't open %s (%s)\n", dn, strerror(errno)); return -1; } while ((dir_entry = readdir(dir)) != NULL) { printf(" %s", dir_entry->d_name); if (checkforbug && !strcmp(dir_entry->d_name, "if_other")) { status = 2; } printf("\n"); } closedir(dir); return status; } static int switch_ns(const char *ns) { int nsfd = get_nsfd(ns); if (setns(nsfd, CLONE_NEWNET) < 0) { fprintf(stderr, "Unable to switch to namespace(fd=%d): %s.\n", nsfd, strerror(errno)); exit(-1); } return 0; } int main (int argc, char **argv) { int bugcheck = 0; int checkforbug = 1; if (argc == 2 && !strcmp(argv[1], "cached")) { // access /proc/net a bit so we dcache file and // directory inodes printf("Before net namespace change :\n"); dump_file("/proc/net/dev", 0); dump_dir("/proc/net/dev_snmp6", 0); } else { // we cannot check for bug if not run with cached arg checkforbug = 0; } // change namespace switch_ns("netns_other"); // access /proc/net again, if all works we should see // new info because of net namespace change printf("\n\nAfter net namespace change :\n"); bugcheck += dump_file("/proc/net/dev", 1); bugcheck += dump_dir("/proc/net/dev_snmp6", 1); if (!checkforbug) return 0; switch (bugcheck) { case 1 : printf("This kernel is fixed for file inode bug but suffers dir inode bug\n"); break; case 3 : printf("This kernel is fixed for both file and dir inode bug\n"); break; default : printf("This kernel suffers both file and dir inode bug\n"); } return bugcheck != 3; }