/*
 * df735_trigger.c - minimal trigger for the assertion mechanism cited in DF-0735.
 *
 * The cited bug at sys/netgraph7/ng_ipfw.c:248 calls ip_input(m) directly from
 * ng_ipfw_rcvdata(), which runs in the netgraph worker thread created at
 * ng_base.c:2787-2789. That thread is a plain lwkt thread (NOT registered via
 * netmsg_service_port_init at netisr.c:258-280), so it is NOT a netisr thread.
 * The first line of ip_input() (ip_input.c:460) is ASSERT_NETISR_NCPUS(mycpuid),
 * a KASSERT that on default-GENERIC (INVARIANTS ON) fires the moment ip_input
 * is entered from any non-netisr caller.
 *
 * The real ng_ipfw.ko is dead code on this tree (see ng_df735_poc.c header for
 * why), so we cannot exercise the exact cited call site. To prove the
 * underlying mechanism, this module exposes a sysctl whose handler runs in the
 * *invoking thread* (which, like the netgraph worker thread, is not a netisr
 * thread) and calls ip_input(m). If the assertion is compiled in, this panics.
 *
 * Trigger:   sysctl debug.df735_trigger=1     (as root)
 *
 * The mechanism is identical whether the caller is a sysctl-handler thread,
 * a kthread, or the netgraph worker thread -- all are non-netisr threads,
 * so all trip the same KASSERT.
 */

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
#include <sys/types.h>

#include <net/netisr.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>

/* ip_input() is defined at sys/netinet/ip_input.c:445 but is not declared
 * in any public header (only ip_input_handler is wired into netisr at
 * ip_input.c:393). ng_ipfw.c gets an implicit declaration for the same
 * call (line 248); we declare it explicitly. */
extern void ip_input(struct mbuf *);

static int df735_trigger(SYSCTL_HANDLER_ARGS);

static int df735_armed = 0;

SYSCTL_PROC(_debug, OID_AUTO, df735_trigger, CTLTYPE_INT | CTLFLAG_RW,
    NULL, 0, df735_trigger, "I",
    "DF-0735: write 1 to call ip_input() from this (non-netisr) thread");

static int
df735_trigger(SYSCTL_HANDLER_ARGS)
{
	int error, val = 0;

	error = sysctl_handle_int(oidp, &val, 0, req);
	if (error != 0 || req->newptr == NULL)
		return (error);

	if (val) {
		struct mbuf *m;
		/* Build a minimal mbuf with packet header. The KASSERT at the
		 * top of ip_input fires before any packet-body examination,
		 * so the contents are irrelevant. */
		m = m_gethdr(M_WAITOK, MT_DATA);
		if (m == NULL)
			return (ENOMEM);
		m->m_pkthdr.len = m->m_len = 0;
		df735_armed++;
		/* The cited call site -- sys/netgraph7/ng_ipfw.c:248 -- is
		 * `ip_input(m);` executed from the netgraph worker thread.
		 * This call is the same statement from a different (but also
		 * non-netisr) thread context. */
		ip_input(m);
	}

	return (0);
}
