#pragma once /* * Test Helpers * Bunch of helpers to setup the environment for networking tests. This * includes net-namespace setups, veth setups, and more. */ #undef NDEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "n-acd.h" static inline void test_add_child_ip(const struct in_addr *addr) { char *p; int r; r = asprintf(&p, "ip addr add dev veth1 %s/8", inet_ntoa(*addr)); c_assert(r >= 0); r = system(p); c_assert(r >= 0); free(p); } static inline void test_del_child_ip(const struct in_addr *addr) { char *p; int r; r = asprintf(&p, "ip addr del dev veth1 %s/8", inet_ntoa(*addr)); c_assert(r >= 0); r = system(p); c_assert(r >= 0); free(p); } static inline void test_if_query(const char *name, int *indexp, struct ether_addr *macp) { struct ifreq ifr = {}; size_t l; int r, s; l = strlen(name); c_assert(l <= IF_NAMESIZE); if (indexp) { *indexp = if_nametoindex(name); c_assert(*indexp > 0); } if (macp) { s = socket(AF_INET, SOCK_DGRAM, 0); c_assert(s >= 0); strncpy(ifr.ifr_name, name, l + 1); r = ioctl(s, SIOCGIFHWADDR, &ifr); c_assert(r >= 0); memcpy(macp->ether_addr_octet, ifr.ifr_hwaddr.sa_data, ETH_ALEN); close(s); } } static inline void test_veth_cmd(int ifindex, const char *cmd) { char *p, name[IF_NAMESIZE + 1] = {}; int r; p = if_indextoname(ifindex, name); c_assert(p); r = asprintf(&p, "ip link set %s %s", name, cmd); c_assert(r >= 0); /* Again: Ewwww... */ r = system(p); c_assert(r == 0); free(p); } static inline void test_veth_new(int *parent_indexp, struct ether_addr *parent_macp, int *child_indexp, struct ether_addr *child_macp) { int r; /* Eww... but it works. */ r = system("ip link add type veth"); c_assert(r == 0); r = system("ip link set veth0 up"); c_assert(r == 0); r = system("ip link set veth1 up"); c_assert(r == 0); test_if_query("veth0", parent_indexp, parent_macp); test_if_query("veth1", child_indexp, child_macp); } static inline void test_loopback_up(int *indexp, struct ether_addr *macp) { int r; r = system("ip link set lo up"); c_assert(r == 0); test_if_query("lo", indexp, macp); } static inline void test_raise_memlock(void) { const size_t wanted = 64 * 1024 * 1024; struct rlimit get, set; int r; r = getrlimit(RLIMIT_MEMLOCK, &get); c_assert(!r); /* try raising limit to @wanted */ set.rlim_cur = wanted; set.rlim_max = (wanted > get.rlim_max) ? wanted : get.rlim_max; r = setrlimit(RLIMIT_MEMLOCK, &set); if (r) { c_assert(errno == EPERM); /* not privileged to raise limit, so maximize soft limit */ set.rlim_cur = get.rlim_max; set.rlim_max = get.rlim_max; r = setrlimit(RLIMIT_MEMLOCK, &set); c_assert(!r); } } static inline void test_unshare_user_namespace(void) { uid_t euid; gid_t egid; int r, fd; /* * Enter a new user namespace as root:root. */ euid = geteuid(); egid = getegid(); r = unshare(CLONE_NEWUSER); c_assert(r >= 0); fd = open("/proc/self/uid_map", O_WRONLY); c_assert(fd >= 0); r = dprintf(fd, "0 %d 1\n", euid); c_assert(r >= 0); close(fd); fd = open("/proc/self/setgroups", O_WRONLY); c_assert(fd >= 0); r = dprintf(fd, "deny"); c_assert(r >= 0); close(fd); fd = open("/proc/self/gid_map", O_WRONLY); c_assert(fd >= 0); r = dprintf(fd, "0 %d 1\n", egid); c_assert(r >= 0); close(fd); } static inline void test_setup(void) { int r; /* * Move into a new network and mount namespace both associated * with a new user namespace where the current eUID is mapped to * 0. Then create a a private instance of /run/netns. This ensures * that any network devices or network namespaces are private to * the test process. */ test_raise_memlock(); test_unshare_user_namespace(); r = unshare(CLONE_NEWNET | CLONE_NEWNS); c_assert(r >= 0); r = mount(NULL, "/", "", MS_PRIVATE | MS_REC, NULL); c_assert(r >= 0); r = mount(NULL, "/run", "tmpfs", 0, NULL); c_assert(r >= 0); r = mkdir("/run/netns", 0755); c_assert(r >= 0); }