DF-3456 / dns_flood.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 | /* * DF-3456 v6: concurrent pf INPUT anchor walks via DNS query/response * through the virtio NIC (slirp gateway 10.0.2.2:53). * * Each process: UDP socket -> send minimal DNS A-query for "a." to * 10.0.2.2:53, pace with delay_us, poll for responses. With pf enabled and * a "block drop udp from port 53" rule inside the anchor sandwich, every * response that still arrives is a pf rule-bypass via anchor-walk frame * tearing (netisr-side concurrent walks). * * usage: dns_flood <nproc> <secs> <delay_us> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <signal.h> #include <poll.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h> #include <netinet/in.h> #include <arpa/inet.h> static volatile sig_atomic_t stop = 0; static void onsig(int s __unused) { stop = 1; } static void mk_query(unsigned char *q, int *qlen, int id) { /* header */ q[0] = (id >> 8) & 0xff; q[1] = id & 0xff; q[2] = 0x01; q[3] = 0x00; /* RD=1 */ q[4] = q[5] = 0; /* QD=1 below */ q[6] = q[7] = 0; q[8] = q[9] = 0; q[10] = q[11] = 0; q[4] = 0; q[5] = 1; /* one question */ /* qname "a" */ q[12] = 1; q[13] = 'a'; q[14] = 0; /* type A, class IN */ q[15] = 0; q[16] = 1; q[17] = 0; q[18] = 1; *qlen = 19; } static long long total_tx, total_rx; static int dns_one(int delay_us) { struct sockaddr_in sin; struct pollfd pfd; unsigned char q[64], rbuf[512]; int s, qlen, n, id = rand() & 0xffff; int got = 0; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) return -1; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(53); sin.sin_addr.s_addr = inet_addr("10.0.2.2"); mk_query(q, &qlen, id); if (sendto(s, q, qlen, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0) { close(s); return -1; } total_tx++; pfd.fd = s; pfd.events = POLLIN; if (poll(&pfd, 1, 4) > 0) { /* tiny wait: bypassed pkts still count */ n = recv(s, rbuf, sizeof(rbuf), MSG_DONTWAIT); if (n > 0) { total_rx++; got = 1; } } close(s); usleep(delay_us); return got; } static void worker(int secs, int delay_us, int id) { time_t t0 = time(NULL); long long last = 0; srand(getpid() ^ id); while (!stop && (time(NULL) - t0) < secs) dns_one(delay_us); fprintf(stderr, "worker %d: tx=%lld rx=%lld (delta %lld)\n", id, total_tx, total_rx, total_rx - last); _exit(0); } int main(int argc, char **argv) { int nproc, secs, delay_us, i; time_t t0; if (argc != 4) { fprintf(stderr, "usage: %s <nproc> <secs> <delay_us>\n", argv[0]); return 2; } nproc = atoi(argv[1]); secs = atoi(argv[2]); delay_us = atoi(argv[3]); signal(SIGINT, onsig); signal(SIGTERM, onsig); t0 = time(NULL); for (i = 0; i < nproc; i++) { if (fork() == 0) worker(secs, delay_us, i); } for (i = 0; i < nproc; i++) wait(NULL); printf("dns_flood done: %d procs, %llds\n", nproc, (long long)(time(NULL) - t0)); return 0; } |