DragonFlyBSD Kernel Audit
DF-0743 / live_trigger.c
← back to finding ↓ download raw
/*
 * DF-0743 — live discriminator trigger (runs as root).
 *
 * Sends two GRE packets to a configured gre0 and tags the INNER
 * (decapsulated) IP packets with distinct ip_id values so tcpdump on
 * gre0 can tell them apart.  gre_input2 BPF-taps the inner packet at
 * ip_gre.c:184-189 ONLY when decapsulation succeeds (i.e. the ptype
 * switch at ip_gre.c:164 did not fall through to return(0)).
 *
 *   Packet A: ip_hl=5, real GRE ptype @20 = ETHERTYPE_IP.
 *             inner ip_id = 0xAAAA.
 *             -> BOTH buggy and fixed: ptype read correctly -> decapsulated
 *                -> tcpdump on gre0 captures inner (id 0xAAAA).
 *
 *   Packet B: ip_hl=7 (8 bytes options @20..27).  Real GRE ptype @28 =
 *             ETHERTYPE_IP, but option bytes @22-23 = 0x0000.
 *             inner ip_id = 0xBBBB.
 *             -> BUGGY: gi_ptype reads @22 = 0x0000 -> switch default ->
 *                return(0) -> NOT decapsulated -> tcpdump captures nothing.
 *             -> FIXED : reads ptype @28 = 0x0800 -> decapsulated ->
 *                tcpdump captures inner (id 0xBBBB).
 *
 * Discriminator (capture count for id 0xBBBB):
 *   buggy  : 0   (packet B dropped by the misparse)
 *   fixed  : 1   (packet B decapsulated)
 *
 * Build:  cc -O2 -Wall -o live_trigger live_trigger.c
 * Run:    (root) see live_run.sh
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define IPPROTO_GRE 47
#define ETHERTYPE_IP 0x0800

struct gre_h { uint16_t flags; uint16_t ptype; } __attribute__((__packed__));

static uint16_t cksum16(const void *p, size_t len) {
    uint32_t s = 0; const uint16_t *w = p; int n = len >> 1;
    while (n-- > 0) s += *w++;
    if (len & 1) s += htons(((const uint8_t *)p)[len - 1]);
    s = (s >> 16) + (s & 0xffff); s += (s >> 16);
    return (uint16_t)(~s);
}

static void build_outer(uint8_t *buf, int hlen, int totlen) {
    memset(buf, 0, hlen);
    struct ip *ip = (struct ip *)buf;
    ip->ip_v = 4; ip->ip_hl = hlen/4; ip->ip_len = htons(totlen);
    ip->ip_ttl = 64; ip->ip_p = IPPROTO_GRE;
    inet_pton(AF_INET, "127.0.0.1", &ip->ip_src);
    inet_pton(AF_INET, "127.0.0.1", &ip->ip_dst);
    for (int i = 20; i < hlen; i++) buf[i] = 0x01;  /* NOP options */
    ip->ip_sum = 0; ip->ip_sum = cksum16(buf, hlen);
}

static void send_one(int s, int outer_hlen, int gre_off, uint16_t inner_id,
                     uint16_t poison_ptype_at22) {
    uint8_t pkt[64];
    build_outer(pkt, outer_hlen, sizeof(pkt));
    if (poison_ptype_at22) {            /* corrupt would-be gi_ptype @22-23 */
        pkt[22] = (poison_ptype_at22 >> 8) & 0xff;
        pkt[23] = poison_ptype_at22 & 0xff;
        pkt[10] = 0; pkt[11] = 0;
        uint16_t c = cksum16(pkt, outer_hlen); memcpy(pkt+10, &c, 2);
    }
    struct gre_h *g = (struct gre_h *)(pkt + gre_off);
    g->flags = htons(0); g->ptype = htons(ETHERTYPE_IP);
    struct ip *inner = (struct ip *)(pkt + gre_off + 4);
    inner->ip_v = 4; inner->ip_hl = 5; inner->ip_len = htons(sizeof(pkt)-(gre_off+4));
    inner->ip_id = htons(inner_id); inner->ip_ttl = 64; inner->ip_p = 1; /*ICMP*/
    inet_pton(AF_INET, "127.0.0.1", &inner->ip_src);
    inet_pton(AF_INET, "127.0.0.1", &inner->ip_dst);
    inner->ip_sum = 0; inner->ip_sum = cksum16(inner, 20);
    struct sockaddr_in dst; memset(&dst,0,sizeof(dst));
    dst.sin_family = AF_INET; inet_pton(AF_INET,"127.0.0.1",&dst.sin_addr);
    ssize_t n = sendto(s, pkt, sizeof(pkt), 0,(struct sockaddr*)&dst,sizeof(dst));
    printf("sent %zd bytes (outer_hlen=%d gre_off=%d inner_id=0x%04x poison@22=0x%04x)\n",
           n, outer_hlen, gre_off, inner_id, poison_ptype_at22);
}

int main(void) {
    int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (s < 0) { perror("socket"); return 2; }
    int one = 1; setsockopt(s, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one));
    printf("=== Packet A: ip_hl=5, ptype@20=IP, inner_id=0xAAAA ===\n");
    send_one(s, 20, 20, 0xAAAA, 0);     /* no poison: ip_hl=5, GRE@20 */
    usleep(400000);
    printf("=== Packet B: ip_hl=7, ptype@28=IP, option@22=0x0000, inner_id=0xBBBB ===\n");
    send_one(s, 28, 28, 0xBBBB, 0x0000);/* poison @22-23 = 0x0000 */
    close(s);
    usleep(300000);
    return 0;
}