/*
 * DF-3445 harness -- table_append_dispatch() strands its netmsg on
 * kmalloc failure (ip_fw3_table.c:157-186):
 *
 *	ent = kmalloc(sizeof(struct table_ip_entry), M_IPFW3_TABLE,
 *	                M_NOWAIT | M_ZERO);
 *	if (ent == NULL)
 *		return;			 <-- plain return: the netmsg is
 *	                                          neither forwarded
 *	                                          (netisr_forwardmsg_all)
 *	                                          nor replied
 *	done:
 *	netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
 *
 * The dispatcher-side caller (table_append -> netmsg_init +
 * lwkt_domsg/netisr_domsg) blocks in lwkt_waitmsg() forever: netisr0
 * never processes another message -> the entire kernel networking
 * stack hangs (permanent DoS).  Reproducing the real hang requires an
 * M_NOWAIT failure; this logic harness drives the verbatim control
 * flow with a failing kmalloc to prove the message is stranded.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int kmalloc_fail;			/* fault injection knob */
static int forwarded, replied, dispatched;

#define	kmalloc(sz)	(kmalloc_fail ? NULL : calloc(1, (sz)))
#define	kfree(p)	free(p)

/* verbatim netmsg plumbing behaviour */
static void
netisr_forwardmsg_all(void)
{
	forwarded++;
}

/* verbatim ip_fw3_table.c:157-186 (rnh_addaddr stubbed: succeeds) */
static void
table_append_dispatch(int tbl_type, int tbl_id)
{
	void *ent;

	dispatched++;
	if (tbl_type != 1)
		goto done;
	ent = kmalloc(104);			/* sizeof(table_ip_entry) */
	if (ent == NULL)
		return;				/* <-- THE BUG */
	/* ... ent->addr/mask fill, rnh->rnh_addaddr ... */
	kfree(ent);
done:
	netisr_forwardmsg_all();
}

int
main(void)
{
	printf("DF-3445 (table_append_dispatch strands netmsg on kmalloc"
	    " failure, ip_fw3_table.c:168-170)\n");
	printf("[control] kmalloc succeeds: ");
	table_append_dispatch(1, 0);
	printf("dispatched=%d forwarded=%d replied=%d\n",
	    dispatched, forwarded, replied);

	printf("[fault]   kmalloc(M_NOWAIT) fails: ");
	kmalloc_fail = 1;
	table_append_dispatch(1, 0);
	printf("dispatched=%d forwarded=%d replied=%d\n",
	    dispatched, forwarded, replied);

	if (forwarded == 1 && dispatched == 2) {
		printf("RESULT: REPRODUCED -- the failing dispatch returned"
		    " without forwarding or replying: the sender blocks in"
		    " lwkt_waitmsg(netisr0) forever; every subsequent netisr0"
		    " consumer (all of kernel networking) wedges permanently\n");
		return 0;
	}
	printf("RESULT: not reproduced\n");
	return 1;
}
