// https://syzkaller.appspot.com/bug?id=00f0bf520a0c7b72ea7085d420a4bcae14e2c97a #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NLA_ALIGNTO #define NLA_ALIGNTO 4 #define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) #endif #ifndef NLA_HDRLEN #define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr))) #endif #define NLA_DATA(nla) ((void *)((char *)(nla) + NLA_HDRLEN)) #ifndef VETH_INFO_PEER #define VETH_INFO_PEER 1 #endif #ifndef NF_NAT_RANGE_MAP_IPS #define NF_NAT_RANGE_MAP_IPS (1 << 0) #endif static void nla_put(char *buf, int *len, uint16_t type, const void *data, uint16_t dlen) { struct nlattr *nla = (struct nlattr *)(buf + *len); nla->nla_type = type; nla->nla_len = NLA_HDRLEN + dlen; if (data && dlen > 0) memcpy(NLA_DATA(nla), data, dlen); *len += NLA_ALIGN(nla->nla_len); } static void nla_put_u32(char *buf, int *len, uint16_t type, uint32_t val) { nla_put(buf, len, type, &val, sizeof(val)); } static void nla_put_be32(char *buf, int *len, uint16_t type, uint32_t val) { uint32_t be = htonl(val); nla_put(buf, len, type, &be, sizeof(be)); } static void nla_put_str(char *buf, int *len, uint16_t type, const char *str) { nla_put(buf, len, type, str, strlen(str) + 1); } static void write_file(const char *path, const char *val) { int fd = open(path, O_WRONLY); if (fd < 0) { printf("[*] Note: open %s failed: %s (non-fatal)\n", path, strerror(errno)); return; } int res = write(fd, val, strlen(val)); if (res < 0) { printf("[*] Note: write %s failed: %s (non-fatal)\n", path, strerror(errno)); } else { printf("[+] Configured %s = %s\n", path, val); } close(fd); } static void rtnl_send_and_ack(int fd, struct nlmsghdr *nlh, const char *step_name) { struct sockaddr_nl sa; memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; int res = sendto(fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&sa, sizeof(sa)); if (res < 0) { printf("[-] Failed sendto for %s: %s\n", step_name, strerror(errno)); exit(1); } char buf[4096]; int rlen = recv(fd, buf, sizeof(buf), 0); if (rlen < 0) { printf("[-] Failed recv for %s: %s\n", step_name, strerror(errno)); exit(1); } int ack_received = 0; for (struct nlmsghdr *rnlh = (struct nlmsghdr *)buf; NLMSG_OK(rnlh, (unsigned int)rlen); rnlh = NLMSG_NEXT(rnlh, rlen)) { if (rnlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(rnlh); if (err->error != 0) { printf("[-] RTNL error for %s: %s (%d)\n", step_name, strerror(-err->error), err->error); exit(1); } ack_received = 1; printf("[+] RTNL ACK success for %s.\n", step_name); } } if (!ack_received) { printf("[-] Expected RTNL ACK for %s, but none received.\n", step_name); exit(1); } } static void bring_up_lo(void) { int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { printf("[-] Failed to open AF_INET socket: %s\n", strerror(errno)); exit(1); } struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, "lo", IFNAMSIZ - 1); int res = ioctl(s, SIOCGIFFLAGS, &ifr); if (res < 0) { printf("[-] Failed to get ifflags for lo: %s\n", strerror(errno)); close(s); exit(1); } ifr.ifr_flags |= (IFF_UP | IFF_RUNNING); res = ioctl(s, SIOCSIFFLAGS, &ifr); if (res < 0) { printf("[-] Failed to set ifflags for lo: %s\n", strerror(errno)); close(s); exit(1); } close(s); printf("[+] Interface lo brought up successfully.\n"); } static void if_set_addr_and_up(const char *name, const char *ip_str, const char *mask_str) { int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { printf("[-] Failed to open datagram socket for interface %s: %s\n", name, strerror(errno)); exit(1); } struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; sin->sin_family = AF_INET; int res = inet_pton(AF_INET, ip_str, &sin->sin_addr); if (res <= 0) { printf("[-] inet_pton failed for %s on %s: %s\n", ip_str, name, strerror(errno)); close(s); exit(1); } res = ioctl(s, SIOCSIFADDR, &ifr); if (res < 0) { printf("[-] Failed SIOCSIFADDR for %s on %s: %s\n", ip_str, name, strerror(errno)); close(s); exit(1); } if (mask_str) { res = inet_pton(AF_INET, mask_str, &sin->sin_addr); if (res <= 0) { printf("[-] inet_pton failed for netmask %s on %s: %s\n", mask_str, name, strerror(errno)); close(s); exit(1); } res = ioctl(s, SIOCSIFNETMASK, &ifr); if (res < 0) { printf("[-] Failed SIOCSIFNETMASK for %s: %s\n", name, strerror(errno)); close(s); exit(1); } } res = ioctl(s, SIOCGIFFLAGS, &ifr); if (res < 0) { printf("[-] Failed SIOCGIFFLAGS for %s: %s\n", name, strerror(errno)); close(s); exit(1); } ifr.ifr_flags |= (IFF_UP | IFF_RUNNING); res = ioctl(s, SIOCSIFFLAGS, &ifr); if (res < 0) { printf("[-] Failed SIOCSIFFLAGS for %s: %s\n", name, strerror(errno)); close(s); exit(1); } close(s); printf("[+] Interface %s configured with IP %s and brought UP successfully.\n", name, ip_str); } static void get_mac(const char *dev, unsigned char *mac) { int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { printf("[-] Failed to open datagram socket for get_mac: %s\n", strerror(errno)); exit(1); } struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, dev, IFNAMSIZ - 1); int res = ioctl(s, SIOCGIFHWADDR, &ifr); if (res < 0) { printf("[-] Failed to get SIOCGIFHWADDR for %s: %s\n", dev, strerror(errno)); close(s); exit(1); } memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); close(s); printf("[+] Interface %s HWADDR: %02x:%02x:%02x:%02x:%02x:%02x\n", dev, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } static void setup_arp(const char *dev, const char *ip, const unsigned char *mac) { int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { printf("[-] Failed to open datagram socket for setup_arp: %s\n", strerror(errno)); exit(1); } struct arpreq req; memset(&req, 0, sizeof(req)); struct sockaddr_in *sin = (struct sockaddr_in *)&req.arp_pa; sin->sin_family = AF_INET; sin->sin_addr.s_addr = inet_addr(ip); req.arp_ha.sa_family = ARPHRD_ETHER; memcpy(req.arp_ha.sa_data, mac, 6); req.arp_flags = ATF_PERM | ATF_COM; strncpy(req.arp_dev, dev, IFNAMSIZ - 1); int res = ioctl(s, SIOCSARP, &req); if (res < 0) { printf("[-] Failed SIOCSARP for %s on %s: %s\n", ip, dev, strerror(errno)); close(s); exit(1); } close(s); printf("[+] Static ARP configured on %s: %s -> %02x:%02x:%02x:%02x:%02x:%02x\n", dev, ip, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } static void create_veth_pair(void) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { printf("[-] Failed to open NETLINK_ROUTE socket: %s\n", strerror(errno)); exit(1); } char buf[2048]; memset(buf, 0, sizeof(buf)); struct nlmsghdr *nlh = (struct nlmsghdr *)buf; nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); nlh->nlmsg_type = RTM_NEWLINK; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; nlh->nlmsg_seq = 10; int off = nlh->nlmsg_len; nla_put_str(buf, &off, IFLA_IFNAME, "veth0"); int linkinfo_off = off; nla_put(buf, &off, NLA_F_NESTED | IFLA_LINKINFO, NULL, 0); nla_put_str(buf, &off, IFLA_INFO_KIND, "veth"); int infodata_off = off; nla_put(buf, &off, NLA_F_NESTED | IFLA_INFO_DATA, NULL, 0); int peer_off = off; nla_put(buf, &off, NLA_F_NESTED | VETH_INFO_PEER, NULL, 0); struct ifinfomsg peer_ifm; memset(&peer_ifm, 0, sizeof(peer_ifm)); memcpy(buf + off, &peer_ifm, sizeof(peer_ifm)); off += sizeof(peer_ifm); nla_put_str(buf, &off, IFLA_IFNAME, "veth1"); ((struct nlattr *)(buf + peer_off))->nla_len = off - peer_off; ((struct nlattr *)(buf + infodata_off))->nla_len = off - infodata_off; ((struct nlattr *)(buf + linkinfo_off))->nla_len = off - linkinfo_off; nlh->nlmsg_len = off; rtnl_send_and_ack(fd, nlh, "creating veth0 <-> veth1 pair"); close(fd); printf("[+] veth0 <-> veth1 pair created successfully.\n"); } static void move_if_to_netns(const char *name, int netns_fd) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { printf("[-] Failed to open NETLINK_ROUTE socket: %s\n", strerror(errno)); exit(1); } char buf[1024]; memset(buf, 0, sizeof(buf)); struct nlmsghdr *nlh = (struct nlmsghdr *)buf; nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); nlh->nlmsg_type = RTM_SETLINK; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; nlh->nlmsg_seq = 15; struct ifinfomsg *ifi = (struct ifinfomsg *)NLMSG_DATA(nlh); ifi->ifi_family = AF_UNSPEC; ifi->ifi_index = if_nametoindex(name); if (!ifi->ifi_index) { printf("[-] if_nametoindex failed for %s: %s\n", name, strerror(errno)); close(fd); exit(1); } int off = nlh->nlmsg_len; nla_put_u32(buf, &off, IFLA_NET_NS_FD, (uint32_t)netns_fd); nlh->nlmsg_len = off; rtnl_send_and_ack(fd, nlh, "moving veth1 to ns2"); close(fd); printf("[+] Interface %s successfully moved to destination netns.\n", name); } struct nft_batch { char buf[65536]; int len; int seq; }; static void batch_init(struct nft_batch *b) { b->len = 0; b->seq = 100; struct nlmsghdr *nlh = (struct nlmsghdr *)(b->buf + b->len); nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg)); nlh->nlmsg_type = NFNL_MSG_BATCH_BEGIN; nlh->nlmsg_flags = NLM_F_REQUEST; nlh->nlmsg_seq = b->seq++; nlh->nlmsg_pid = 0; struct nfgenmsg *nfg = (struct nfgenmsg *)NLMSG_DATA(nlh); nfg->nfgen_family = NFPROTO_UNSPEC; nfg->version = NFNETLINK_V0; nfg->res_id = htons(NFNL_SUBSYS_NFTABLES); b->len += NLMSG_ALIGN(nlh->nlmsg_len); } static void batch_add_msg(struct nft_batch *b, uint16_t type, uint8_t family, uint16_t flags, const char *payload, int plen) { struct nlmsghdr *nlh = (struct nlmsghdr *)(b->buf + b->len); nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg) + plen); nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | type; nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; nlh->nlmsg_seq = b->seq++; nlh->nlmsg_pid = 0; struct nfgenmsg *nfg = (struct nfgenmsg *)NLMSG_DATA(nlh); nfg->nfgen_family = family; nfg->version = NFNETLINK_V0; nfg->res_id = 0; if (payload && plen > 0) memcpy(b->buf + b->len + NLMSG_LENGTH(sizeof(struct nfgenmsg)), payload, plen); b->len += NLMSG_ALIGN(nlh->nlmsg_len); } static void batch_finish_and_check(int fd, struct nft_batch *b, int expected_acks) { struct nlmsghdr *nlh = (struct nlmsghdr *)(b->buf + b->len); nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg)); nlh->nlmsg_type = NFNL_MSG_BATCH_END; nlh->nlmsg_flags = NLM_F_REQUEST; nlh->nlmsg_seq = b->seq++; nlh->nlmsg_pid = 0; struct nfgenmsg *nfg = (struct nfgenmsg *)NLMSG_DATA(nlh); nfg->nfgen_family = NFPROTO_UNSPEC; nfg->version = NFNETLINK_V0; nfg->res_id = htons(NFNL_SUBSYS_NFTABLES); b->len += NLMSG_ALIGN(nlh->nlmsg_len); struct sockaddr_nl sa; memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; int res = sendto(fd, b->buf, b->len, 0, (struct sockaddr *)&sa, sizeof(sa)); if (res < 0) { printf("[-] Failed sendto nftables batch: %s\n", strerror(errno)); exit(1); } printf("[+] Sent nftables batch (%d bytes, awaiting %d ACKs)...\n", b->len, expected_acks); int received_acks = 0; while (received_acks < expected_acks) { char rcvbuf[8192]; struct pollfd pfd = {.fd = fd, .events = POLLIN}; int pr = poll(&pfd, 1, 2000); if (pr <= 0) { printf("[-] Timeout waiting for netfilter netlink ACKs (%d/%d received)\n", received_acks, expected_acks); exit(1); } ssize_t rlen = recv(fd, rcvbuf, sizeof(rcvbuf), 0); if (rlen < 0) { printf("[-] Failed recv from netfilter: %s\n", strerror(errno)); exit(1); } for (struct nlmsghdr *rh = (struct nlmsghdr *)rcvbuf; NLMSG_OK(rh, (unsigned int)rlen); rh = NLMSG_NEXT(rh, rlen)) { if (rh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(rh); if (err->error != 0) { printf("[-] Nftables batch error on seq %u: %d (%s)\n", rh->nlmsg_seq, err->error, strerror(-err->error)); exit(1); } received_acks++; printf("[+] Received ACK for seq %u (total %d/%d)\n", rh->nlmsg_seq, received_acks, expected_acks); } } } printf("[+] All %d nftables batch messages successfully applied and acknowledged.\n", received_acks); } static void setup_nftables_ns2(void) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER); if (fd < 0) { printf("[-] Failed to open NETLINK_NETFILTER socket in ns2: %s\n", strerror(errno)); exit(1); } struct nft_batch b; batch_init(&b); char attr[2048]; int len; int expected_acks = 0; /* 1. Table "nat" in NFPROTO_IPV4 */ len = 0; nla_put_str(attr, &len, NFTA_TABLE_NAME, "nat"); batch_add_msg(&b, NFT_MSG_NEWTABLE, NFPROTO_IPV4, NLM_F_CREATE, attr, len); expected_acks++; /* 2. Chain "prerouting": hook NF_INET_PRE_ROUTING (0), type "filter", priority -150 */ len = 0; nla_put_str(attr, &len, NFTA_CHAIN_TABLE, "nat"); nla_put_str(attr, &len, NFTA_CHAIN_NAME, "prerouting"); nla_put_str(attr, &len, NFTA_CHAIN_TYPE, "filter"); int hook_off = len; nla_put(attr, &len, NLA_F_NESTED | NFTA_CHAIN_HOOK, NULL, 0); nla_put_be32(attr, &len, NFTA_HOOK_HOOKNUM, NF_INET_PRE_ROUTING); nla_put_be32(attr, &len, NFTA_HOOK_PRIORITY, (uint32_t)-150); ((struct nlattr *)(attr + hook_off))->nla_len = len - hook_off; batch_add_msg(&b, NFT_MSG_NEWCHAIN, NFPROTO_IPV4, NLM_F_CREATE, attr, len); expected_acks++; /* 3. Rule in "prerouting": reject TCP_RST */ len = 0; nla_put_str(attr, &len, NFTA_RULE_TABLE, "nat"); nla_put_str(attr, &len, NFTA_RULE_CHAIN, "prerouting"); int exprs_off = len; nla_put(attr, &len, NLA_F_NESTED | NFTA_RULE_EXPRESSIONS, NULL, 0); int elem_off = len; nla_put(attr, &len, NLA_F_NESTED | NFTA_LIST_ELEM, NULL, 0); nla_put_str(attr, &len, NFTA_EXPR_NAME, "reject"); int data_off = len; nla_put(attr, &len, NLA_F_NESTED | NFTA_EXPR_DATA, NULL, 0); nla_put_be32(attr, &len, NFTA_REJECT_TYPE, NFT_REJECT_TCP_RST); ((struct nlattr *)(attr + data_off))->nla_len = len - data_off; ((struct nlattr *)(attr + elem_off))->nla_len = len - elem_off; ((struct nlattr *)(attr + exprs_off))->nla_len = len - exprs_off; batch_add_msg(&b, NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_CREATE | NLM_F_APPEND, attr, len); expected_acks++; /* 4. Chain "output": hook NF_INET_LOCAL_OUT (3), type "nat", priority -100 */ len = 0; nla_put_str(attr, &len, NFTA_CHAIN_TABLE, "nat"); nla_put_str(attr, &len, NFTA_CHAIN_NAME, "output"); nla_put_str(attr, &len, NFTA_CHAIN_TYPE, "nat"); hook_off = len; nla_put(attr, &len, NLA_F_NESTED | NFTA_CHAIN_HOOK, NULL, 0); nla_put_be32(attr, &len, NFTA_HOOK_HOOKNUM, NF_INET_LOCAL_OUT); nla_put_be32(attr, &len, NFTA_HOOK_PRIORITY, (uint32_t)-100); ((struct nlattr *)(attr + hook_off))->nla_len = len - hook_off; batch_add_msg(&b, NFT_MSG_NEWCHAIN, NFPROTO_IPV4, NLM_F_CREATE, attr, len); expected_acks++; /* 5. Rule in "output": xt_dnat_target_v2 */ struct nf_nat_range2 range; memset(&range, 0, sizeof(range)); range.flags = NF_NAT_RANGE_MAP_IPS; range.min_addr.ip = inet_addr("127.0.0.1"); range.max_addr.ip = inet_addr("127.0.0.1"); len = 0; nla_put_str(attr, &len, NFTA_RULE_TABLE, "nat"); nla_put_str(attr, &len, NFTA_RULE_CHAIN, "output"); exprs_off = len; nla_put(attr, &len, NLA_F_NESTED | NFTA_RULE_EXPRESSIONS, NULL, 0); elem_off = len; nla_put(attr, &len, NLA_F_NESTED | NFTA_LIST_ELEM, NULL, 0); nla_put_str(attr, &len, NFTA_EXPR_NAME, "target"); data_off = len; nla_put(attr, &len, NLA_F_NESTED | NFTA_EXPR_DATA, NULL, 0); nla_put_str(attr, &len, NFTA_TARGET_NAME, "DNAT"); nla_put_be32(attr, &len, NFTA_TARGET_REV, 2); nla_put(attr, &len, NFTA_TARGET_INFO, &range, sizeof(range)); ((struct nlattr *)(attr + data_off))->nla_len = len - data_off; ((struct nlattr *)(attr + elem_off))->nla_len = len - elem_off; ((struct nlattr *)(attr + exprs_off))->nla_len = len - exprs_off; batch_add_msg(&b, NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_CREATE | NLM_F_APPEND, attr, len); expected_acks++; batch_finish_and_check(fd, &b, expected_acks); close(fd); printf("[+] Nftables rules in ns2 successfully committed.\n"); } static void trigger_connect(void) { int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); if (s < 0) { printf("[-] Failed to create non-blocking AF_INET socket: %s\n", strerror(errno)); exit(1); } int res = setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, "veth0", strlen("veth0")); if (res < 0) { printf("[-] Failed to bind socket to device veth0: %s\n", strerror(errno)); close(s); exit(1); } struct sockaddr_in bind_sin; memset(&bind_sin, 0, sizeof(bind_sin)); bind_sin.sin_family = AF_INET; bind_sin.sin_addr.s_addr = inet_addr("192.168.1.1"); res = bind(s, (struct sockaddr *)&bind_sin, sizeof(bind_sin)); if (res < 0) { printf("[-] Failed to bind socket to 192.168.1.1: %s\n", strerror(errno)); close(s); exit(1); } struct sockaddr_in conn_sin; memset(&conn_sin, 0, sizeof(conn_sin)); conn_sin.sin_family = AF_INET; conn_sin.sin_port = htons(1234); conn_sin.sin_addr.s_addr = inet_addr("192.168.1.2"); printf("[*] Connecting from veth0 (192.168.1.1) to veth1 (192.168.1.2:1234)...\n"); res = connect(s, (struct sockaddr *)&conn_sin, sizeof(conn_sin)); if (res < 0) { printf("[+] Non-blocking connect() returned %d (%s).\n", res, strerror(errno)); } else { printf("[+] connect() returned %d.\n", res); } usleep(50000); close(s); } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Starting reproducer for WARNING in xt_dnat_target_v2...\n"); /* 1. Open initial network namespace ns1 */ int ns1 = open("/proc/self/ns/net", O_RDONLY); if (ns1 < 0) { printf("[-] Failed to open initial /proc/self/ns/net: %s\n", strerror(errno)); exit(1); } printf("[+] Opened ns1 handle (fd=%d).\n", ns1); /* 2. Create veth pair (veth0 <-> veth1) in ns1 */ create_veth_pair(); /* 3. Read hardware MAC addresses */ unsigned char mac0[6], mac1[6]; get_mac("veth0", mac0); get_mac("veth1", mac1); /* 4. Create second network namespace ns2 */ int res = unshare(CLONE_NEWNET); if (res < 0) { printf("[-] Failed unshare(CLONE_NEWNET): %s\n", strerror(errno)); exit(1); } int ns2 = open("/proc/self/ns/net", O_RDONLY); if (ns2 < 0) { printf("[-] Failed to open second /proc/self/ns/net: %s\n", strerror(errno)); exit(1); } printf("[+] Created and opened ns2 handle (fd=%d).\n", ns2); /* 5. Switch back to ns1 to move veth1 into ns2 */ res = setns(ns1, CLONE_NEWNET); if (res < 0) { printf("[-] Failed to switch back to ns1: %s\n", strerror(errno)); exit(1); } printf("[+] Switched back to ns1.\n"); move_if_to_netns("veth1", ns2); /* 6. Configure ns1 interfaces and ARP */ bring_up_lo(); if_set_addr_and_up("veth0", "192.168.1.1", "255.255.255.0"); setup_arp("veth0", "192.168.1.2", mac1); write_file("/proc/sys/net/ipv4/conf/all/accept_local", "1"); write_file("/proc/sys/net/ipv4/conf/veth0/accept_local", "1"); write_file("/proc/sys/net/ipv4/conf/all/route_localnet", "1"); write_file("/proc/sys/net/ipv4/conf/all/rp_filter", "0"); write_file("/proc/sys/net/ipv4/conf/veth0/rp_filter", "0"); printf("[+] ns1 network configuration complete.\n"); /* 7. Switch to ns2, configure interfaces, ARP, and nftables */ res = setns(ns2, CLONE_NEWNET); if (res < 0) { printf("[-] Failed to switch to ns2: %s\n", strerror(errno)); exit(1); } printf("[+] Switched to ns2.\n"); bring_up_lo(); if_set_addr_and_up("veth1", "192.168.1.2", "255.255.255.0"); setup_arp("veth1", "192.168.1.1", mac0); write_file("/proc/sys/net/ipv4/conf/all/accept_local", "1"); write_file("/proc/sys/net/ipv4/conf/veth1/accept_local", "1"); write_file("/proc/sys/net/ipv4/conf/all/route_localnet", "1"); write_file("/proc/sys/net/ipv4/conf/all/rp_filter", "0"); write_file("/proc/sys/net/ipv4/conf/veth1/rp_filter", "0"); setup_nftables_ns2(); printf("[+] ns2 network and nftables configuration complete.\n"); /* 8. Switch back to ns1 and trigger connections */ res = setns(ns1, CLONE_NEWNET); if (res < 0) { printf("[-] Failed to switch back to ns1 for trigger: %s\n", strerror(errno)); exit(1); } printf("[+] Switched to ns1. Ready to trigger.\n"); for (int i = 0; i < 5; i++) { printf("[*] Trigger iteration %d/5...\n", i + 1); trigger_connect(); usleep(20000); } printf("[*] Sleeping to allow kernel asynchronous bottom-half processing and warnings...\n"); sleep(1); close(ns1); close(ns2); printf("[+] Reproducer completed.\n"); return 0; }