xref: /minix/minix/tests/test83.c (revision ad920fc4)
1c97d4ff6SErik van der Kouwe /*
2c97d4ff6SErik van der Kouwe  * test83: test bad network packets
3c97d4ff6SErik van der Kouwe  */
4c97d4ff6SErik van der Kouwe 
5c97d4ff6SErik van der Kouwe #define DEBUG 0
6c97d4ff6SErik van der Kouwe 
7c97d4ff6SErik van der Kouwe #if DEBUG
8c97d4ff6SErik van der Kouwe #define dbgprintf(...)	do {						\
9c97d4ff6SErik van der Kouwe 				struct timeval time = { };		\
10c97d4ff6SErik van der Kouwe 				gettimeofday(&time, NULL);		\
11c97d4ff6SErik van der Kouwe 				fprintf(stderr, "[%2d:%.2d:%.2d.%.6d p%d %s:%d] ", \
12c97d4ff6SErik van der Kouwe 					(int) ((time.tv_sec / 3600) % 24), \
13c97d4ff6SErik van der Kouwe 					(int) ((time.tv_sec / 60) % 60), \
14c97d4ff6SErik van der Kouwe 					(int) (time.tv_sec % 60),	\
15c97d4ff6SErik van der Kouwe 					time.tv_usec,			\
16c97d4ff6SErik van der Kouwe 					getpid(),			\
17c97d4ff6SErik van der Kouwe 					__FUNCTION__,			\
18c97d4ff6SErik van der Kouwe 					__LINE__);			\
19c97d4ff6SErik van der Kouwe 				fprintf(stderr, __VA_ARGS__);		\
20c97d4ff6SErik van der Kouwe 				fflush(stderr);				\
21c97d4ff6SErik van der Kouwe 			} while (0)
22c97d4ff6SErik van der Kouwe #else
23c97d4ff6SErik van der Kouwe #define	dbgprintf(...)
24c97d4ff6SErik van der Kouwe #endif
25c97d4ff6SErik van der Kouwe 
26c97d4ff6SErik van der Kouwe #include <arpa/inet.h>
27c97d4ff6SErik van der Kouwe #include <assert.h>
28c97d4ff6SErik van der Kouwe #include <fcntl.h>
29c97d4ff6SErik van der Kouwe #include <netinet/in.h>
30c97d4ff6SErik van der Kouwe #include <signal.h>
31c97d4ff6SErik van der Kouwe #include <stdarg.h>
32c97d4ff6SErik van der Kouwe #include <stdint.h>
33c97d4ff6SErik van der Kouwe #include <stdio.h>
34c97d4ff6SErik van der Kouwe #include <stdlib.h>
35c97d4ff6SErik van der Kouwe #include <string.h>
36c97d4ff6SErik van der Kouwe #include <sys/ioctl.h>
37c97d4ff6SErik van der Kouwe #include <sys/socket.h>
38c97d4ff6SErik van der Kouwe #include <sys/time.h>
39c97d4ff6SErik van der Kouwe #include <sys/wait.h>
40c97d4ff6SErik van der Kouwe 
41c97d4ff6SErik van der Kouwe #include "common.h"
42c97d4ff6SErik van der Kouwe 
43c97d4ff6SErik van der Kouwe int max_error = 100;
44c97d4ff6SErik van der Kouwe 
45c97d4ff6SErik van der Kouwe /* https://tools.ietf.org/html/rfc791 */
46c97d4ff6SErik van der Kouwe struct header_ip {
47c97d4ff6SErik van der Kouwe 	uint8_t  ver_ihl; /* Version (4 bits) + IHL (4 bits) */
48c97d4ff6SErik van der Kouwe 	uint8_t  tos;     /* Type of Service */
49c97d4ff6SErik van der Kouwe 	uint16_t len;     /* Total Length */
50c97d4ff6SErik van der Kouwe 	uint16_t id;      /* Identification */
51c97d4ff6SErik van der Kouwe 	uint16_t fl_fo;   /* Flags (3 bits) + Fragment Offset (13 bits) */
52c97d4ff6SErik van der Kouwe 	uint8_t  ttl;     /* Time to Live */
53c97d4ff6SErik van der Kouwe 	uint8_t  prot;    /* Protocol */
54c97d4ff6SErik van der Kouwe 	uint16_t cs;      /* Header Checksum */
55c97d4ff6SErik van der Kouwe 	uint32_t src;     /* Source Address */
56c97d4ff6SErik van der Kouwe 	uint32_t dst;     /* Destination Address */
57c97d4ff6SErik van der Kouwe 	uint8_t  opt[16]; /* Options  */
58c97d4ff6SErik van der Kouwe };
59c97d4ff6SErik van der Kouwe #define IP_FLAG_EVIL	(1 << 2)
60c97d4ff6SErik van der Kouwe #define IP_FLAG_DF	(1 << 1)
61c97d4ff6SErik van der Kouwe #define IP_FLAG_MF	(1 << 0)
62c97d4ff6SErik van der Kouwe 
63c97d4ff6SErik van der Kouwe /* https://tools.ietf.org/html/rfc790 */
64c97d4ff6SErik van der Kouwe #define IP_PROT_ICMP	1
65c97d4ff6SErik van der Kouwe #define IP_PROT_TCP	6
66c97d4ff6SErik van der Kouwe #define IP_PROT_UDP	17
67c97d4ff6SErik van der Kouwe 
68c97d4ff6SErik van der Kouwe /* https://tools.ietf.org/html/rfc768 */
69c97d4ff6SErik van der Kouwe struct header_udp {
70c97d4ff6SErik van der Kouwe 	uint16_t src; /* Source Port */
71c97d4ff6SErik van der Kouwe 	uint16_t dst; /* Destination Port */
72c97d4ff6SErik van der Kouwe 	uint16_t len; /* Length */
73c97d4ff6SErik van der Kouwe 	uint16_t cs;  /* Checksum */
74c97d4ff6SErik van der Kouwe };
75c97d4ff6SErik van der Kouwe 
76c97d4ff6SErik van der Kouwe struct header_udp_pseudo {
77c97d4ff6SErik van der Kouwe 	uint32_t src;
78c97d4ff6SErik van der Kouwe 	uint32_t dst;
79c97d4ff6SErik van der Kouwe 	uint8_t  zero;
80c97d4ff6SErik van der Kouwe 	uint8_t  prot;
81c97d4ff6SErik van der Kouwe 	uint16_t len;
82c97d4ff6SErik van der Kouwe };
83c97d4ff6SErik van der Kouwe 
84c97d4ff6SErik van der Kouwe /* https://tools.ietf.org/html/rfc793 */
85c97d4ff6SErik van der Kouwe struct header_tcp {
86c97d4ff6SErik van der Kouwe 	uint16_t src;     /* Source Port */
87c97d4ff6SErik van der Kouwe 	uint16_t dst;     /* Destination Port */
88c97d4ff6SErik van der Kouwe 	uint32_t seq;     /* Sequence Number */
89c97d4ff6SErik van der Kouwe 	uint32_t ack;     /* Acknowledgment Number */
90c97d4ff6SErik van der Kouwe 	uint8_t  doff;    /* Data Offset */
91c97d4ff6SErik van der Kouwe 	uint8_t  fl;      /* Flags */
92c97d4ff6SErik van der Kouwe 	uint16_t win;     /* Window */
93c97d4ff6SErik van der Kouwe 	uint16_t cs;      /* Checksum */
94c97d4ff6SErik van der Kouwe 	uint16_t uptr;    /* Urgent Pointer */
95c97d4ff6SErik van der Kouwe 	uint8_t  opt[16]; /* Options  */
96c97d4ff6SErik van der Kouwe };
97c97d4ff6SErik van der Kouwe #define TCP_FLAG_URG	(1 << 5)
98c97d4ff6SErik van der Kouwe #define TCP_FLAG_ACK	(1 << 4)
99c97d4ff6SErik van der Kouwe #define TCP_FLAG_PSH	(1 << 3)
100c97d4ff6SErik van der Kouwe #define TCP_FLAG_RST	(1 << 2)
101c97d4ff6SErik van der Kouwe #define TCP_FLAG_SYN	(1 << 1)
102c97d4ff6SErik van der Kouwe #define TCP_FLAG_FIN	(1 << 0)
103c97d4ff6SErik van der Kouwe 
104c97d4ff6SErik van der Kouwe #define PORT_BASE	12345
105c97d4ff6SErik van der Kouwe #define PORT_COUNT_TCP	4
106c97d4ff6SErik van der Kouwe #define PORT_COUNT_UDP	2
107c97d4ff6SErik van der Kouwe #define PORT_COUNT	(PORT_COUNT_TCP + PORT_COUNT_UDP)
108c97d4ff6SErik van der Kouwe 
109c97d4ff6SErik van der Kouwe #define PORT_BASE_SRC	(PORT_BASE + PORT_COUNT)
110c97d4ff6SErik van der Kouwe #define PORT_COUNT_SRC	79
111c97d4ff6SErik van der Kouwe 
112c97d4ff6SErik van der Kouwe #define PAYLOADSIZE_COUNT 6
113c97d4ff6SErik van der Kouwe static const size_t payloadsizes[] = {
114c97d4ff6SErik van der Kouwe 	0,
115c97d4ff6SErik van der Kouwe 	1,
116c97d4ff6SErik van der Kouwe 	100,
117c97d4ff6SErik van der Kouwe 	1024,
118c97d4ff6SErik van der Kouwe 	2345,
119c97d4ff6SErik van der Kouwe 	65535 - sizeof(struct header_ip) - sizeof(struct header_udp),
120c97d4ff6SErik van der Kouwe };
121c97d4ff6SErik van der Kouwe 
122*ad920fc4SDavid van Moolenbroek /* In its current configuration, this test uses the loopback interface only. */
123*ad920fc4SDavid van Moolenbroek static uint32_t addrsrc = INADDR_LOOPBACK; /* 127.0.0.1 (localhost) */
124*ad920fc4SDavid van Moolenbroek static uint32_t addrdst = INADDR_LOOPBACK; /* 127.0.0.1 (localhost) */
125*ad920fc4SDavid van Moolenbroek static uint32_t addrs[] = {
126*ad920fc4SDavid van Moolenbroek 	INADDR_LOOPBACK, /* 127.0.0.1 (localhost) */
127c97d4ff6SErik van der Kouwe };
128c97d4ff6SErik van der Kouwe 
129c97d4ff6SErik van der Kouwe #define CLOSE(fd) do { assert(fd >= 0); if (close((fd)) != 0) efmt("close failed"); } while (0);
130c97d4ff6SErik van der Kouwe enum server_action {
131c97d4ff6SErik van der Kouwe 	sa_close,
132c97d4ff6SErik van der Kouwe 	sa_read,
133c97d4ff6SErik van der Kouwe 	sa_selectr,
134c97d4ff6SErik van der Kouwe 	sa_selectrw,
135c97d4ff6SErik van der Kouwe 	sa_write,
136c97d4ff6SErik van der Kouwe };
137c97d4ff6SErik van der Kouwe static int server_done;
138c97d4ff6SErik van der Kouwe 
139c97d4ff6SErik van der Kouwe static void server_alarm(int seconds);
140c97d4ff6SErik van der Kouwe 
sigstr_cat(char * p,const char * s)141c97d4ff6SErik van der Kouwe static char *sigstr_cat(char *p, const char *s) {
142c97d4ff6SErik van der Kouwe 	size_t slen = strlen(s);
143c97d4ff6SErik van der Kouwe 	memcpy(p, s, slen);
144c97d4ff6SErik van der Kouwe 	return p + slen;
145c97d4ff6SErik van der Kouwe }
146c97d4ff6SErik van der Kouwe 
sigstr_itoa(char * p,unsigned long n)147c97d4ff6SErik van der Kouwe static char *sigstr_itoa(char *p, unsigned long n) {
148c97d4ff6SErik van der Kouwe 	unsigned digit;
149c97d4ff6SErik van der Kouwe 	unsigned long factor = 1000000000UL;
150c97d4ff6SErik van der Kouwe 	int first = 1;
151c97d4ff6SErik van der Kouwe 
152c97d4ff6SErik van der Kouwe 	while (factor > 0) {
153c97d4ff6SErik van der Kouwe 		digit = (n / factor) % 10;
154c97d4ff6SErik van der Kouwe 		if (!first || digit || factor == 1) {
155c97d4ff6SErik van der Kouwe 			*(p++) = digit + '0';
156c97d4ff6SErik van der Kouwe 			first = 0;
157c97d4ff6SErik van der Kouwe 		}
158c97d4ff6SErik van der Kouwe 		factor /= 10;
159c97d4ff6SErik van der Kouwe 	}
160c97d4ff6SErik van der Kouwe 	return p;
161c97d4ff6SErik van der Kouwe }
162c97d4ff6SErik van der Kouwe 
163ea69bfc7SDavid van Moolenbroek #if 0
164c97d4ff6SErik van der Kouwe static void dbgprintdata(const void *data, size_t size) {
165c97d4ff6SErik van der Kouwe 	size_t addr;
166c97d4ff6SErik van der Kouwe 	const unsigned char *p = data;
167c97d4ff6SErik van der Kouwe 
168c97d4ff6SErik van der Kouwe 	for (addr = 0; addr < size; addr++) {
169c97d4ff6SErik van der Kouwe 		if (addr % 16 == 0) {
170c97d4ff6SErik van der Kouwe 			if (addr > 0) fprintf(stderr, "\n");
171c97d4ff6SErik van der Kouwe 			fprintf(stderr, "%.4zx", addr);
172c97d4ff6SErik van der Kouwe 		}
173c97d4ff6SErik van der Kouwe 		fprintf(stderr, " %.2x", p[addr]);
174c97d4ff6SErik van der Kouwe 	}
175c97d4ff6SErik van der Kouwe 	fprintf(stderr, "\n");
176c97d4ff6SErik van der Kouwe 	fflush(stderr);
177c97d4ff6SErik van der Kouwe }
178ea69bfc7SDavid van Moolenbroek #endif
179c97d4ff6SErik van der Kouwe 
dbgprint_sig(const char * name)180c97d4ff6SErik van der Kouwe static void dbgprint_sig(const char *name) {
181c97d4ff6SErik van der Kouwe #if DEBUG
182c97d4ff6SErik van der Kouwe 	char buf[256];
183c97d4ff6SErik van der Kouwe 	char *p = buf;
184c97d4ff6SErik van der Kouwe 
185c97d4ff6SErik van der Kouwe 	/* fprintf not used to be signal safe */
186c97d4ff6SErik van der Kouwe 	p = sigstr_cat(p, "[");
187c97d4ff6SErik van der Kouwe 	p = sigstr_itoa(p, getpid());
188c97d4ff6SErik van der Kouwe 	p = sigstr_cat(p, "] ");
189c97d4ff6SErik van der Kouwe 	p = sigstr_cat(p, name);
190c97d4ff6SErik van der Kouwe 	p = sigstr_cat(p, "\n");
191c97d4ff6SErik van der Kouwe 	write(STDERR_FILENO, buf, p - buf);
192c97d4ff6SErik van der Kouwe #endif
193c97d4ff6SErik van der Kouwe }
194c97d4ff6SErik van der Kouwe 
195c97d4ff6SErik van der Kouwe #define SIGNAL(sig, handler) (signal_checked((sig), (handler), #sig, __FILE__, __FUNCTION__, __LINE__))
196c97d4ff6SErik van der Kouwe 
signal_checked(int sig,void (* handler)(int),const char * signame,const char * file,const char * func,int line)197c97d4ff6SErik van der Kouwe static void signal_checked(int sig, void (* handler)(int), const char *signame,
198c97d4ff6SErik van der Kouwe 	const char *file, const char *func, int line) {
199c97d4ff6SErik van der Kouwe 	char buf[256];
200c97d4ff6SErik van der Kouwe 	char *p = buf;
201c97d4ff6SErik van der Kouwe 	struct sigaction sa = {
202c97d4ff6SErik van der Kouwe 		.sa_handler = handler,
203c97d4ff6SErik van der Kouwe 	};
204c97d4ff6SErik van der Kouwe 
205c97d4ff6SErik van der Kouwe 	if (sigaction(sig, &sa, NULL) == 0) return;
206c97d4ff6SErik van der Kouwe 
207c97d4ff6SErik van der Kouwe 	/* efmt not used to be signal safe */
208c97d4ff6SErik van der Kouwe 	p = sigstr_cat(p, "[");
209c97d4ff6SErik van der Kouwe 	p = sigstr_cat(p, file);
210c97d4ff6SErik van der Kouwe 	p = sigstr_cat(p, ":");
211c97d4ff6SErik van der Kouwe 	p = sigstr_itoa(p, line);
212c97d4ff6SErik van der Kouwe 	p = sigstr_cat(p, "] error: sigaction(");
213c97d4ff6SErik van der Kouwe 	p = sigstr_cat(p, signame);
214c97d4ff6SErik van der Kouwe 	p = sigstr_cat(p, ") failed in function ");
215c97d4ff6SErik van der Kouwe 	p = sigstr_cat(p, func);
216c97d4ff6SErik van der Kouwe 	p = sigstr_cat(p, ": ");
217c97d4ff6SErik van der Kouwe 	p = sigstr_itoa(p, errno);
218c97d4ff6SErik van der Kouwe 	p = sigstr_cat(p, "\n");
219c97d4ff6SErik van der Kouwe 	write(STDERR_FILENO, buf, p - buf);
220c97d4ff6SErik van der Kouwe 	errct++;
221c97d4ff6SErik van der Kouwe }
222c97d4ff6SErik van der Kouwe 
server_sigusr1(int signo)223c97d4ff6SErik van der Kouwe static void server_sigusr1(int signo) {
224c97d4ff6SErik van der Kouwe 	dbgprint_sig("SIGUSR1");
225c97d4ff6SErik van der Kouwe 
226c97d4ff6SErik van der Kouwe 	/* terminate on the first opportunity */
227c97d4ff6SErik van der Kouwe 	server_done = 1;
228c97d4ff6SErik van der Kouwe 
229c97d4ff6SErik van der Kouwe 	/* in case signal is caught before a blocking operation,
230c97d4ff6SErik van der Kouwe 	 * keep interrupting
231c97d4ff6SErik van der Kouwe 	 */
232c97d4ff6SErik van der Kouwe 	server_alarm(1);
233c97d4ff6SErik van der Kouwe }
234c97d4ff6SErik van der Kouwe 
server_stop(pid_t pid)235c97d4ff6SErik van der Kouwe static void server_stop(pid_t pid) {
236c97d4ff6SErik van der Kouwe 
237c97d4ff6SErik van der Kouwe 	if (pid < 0) return;
238c97d4ff6SErik van der Kouwe 
239c97d4ff6SErik van der Kouwe 	dbgprintf("sending SIGUSR1 to child %d\n", (int) pid);
240c97d4ff6SErik van der Kouwe 	if (kill(pid, SIGUSR1) != 0) efmt("kill failed");
241c97d4ff6SErik van der Kouwe }
242c97d4ff6SErik van der Kouwe 
server_wait(pid_t pid)243c97d4ff6SErik van der Kouwe static void server_wait(pid_t pid) {
244c97d4ff6SErik van der Kouwe 	int exitcode, status;
245c97d4ff6SErik van der Kouwe 	pid_t r;
246c97d4ff6SErik van der Kouwe 
247c97d4ff6SErik van der Kouwe 	if (pid < 0) return;
248c97d4ff6SErik van der Kouwe 
249c97d4ff6SErik van der Kouwe 	dbgprintf("waiting for child %d\n", (int) pid);
250c97d4ff6SErik van der Kouwe 	r = waitpid(pid, &status, 0);
251c97d4ff6SErik van der Kouwe 	if (r != pid) {
252c97d4ff6SErik van der Kouwe 		efmt("waitpid failed");
253c97d4ff6SErik van der Kouwe 		return;
254c97d4ff6SErik van der Kouwe 	}
255c97d4ff6SErik van der Kouwe 
256c97d4ff6SErik van der Kouwe 	if (WIFEXITED(status)) {
257c97d4ff6SErik van der Kouwe 		exitcode = WEXITSTATUS(status);
258c97d4ff6SErik van der Kouwe 		if (exitcode < 0) {
259c97d4ff6SErik van der Kouwe 			efmt("negative exit code from child %d\n", (int) pid);
260c97d4ff6SErik van der Kouwe 		} else {
261c97d4ff6SErik van der Kouwe 			dbgprintf("child exited exitcode=%d\n", exitcode);
262c97d4ff6SErik van der Kouwe 			errct += exitcode;
263c97d4ff6SErik van der Kouwe 		}
264c97d4ff6SErik van der Kouwe 	} else if (WIFSIGNALED(status)) {
265c97d4ff6SErik van der Kouwe 		efmt("child killed by signal %d", WTERMSIG(status));
266c97d4ff6SErik van der Kouwe 	} else {
267c97d4ff6SErik van der Kouwe 		efmt("child has unexpected exit status 0x%x", status);
268c97d4ff6SErik van der Kouwe 	}
269c97d4ff6SErik van der Kouwe }
270c97d4ff6SErik van der Kouwe 
server_sigalrm(int signum)271c97d4ff6SErik van der Kouwe static void server_sigalrm(int signum) {
272c97d4ff6SErik van der Kouwe 	server_alarm(1);
273c97d4ff6SErik van der Kouwe }
274c97d4ff6SErik van der Kouwe 
server_alarm(int seconds)275c97d4ff6SErik van der Kouwe static void server_alarm(int seconds) {
276c97d4ff6SErik van der Kouwe 	SIGNAL(SIGALRM, server_sigalrm);
277c97d4ff6SErik van der Kouwe 	alarm(seconds);
278c97d4ff6SErik van der Kouwe }
279c97d4ff6SErik van der Kouwe 
server_no_alarm(void)280c97d4ff6SErik van der Kouwe static void server_no_alarm(void) {
281c97d4ff6SErik van der Kouwe 	int errno_old = errno;
282c97d4ff6SErik van der Kouwe 	alarm(0);
283c97d4ff6SErik van der Kouwe 	SIGNAL(SIGALRM, SIG_DFL);
284c97d4ff6SErik van der Kouwe 	errno = errno_old;
285c97d4ff6SErik van der Kouwe }
286c97d4ff6SErik van der Kouwe 
server_rw(int fd,int is_write,int * success)287c97d4ff6SErik van der Kouwe static int server_rw(int fd, int is_write, int *success) {
288c97d4ff6SErik van der Kouwe 	char buf[4096];
289c97d4ff6SErik van der Kouwe 	ssize_t r;
290c97d4ff6SErik van der Kouwe 
291c97d4ff6SErik van der Kouwe 	/* return 0 means close connection, *success=0 means stop server */
292c97d4ff6SErik van der Kouwe 
293c97d4ff6SErik van der Kouwe 	if (is_write) {
294c97d4ff6SErik van der Kouwe 		/* ignore SIGPIPE */
295c97d4ff6SErik van der Kouwe 		SIGNAL(SIGPIPE, SIG_IGN);
296c97d4ff6SErik van der Kouwe 
297c97d4ff6SErik van der Kouwe 		/* initialize buffer */
298c97d4ff6SErik van der Kouwe 		memset(buf, -1, sizeof(buf));
299c97d4ff6SErik van der Kouwe 	}
300c97d4ff6SErik van der Kouwe 
301c97d4ff6SErik van der Kouwe 	/* don't block for more than 1s */
302c97d4ff6SErik van der Kouwe 	server_alarm(1);
303c97d4ff6SErik van der Kouwe 
304c97d4ff6SErik van der Kouwe 	/* perform read or write operation */
305c97d4ff6SErik van der Kouwe 	dbgprintf("server_rw waiting is_write=%d\n", is_write);
306c97d4ff6SErik van der Kouwe 	r = is_write ? write(fd, buf, sizeof(buf)) : read(fd, buf, sizeof(buf));
307c97d4ff6SErik van der Kouwe 
308c97d4ff6SErik van der Kouwe 	/* stop alarm (preserves errno) */
309c97d4ff6SErik van der Kouwe 	server_no_alarm();
310c97d4ff6SErik van der Kouwe 
311c97d4ff6SErik van der Kouwe 	/* handle read/write result */
312c97d4ff6SErik van der Kouwe 	if (r >= 0) {
313c97d4ff6SErik van der Kouwe 		dbgprintf("server_rw done\n");
314c97d4ff6SErik van der Kouwe 		*success = 1;
315c97d4ff6SErik van der Kouwe 		return r > 0;
316c97d4ff6SErik van der Kouwe 	}
317c97d4ff6SErik van der Kouwe 
318c97d4ff6SErik van der Kouwe 	switch (errno) {
319c97d4ff6SErik van der Kouwe 	case EINTR:
320c97d4ff6SErik van der Kouwe 		dbgprintf("server_rw interrupted\n");
321c97d4ff6SErik van der Kouwe 		*success = 1;
322c97d4ff6SErik van der Kouwe 		return 0;
323c97d4ff6SErik van der Kouwe 	case ECONNRESET:
324c97d4ff6SErik van der Kouwe 		dbgprintf("server_rw connection reset\n");
325c97d4ff6SErik van der Kouwe 		*success = 1;
326c97d4ff6SErik van der Kouwe 		return 0;
327c97d4ff6SErik van der Kouwe 	case EPIPE:
328c97d4ff6SErik van der Kouwe 		if (is_write) {
329c97d4ff6SErik van der Kouwe 			dbgprintf("server_rw EPIPE\n");
330c97d4ff6SErik van der Kouwe 			*success = 1;
331c97d4ff6SErik van der Kouwe 			return 0;
332c97d4ff6SErik van der Kouwe 		}
333c97d4ff6SErik van der Kouwe 		/* fall through */
334c97d4ff6SErik van der Kouwe 	default:
335c97d4ff6SErik van der Kouwe 		efmt("%s failed", is_write ? "write" : "read");
336c97d4ff6SErik van der Kouwe 		*success = 0;
337c97d4ff6SErik van der Kouwe 		return 0;
338c97d4ff6SErik van der Kouwe 	}
339c97d4ff6SErik van der Kouwe }
340c97d4ff6SErik van der Kouwe 
server_select(int fd,int is_rw,int * success,enum server_action * actionnext)341c97d4ff6SErik van der Kouwe static int server_select(int fd, int is_rw, int *success,
342c97d4ff6SErik van der Kouwe 	enum server_action *actionnext) {
343c97d4ff6SErik van der Kouwe 	int r;
344c97d4ff6SErik van der Kouwe 	fd_set readfds, writefds;
345c97d4ff6SErik van der Kouwe 	struct timeval timeout = { .tv_sec = 1, .tv_usec = 0 };
346c97d4ff6SErik van der Kouwe 
347c97d4ff6SErik van der Kouwe 	/* return 0 means close connection, *success=0 means stop server */
348c97d4ff6SErik van der Kouwe 
349c97d4ff6SErik van der Kouwe 	/* prepare fd sets */
350c97d4ff6SErik van der Kouwe 	FD_ZERO(&readfds);
351c97d4ff6SErik van der Kouwe 	FD_SET(fd, &readfds);
352c97d4ff6SErik van der Kouwe 	FD_ZERO(&writefds);
353c97d4ff6SErik van der Kouwe 	if (is_rw) FD_SET(fd, &writefds);
354c97d4ff6SErik van der Kouwe 
355c97d4ff6SErik van der Kouwe 	/* perform select */
356c97d4ff6SErik van der Kouwe 	errno = 0;
357c97d4ff6SErik van der Kouwe 	dbgprintf("server_select waiting\n");
358c97d4ff6SErik van der Kouwe 	r = select(fd + 1, &readfds, &writefds, NULL, &timeout);
359c97d4ff6SErik van der Kouwe 
360c97d4ff6SErik van der Kouwe 	/* handle result */
361c97d4ff6SErik van der Kouwe 	if (r < 0) {
362c97d4ff6SErik van der Kouwe 		switch (errno) {
363c97d4ff6SErik van der Kouwe 		case EINTR:
364c97d4ff6SErik van der Kouwe 			dbgprintf("server_select interrupted\n");
365c97d4ff6SErik van der Kouwe 			*success = 1;
366c97d4ff6SErik van der Kouwe 			return 0;
367c97d4ff6SErik van der Kouwe 		default:
368c97d4ff6SErik van der Kouwe 			efmt("select failed");
369c97d4ff6SErik van der Kouwe 			*success = 0;
370c97d4ff6SErik van der Kouwe 			return 0;
371c97d4ff6SErik van der Kouwe 		}
372c97d4ff6SErik van der Kouwe 	}
373c97d4ff6SErik van der Kouwe 	if (r == 0) {
374c97d4ff6SErik van der Kouwe 		dbgprintf("server_select nothing available\n");
375c97d4ff6SErik van der Kouwe 		*success = 1;
376c97d4ff6SErik van der Kouwe 		return 0;
377c97d4ff6SErik van der Kouwe 	}
378c97d4ff6SErik van der Kouwe 
379c97d4ff6SErik van der Kouwe 	if (FD_ISSET(fd, &readfds)) {
380c97d4ff6SErik van der Kouwe 		dbgprintf("server_select read available\n");
381c97d4ff6SErik van der Kouwe 		*actionnext = sa_read;
382c97d4ff6SErik van der Kouwe 		*success = 1;
383c97d4ff6SErik van der Kouwe 		return 1;
384c97d4ff6SErik van der Kouwe 	} else if (FD_ISSET(fd, &writefds)) {
385c97d4ff6SErik van der Kouwe 		dbgprintf("server_select write available\n");
386c97d4ff6SErik van der Kouwe 		*actionnext = sa_write;
387c97d4ff6SErik van der Kouwe 		*success = 1;
388c97d4ff6SErik van der Kouwe 		return 1;
389c97d4ff6SErik van der Kouwe 	}
390c97d4ff6SErik van der Kouwe 
391c97d4ff6SErik van der Kouwe 	*success = 0;
392c97d4ff6SErik van der Kouwe 	efmt("select did not set fd");
393c97d4ff6SErik van der Kouwe 	return 0;
394c97d4ff6SErik van der Kouwe }
395c97d4ff6SErik van der Kouwe 
server_accept(int servfd,int type,enum server_action action)396c97d4ff6SErik van der Kouwe static int server_accept(int servfd, int type, enum server_action action) {
397c97d4ff6SErik van der Kouwe 	enum server_action actionnext;
398c97d4ff6SErik van der Kouwe 	struct sockaddr addr;
399c97d4ff6SErik van der Kouwe 	socklen_t addrsize;
400c97d4ff6SErik van der Kouwe 	int connfd;
401c97d4ff6SErik van der Kouwe 	int success = 0;
402c97d4ff6SErik van der Kouwe 
403c97d4ff6SErik van der Kouwe 	/* if connection-oriented, accept a conmection */
404c97d4ff6SErik van der Kouwe 	if (type == SOCK_DGRAM) {
405c97d4ff6SErik van der Kouwe 		connfd = servfd;
406c97d4ff6SErik van der Kouwe 	} else {
407c97d4ff6SErik van der Kouwe 		dbgprintf("server_accept waiting for connection\n");
408c97d4ff6SErik van der Kouwe 		addrsize = sizeof(addr);
409c97d4ff6SErik van der Kouwe 		connfd = accept(servfd, &addr, &addrsize);
410c97d4ff6SErik van der Kouwe 		if (connfd < 0) {
411c97d4ff6SErik van der Kouwe 			switch (errno) {
412c97d4ff6SErik van der Kouwe 			case EINTR:
413c97d4ff6SErik van der Kouwe 				dbgprintf("server_accept interrupted\n");
414c97d4ff6SErik van der Kouwe 				return 1;
415c97d4ff6SErik van der Kouwe 			default:
416c97d4ff6SErik van der Kouwe 				efmt("cannot accept connection");
417c97d4ff6SErik van der Kouwe 				return 0;
418c97d4ff6SErik van der Kouwe 			}
419c97d4ff6SErik van der Kouwe 		}
420c97d4ff6SErik van der Kouwe 		dbgprintf("server_accept new connection\n");
421c97d4ff6SErik van der Kouwe 	}
422c97d4ff6SErik van der Kouwe 
423c97d4ff6SErik van der Kouwe 	/* perform requested action while the connection is open */
424c97d4ff6SErik van der Kouwe 	actionnext = action;
425c97d4ff6SErik van der Kouwe 	while (!server_done) {
426c97d4ff6SErik van der Kouwe 		switch (actionnext) {
427c97d4ff6SErik van der Kouwe 		case sa_close:
428c97d4ff6SErik van der Kouwe 			success = 1;
429c97d4ff6SErik van der Kouwe 			goto cleanup;
430c97d4ff6SErik van der Kouwe 		case sa_read:
431c97d4ff6SErik van der Kouwe 			if (!server_rw(connfd, 0, &success)) goto cleanup;
432c97d4ff6SErik van der Kouwe 			actionnext = action;
433c97d4ff6SErik van der Kouwe 			break;
434c97d4ff6SErik van der Kouwe 		case sa_selectr:
435c97d4ff6SErik van der Kouwe 		case sa_selectrw:
436c97d4ff6SErik van der Kouwe 			if (!server_select(connfd, actionnext == sa_selectrw,
437c97d4ff6SErik van der Kouwe 				&success, &actionnext)) {
438c97d4ff6SErik van der Kouwe 				goto cleanup;
439c97d4ff6SErik van der Kouwe 			}
440c97d4ff6SErik van der Kouwe 			break;
441c97d4ff6SErik van der Kouwe 		case sa_write:
442c97d4ff6SErik van der Kouwe 			if (!server_rw(connfd, 1, &success)) goto cleanup;
443c97d4ff6SErik van der Kouwe 			actionnext = action;
444c97d4ff6SErik van der Kouwe 			break;
445c97d4ff6SErik van der Kouwe 		default:
446c97d4ff6SErik van der Kouwe 			efmt("bad server action");
447c97d4ff6SErik van der Kouwe 			success = 0;
448c97d4ff6SErik van der Kouwe 			goto cleanup;
449c97d4ff6SErik van der Kouwe 		}
450c97d4ff6SErik van der Kouwe 	}
451c97d4ff6SErik van der Kouwe 
452c97d4ff6SErik van der Kouwe 	/* socket connection socket */
453c97d4ff6SErik van der Kouwe cleanup:
454c97d4ff6SErik van der Kouwe 	dbgprintf("server_accept done success=%d\n", success);
455c97d4ff6SErik van der Kouwe 	if (connfd != servfd) CLOSE(connfd);
456c97d4ff6SErik van der Kouwe 	return success;
457c97d4ff6SErik van der Kouwe }
458c97d4ff6SErik van der Kouwe 
server_start(int type,int port,enum server_action action)459c97d4ff6SErik van der Kouwe static pid_t server_start(int type, int port, enum server_action action) {
460c97d4ff6SErik van der Kouwe 	struct sockaddr_in addr = {
461c97d4ff6SErik van der Kouwe 		.sin_family = AF_INET,
462c97d4ff6SErik van der Kouwe 		.sin_port = htons(port),
463c97d4ff6SErik van der Kouwe 		.sin_addr = { htonl(INADDR_ANY) },
464c97d4ff6SErik van der Kouwe 	};
465*ad920fc4SDavid van Moolenbroek 	int fd, on;
466c97d4ff6SErik van der Kouwe 	pid_t pid = -1;
467c97d4ff6SErik van der Kouwe 
468c97d4ff6SErik van der Kouwe 	dbgprintf("server_start port %d\n", port);
469c97d4ff6SErik van der Kouwe 
470c97d4ff6SErik van der Kouwe 	/* create socket */
471c97d4ff6SErik van der Kouwe 	fd = socket(AF_INET, type, 0);
472c97d4ff6SErik van der Kouwe 	if (fd < 0) {
473c97d4ff6SErik van der Kouwe 		efmt("cannot create socket");
474c97d4ff6SErik van der Kouwe 		goto cleanup;
475c97d4ff6SErik van der Kouwe 	}
476c97d4ff6SErik van der Kouwe 
477*ad920fc4SDavid van Moolenbroek 	on = 1;
478*ad920fc4SDavid van Moolenbroek 	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) {
479*ad920fc4SDavid van Moolenbroek 		efmt("cannot set SO_REUSEADDR option on socket");
480*ad920fc4SDavid van Moolenbroek 		goto cleanup;
481*ad920fc4SDavid van Moolenbroek 	}
482*ad920fc4SDavid van Moolenbroek 
483c97d4ff6SErik van der Kouwe 	/* bind socket */
484c97d4ff6SErik van der Kouwe 	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0) {
485c97d4ff6SErik van der Kouwe 		efmt("cannot bind socket");
486c97d4ff6SErik van der Kouwe 		goto cleanup;
487c97d4ff6SErik van der Kouwe 	}
488c97d4ff6SErik van der Kouwe 
489c97d4ff6SErik van der Kouwe 	/* make it a server socket if needed */
490c97d4ff6SErik van der Kouwe 	if (type != SOCK_DGRAM) {
491c97d4ff6SErik van der Kouwe 		if (listen(fd, 5) != 0) {
492c97d4ff6SErik van der Kouwe 			efmt("cannot listen on socket");
493c97d4ff6SErik van der Kouwe 			goto cleanup;
494c97d4ff6SErik van der Kouwe 		}
495c97d4ff6SErik van der Kouwe 	}
496c97d4ff6SErik van der Kouwe 
497c97d4ff6SErik van der Kouwe 	/* intercept SIGUSR1 in case parent wants the server to stop */
498c97d4ff6SErik van der Kouwe 	SIGNAL(SIGUSR1, server_sigusr1);
499c97d4ff6SErik van der Kouwe 
500c97d4ff6SErik van der Kouwe 	/* fork; parent continues, child becomes server */
501c97d4ff6SErik van der Kouwe 	pid = fork();
502c97d4ff6SErik van der Kouwe 	if (pid < 0) {
503c97d4ff6SErik van der Kouwe 		efmt("cannot create socket");
504c97d4ff6SErik van der Kouwe 		goto cleanup;
505c97d4ff6SErik van der Kouwe 	}
506c97d4ff6SErik van der Kouwe 	if (pid) goto cleanup;
507c97d4ff6SErik van der Kouwe 
508c97d4ff6SErik van der Kouwe 	/* server loop */
509c97d4ff6SErik van der Kouwe 	dbgprintf("server_start child\n");
510c97d4ff6SErik van der Kouwe 	while (!server_done && server_accept(fd, type, action)) {}
511c97d4ff6SErik van der Kouwe 	dbgprintf("server_start child returns\n");
512c97d4ff6SErik van der Kouwe 
513c97d4ff6SErik van der Kouwe 	CLOSE(fd);
514c97d4ff6SErik van der Kouwe 	exit(errct);
515c97d4ff6SErik van der Kouwe 
516c97d4ff6SErik van der Kouwe cleanup:
517c97d4ff6SErik van der Kouwe 	dbgprintf("server_start parent returns pid=%d\n", (int) pid);
518c97d4ff6SErik van der Kouwe 	if (fd >= 0) CLOSE(fd);
519c97d4ff6SErik van der Kouwe 	return pid;
520c97d4ff6SErik van der Kouwe }
521c97d4ff6SErik van der Kouwe 
send_packet_raw(int fd,const void * buf,size_t size)522c97d4ff6SErik van der Kouwe static ssize_t send_packet_raw(int fd, const void *buf, size_t size) {
523*ad920fc4SDavid van Moolenbroek 	struct sockaddr_in sin;
524*ad920fc4SDavid van Moolenbroek 
525*ad920fc4SDavid van Moolenbroek 	memset(&sin, 0, sizeof(sin));
526*ad920fc4SDavid van Moolenbroek 	sin.sin_family = AF_INET;
527*ad920fc4SDavid van Moolenbroek 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
528*ad920fc4SDavid van Moolenbroek 
529*ad920fc4SDavid van Moolenbroek 	return sendto(fd, buf, size, 0, (struct sockaddr *)&sin, sizeof(sin));
530c97d4ff6SErik van der Kouwe }
531c97d4ff6SErik van der Kouwe 
532c97d4ff6SErik van der Kouwe enum settings_ip {
533c97d4ff6SErik van der Kouwe 	si_bad_version   = (1 <<  0),
534c97d4ff6SErik van der Kouwe 	si_bad_ihl_small = (1 <<  1),
535c97d4ff6SErik van der Kouwe 	si_bad_ihl_big   = (1 <<  2),
536c97d4ff6SErik van der Kouwe 	si_bad_len_small = (1 <<  3),
537c97d4ff6SErik van der Kouwe 	si_bad_len_big   = (1 <<  4),
538c97d4ff6SErik van der Kouwe 	si_bad_len_huge  = (1 <<  5),
539c97d4ff6SErik van der Kouwe 	si_bad_cs        = (1 <<  6),
540c97d4ff6SErik van der Kouwe 	si_zero_cs       = (1 <<  7),
541c97d4ff6SErik van der Kouwe 
542c97d4ff6SErik van der Kouwe 	si_flag_evil     = (1 <<  8),
543c97d4ff6SErik van der Kouwe 	si_flag_df       = (1 <<  9),
544c97d4ff6SErik van der Kouwe 	si_flag_mf       = (1 << 10),
545c97d4ff6SErik van der Kouwe 
546c97d4ff6SErik van der Kouwe 	si_opt_end       = (1 << 11),
547c97d4ff6SErik van der Kouwe 	si_opt_topsec    = (1 << 12),
548c97d4ff6SErik van der Kouwe 	si_opt_nop       = (1 << 13),
549c97d4ff6SErik van der Kouwe 	si_opt_badopt    = (1 << 14),
550c97d4ff6SErik van der Kouwe 	si_opt_badpad    = (1 << 15),
551c97d4ff6SErik van der Kouwe };
552c97d4ff6SErik van der Kouwe 
553c97d4ff6SErik van der Kouwe enum settings_udp {
554c97d4ff6SErik van der Kouwe 	su_bad_len_small = (1 <<  0),
555c97d4ff6SErik van der Kouwe 	su_bad_len_big   = (1 <<  1),
556c97d4ff6SErik van der Kouwe 	su_bad_len_huge  = (1 <<  2),
557c97d4ff6SErik van der Kouwe 	su_bad_cs        = (1 <<  3),
558c97d4ff6SErik van der Kouwe 	su_zero_cs       = (1 <<  4),
559c97d4ff6SErik van der Kouwe };
560c97d4ff6SErik van der Kouwe 
561c97d4ff6SErik van der Kouwe enum fragmode_ip {
562c97d4ff6SErik van der Kouwe 	fi_as_needed,
563c97d4ff6SErik van der Kouwe 	fi_one,
564c97d4ff6SErik van der Kouwe 	fi_two,
565c97d4ff6SErik van der Kouwe 	fi_frag_tiny,
566c97d4ff6SErik van der Kouwe 	fi_frag_overlap,
567c97d4ff6SErik van der Kouwe 	fi_frag_first,
568c97d4ff6SErik van der Kouwe 	fi_frag_last,
569c97d4ff6SErik van der Kouwe 	fi_frag_repeat,
570c97d4ff6SErik van der Kouwe 	fi_fo_max,
571c97d4ff6SErik van der Kouwe };
572c97d4ff6SErik van der Kouwe 
checksum_ip(const void * header,size_t headersize)573c97d4ff6SErik van der Kouwe static uint16_t checksum_ip(const void *header, size_t headersize) {
574c97d4ff6SErik van der Kouwe 	const uint16_t *p = header;
575c97d4ff6SErik van der Kouwe 	uint32_t sum = 0;
576c97d4ff6SErik van der Kouwe 
577c97d4ff6SErik van der Kouwe 	while (headersize > 0) {
578c97d4ff6SErik van der Kouwe 		assert(headersize >= sizeof(*p));
579c97d4ff6SErik van der Kouwe 		sum += ntohs(*p);
580c97d4ff6SErik van der Kouwe 		headersize -= sizeof(*p);
581c97d4ff6SErik van der Kouwe 		p++;
582c97d4ff6SErik van der Kouwe 	}
583c97d4ff6SErik van der Kouwe 	sum += sum >> 16;
584c97d4ff6SErik van der Kouwe 	return htons(~sum);
585c97d4ff6SErik van der Kouwe }
586c97d4ff6SErik van der Kouwe 
send_packet_ip_base(int fd,enum settings_ip ipsettings,uint8_t tos,uint16_t id,uint16_t fo,uint8_t ttl,uint8_t prot,uint32_t srcip,uint32_t dstip,const void * payload,size_t payloadsize)587c97d4ff6SErik van der Kouwe static void send_packet_ip_base(
588c97d4ff6SErik van der Kouwe 	int fd,
589c97d4ff6SErik van der Kouwe 	enum settings_ip ipsettings,
590c97d4ff6SErik van der Kouwe 	uint8_t  tos,
591c97d4ff6SErik van der Kouwe 	uint16_t id,
592c97d4ff6SErik van der Kouwe 	uint16_t fo,
593c97d4ff6SErik van der Kouwe 	uint8_t  ttl,
594c97d4ff6SErik van der Kouwe 	uint8_t  prot,
595c97d4ff6SErik van der Kouwe 	uint32_t srcip,
596c97d4ff6SErik van der Kouwe 	uint32_t dstip,
597c97d4ff6SErik van der Kouwe 	const void *payload,
598c97d4ff6SErik van der Kouwe 	size_t payloadsize) {
599c97d4ff6SErik van der Kouwe 	uint8_t ver = (ipsettings & si_bad_version) ? 3 : 4;
600c97d4ff6SErik van der Kouwe 	uint8_t ihl, ihl_fuzzed;
601c97d4ff6SErik van der Kouwe 	uint16_t fl = ((ipsettings & si_flag_evil) ? IP_FLAG_EVIL : 0) |
602c97d4ff6SErik van der Kouwe 	              ((ipsettings & si_flag_df)   ? IP_FLAG_DF   : 0) |
603c97d4ff6SErik van der Kouwe 	              ((ipsettings & si_flag_mf)   ? IP_FLAG_MF   : 0);
604c97d4ff6SErik van der Kouwe 	uint16_t len;
605c97d4ff6SErik van der Kouwe 	int optlen;
606c97d4ff6SErik van der Kouwe 	struct header_ip header = {
607c97d4ff6SErik van der Kouwe 		.tos     = tos,
608c97d4ff6SErik van der Kouwe 		.id      = htons(id),
609*ad920fc4SDavid van Moolenbroek 		.fl_fo   = (fl << 13) | fo, /* no htons(), lwip swaps this */
610c97d4ff6SErik van der Kouwe 		.ttl     = ttl,
611c97d4ff6SErik van der Kouwe 		.prot    = prot,
612c97d4ff6SErik van der Kouwe 		.cs      = 0,
613c97d4ff6SErik van der Kouwe 		.src     = htonl(srcip),
614c97d4ff6SErik van der Kouwe 		.dst     = htonl(dstip),
615c97d4ff6SErik van der Kouwe 	};
616c97d4ff6SErik van der Kouwe 	char packet[6536];
617c97d4ff6SErik van der Kouwe 	size_t packetsize;
618c97d4ff6SErik van der Kouwe 	ssize_t r;
619c97d4ff6SErik van der Kouwe 
620c97d4ff6SErik van der Kouwe 	dbgprintf("sending IP packet src=%d.%d.%d.%d dst=%d.%d.%d.%d "
621c97d4ff6SErik van der Kouwe 		"payloadsize=%zu id=0x%.4x fragoff=%d%s\n",
622c97d4ff6SErik van der Kouwe 		(uint8_t) (srcip >> 24), (uint8_t) (srcip >> 16),
623c97d4ff6SErik van der Kouwe 		(uint8_t) (srcip >> 8), (uint8_t) (srcip >> 0),
624c97d4ff6SErik van der Kouwe 		(uint8_t) (dstip >> 24), (uint8_t) (dstip >> 16),
625c97d4ff6SErik van der Kouwe 		(uint8_t) (dstip >> 8), (uint8_t) (dstip >> 0),
626c97d4ff6SErik van der Kouwe 		payloadsize, id, fo, (ipsettings & si_flag_mf) ? " (MF)" : "");
627c97d4ff6SErik van der Kouwe 
628c97d4ff6SErik van der Kouwe 	optlen = 0;
629c97d4ff6SErik van der Kouwe 	if (ipsettings & si_opt_badpad) memset(header.opt, -1, sizeof(header.opt));
630c97d4ff6SErik van der Kouwe 	if (ipsettings & si_opt_nop) header.opt[optlen++] = 0x01;
631c97d4ff6SErik van der Kouwe 	if (ipsettings & si_opt_topsec) {
632c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x82;
633c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x0b;
634c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x6b; /* S: top secret */
635c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0xc5; /* S: top secret */
636c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x00; /* C */
637c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x00; /* C */
638c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 'A'; /* H */
639c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 'B'; /* H */
640c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 'C'; /* TCC */
641c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 'D'; /* TCC */
642c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 'E'; /* TCC */
643c97d4ff6SErik van der Kouwe 	}
644c97d4ff6SErik van der Kouwe 	if (ipsettings & si_opt_badopt) header.opt[optlen++] = 0xff;
645c97d4ff6SErik van der Kouwe 	if (ipsettings & si_opt_end) header.opt[optlen++] = 0x00;
646c97d4ff6SErik van der Kouwe 	assert(optlen <= sizeof(header.opt));
647c97d4ff6SErik van der Kouwe 
648c97d4ff6SErik van der Kouwe 	ihl = ihl_fuzzed = (20 + optlen + 3) / 4;
649c97d4ff6SErik van der Kouwe 	if (ipsettings & si_bad_ihl_small) ihl_fuzzed = 4;
650c97d4ff6SErik van der Kouwe 	if (ipsettings & si_bad_ihl_big) ihl_fuzzed = 15;
651c97d4ff6SErik van der Kouwe 	header.ver_ihl = (ver << 4) | ihl_fuzzed;
652c97d4ff6SErik van der Kouwe 
653c97d4ff6SErik van der Kouwe 	len = ihl * 4 + payloadsize;
654c97d4ff6SErik van der Kouwe 	if (ipsettings & si_bad_len_small) len = ihl * 4 - 1;
655c97d4ff6SErik van der Kouwe 	if (ipsettings & si_bad_len_big) len += 1;
656c97d4ff6SErik van der Kouwe 	if (ipsettings & si_bad_len_huge) len = 0xffff;
657*ad920fc4SDavid van Moolenbroek 	header.len = len; /* no htons(), lwip swaps this */
658c97d4ff6SErik van der Kouwe 
659c97d4ff6SErik van der Kouwe 	packetsize = ihl * 4 + payloadsize;
660c97d4ff6SErik van der Kouwe 	if (packetsize > sizeof(packet)) {
661c97d4ff6SErik van der Kouwe 		payloadsize = sizeof(packet) - ihl * 4;
662c97d4ff6SErik van der Kouwe 		packetsize = sizeof(packet);
663c97d4ff6SErik van der Kouwe 	}
664c97d4ff6SErik van der Kouwe 
665c97d4ff6SErik van der Kouwe 	header.cs = checksum_ip(&header, ihl * 4);
666c97d4ff6SErik van der Kouwe 	if (ipsettings & si_zero_cs) header.cs = 0;
667c97d4ff6SErik van der Kouwe 	if (ipsettings & si_bad_cs) header.cs += 1;
668c97d4ff6SErik van der Kouwe 
669c97d4ff6SErik van der Kouwe 	memset(packet, 0, sizeof(packet));
670c97d4ff6SErik van der Kouwe 	memcpy(packet, &header, ihl * 4);
671c97d4ff6SErik van der Kouwe 	memcpy(packet + ihl * 4, payload, payloadsize);
672c97d4ff6SErik van der Kouwe 
673c97d4ff6SErik van der Kouwe 	errno = 0;
674c97d4ff6SErik van der Kouwe 	r = send_packet_raw(fd, packet, packetsize);
675c97d4ff6SErik van der Kouwe 	if (r == -1 && errno == EPACKSIZE &&
676c97d4ff6SErik van der Kouwe 		(packetsize < 60 || packetsize > 1514)) {
677c97d4ff6SErik van der Kouwe 		return;
678c97d4ff6SErik van der Kouwe 	}
679c97d4ff6SErik van der Kouwe 	if (r != packetsize) {
680c97d4ff6SErik van der Kouwe 		efmt("write to network interface failed");
681c97d4ff6SErik van der Kouwe 	}
682c97d4ff6SErik van der Kouwe }
683c97d4ff6SErik van der Kouwe 
send_packet_ip(int fd,enum settings_ip ipsettings,uint8_t tos,uint16_t id,uint8_t ttl,uint8_t prot,uint32_t srcip,uint32_t dstip,enum fragmode_ip fragmode,const void * payload,size_t payloadsize)684c97d4ff6SErik van der Kouwe static void send_packet_ip(
685c97d4ff6SErik van der Kouwe 	int fd,
686c97d4ff6SErik van der Kouwe 	enum settings_ip ipsettings,
687c97d4ff6SErik van der Kouwe 	uint8_t tos,
688c97d4ff6SErik van der Kouwe 	uint16_t id,
689c97d4ff6SErik van der Kouwe 	uint8_t ttl,
690c97d4ff6SErik van der Kouwe 	uint8_t prot,
691c97d4ff6SErik van der Kouwe 	uint32_t srcip,
692c97d4ff6SErik van der Kouwe 	uint32_t dstip,
693c97d4ff6SErik van der Kouwe 	enum fragmode_ip fragmode,
694c97d4ff6SErik van der Kouwe 	const void *payload,
695c97d4ff6SErik van der Kouwe 	size_t payloadsize) {
696c97d4ff6SErik van der Kouwe 	enum settings_ip flags;
697c97d4ff6SErik van der Kouwe 	size_t fragcount = 1;
698c97d4ff6SErik van der Kouwe 	size_t fragsize, fragsizecur;
699c97d4ff6SErik van der Kouwe 	size_t fragstart = 0;
700c97d4ff6SErik van der Kouwe 	size_t fragstep;
701c97d4ff6SErik van der Kouwe 
702c97d4ff6SErik van der Kouwe 	switch (fragmode) {
703c97d4ff6SErik van der Kouwe 	case fi_as_needed:
704c97d4ff6SErik van der Kouwe 		fragsize = fragstep = 1500;
705c97d4ff6SErik van der Kouwe 		fragcount = (payloadsize + fragsize - 1) / fragsize;
706c97d4ff6SErik van der Kouwe 		break;
707c97d4ff6SErik van der Kouwe 	case fi_one:
708c97d4ff6SErik van der Kouwe 	case fi_fo_max:
709c97d4ff6SErik van der Kouwe 		fragsize = fragstep = payloadsize;
710c97d4ff6SErik van der Kouwe 		break;
711c97d4ff6SErik van der Kouwe 	case fi_two:
712c97d4ff6SErik van der Kouwe 		fragcount = 2;
713c97d4ff6SErik van der Kouwe 		fragsize = fragstep = (payloadsize + 1) / 2;
714c97d4ff6SErik van der Kouwe 		break;
715c97d4ff6SErik van der Kouwe 	case fi_frag_tiny:
716c97d4ff6SErik van der Kouwe 		fragcount = (payloadsize >= 100) ? 100 :
717c97d4ff6SErik van der Kouwe 			(payloadsize < 1) ? 1 : payloadsize;
718c97d4ff6SErik van der Kouwe 		fragsize = fragstep = (payloadsize + fragcount - 1) / fragcount;
719c97d4ff6SErik van der Kouwe 		break;
720c97d4ff6SErik van der Kouwe 	case fi_frag_overlap:
721c97d4ff6SErik van der Kouwe 		fragcount = 2;
722c97d4ff6SErik van der Kouwe 		fragsize = (payloadsize * 2 + 2) / 3;
723c97d4ff6SErik van der Kouwe 		fragstep = (payloadsize + 1) / 2;
724c97d4ff6SErik van der Kouwe 		break;
725c97d4ff6SErik van der Kouwe 	case fi_frag_first:
726c97d4ff6SErik van der Kouwe 		fragcount = 1;
727c97d4ff6SErik van der Kouwe 		fragsize = fragstep = (payloadsize + 1) / 2;
728c97d4ff6SErik van der Kouwe 		break;
729c97d4ff6SErik van der Kouwe 	case fi_frag_last:
730c97d4ff6SErik van der Kouwe 		fragcount = 1;
731c97d4ff6SErik van der Kouwe 		fragsize = fragstep = (payloadsize + 1) / 2;
732c97d4ff6SErik van der Kouwe 		break;
733c97d4ff6SErik van der Kouwe 	case fi_frag_repeat:
734c97d4ff6SErik van der Kouwe 		fragcount = 2;
735c97d4ff6SErik van der Kouwe 		fragsize = payloadsize;
736c97d4ff6SErik van der Kouwe 		fragstep = 0;
737c97d4ff6SErik van der Kouwe 		break;
7387c48de6cSDavid van Moolenbroek 	default:
7397c48de6cSDavid van Moolenbroek 		abort();
740c97d4ff6SErik van der Kouwe 	}
741c97d4ff6SErik van der Kouwe 
742c97d4ff6SErik van der Kouwe 	while (fragcount > 0) {
743c97d4ff6SErik van der Kouwe 		if (fragstart >= payloadsize) {
744c97d4ff6SErik van der Kouwe 			fragsizecur = 0;
745c97d4ff6SErik van der Kouwe 		} else if (payloadsize - fragstart < fragsize) {
746c97d4ff6SErik van der Kouwe 			fragsizecur = payloadsize - fragstart;
747c97d4ff6SErik van der Kouwe 		} else {
748c97d4ff6SErik van der Kouwe 			fragsizecur = fragsize;
749c97d4ff6SErik van der Kouwe 		}
750c97d4ff6SErik van der Kouwe 
751c97d4ff6SErik van der Kouwe 		flags = 0;
752c97d4ff6SErik van der Kouwe 		if (fragstart + fragsizecur < payloadsize) flags |= si_flag_mf;
753c97d4ff6SErik van der Kouwe 		send_packet_ip_base(
754c97d4ff6SErik van der Kouwe 			fd,
755c97d4ff6SErik van der Kouwe 			ipsettings | flags,
756c97d4ff6SErik van der Kouwe 			tos,
757c97d4ff6SErik van der Kouwe 			id,
758c97d4ff6SErik van der Kouwe 			(fragmode == fi_fo_max) ? 0x1fff : fragstart,
759c97d4ff6SErik van der Kouwe 			ttl,
760c97d4ff6SErik van der Kouwe 			prot,
761c97d4ff6SErik van der Kouwe 			srcip,
762c97d4ff6SErik van der Kouwe 			dstip,
763c97d4ff6SErik van der Kouwe 			(uint8_t *) payload + fragstart,
764c97d4ff6SErik van der Kouwe 			fragsizecur);
765c97d4ff6SErik van der Kouwe 
766c97d4ff6SErik van der Kouwe 		fragcount--;
767c97d4ff6SErik van der Kouwe 		fragstart += fragstep;
768c97d4ff6SErik van der Kouwe 	}
769c97d4ff6SErik van der Kouwe }
770c97d4ff6SErik van der Kouwe 
checksum_udp_sum(const void * buf,size_t size)771c97d4ff6SErik van der Kouwe static uint32_t checksum_udp_sum(const void *buf, size_t size) {
772c97d4ff6SErik van der Kouwe 	const uint16_t *p = buf;
773c97d4ff6SErik van der Kouwe 	uint32_t sum = 0;
774c97d4ff6SErik van der Kouwe 
775c97d4ff6SErik van der Kouwe 	while (size > 0) {
776c97d4ff6SErik van der Kouwe 		assert(size >= sizeof(*p));
777c97d4ff6SErik van der Kouwe 		sum += ntohs(*p);
778c97d4ff6SErik van der Kouwe 		size -= sizeof(*p);
779c97d4ff6SErik van der Kouwe 		p++;
780c97d4ff6SErik van der Kouwe 	}
781c97d4ff6SErik van der Kouwe 	return sum;
782c97d4ff6SErik van der Kouwe }
783c97d4ff6SErik van der Kouwe 
checksum_udp(uint32_t srcip,uint32_t dstip,uint8_t prot,const void * packet,size_t packetsize)784c97d4ff6SErik van der Kouwe static uint16_t checksum_udp(
785c97d4ff6SErik van der Kouwe 	uint32_t srcip,
786c97d4ff6SErik van der Kouwe 	uint32_t dstip,
787c97d4ff6SErik van der Kouwe 	uint8_t prot,
788c97d4ff6SErik van der Kouwe 	const void *packet,
789c97d4ff6SErik van der Kouwe 	size_t packetsize) {
790c97d4ff6SErik van der Kouwe 	uint32_t sum = 0;
791c97d4ff6SErik van der Kouwe 	struct header_udp_pseudo header = {
792c97d4ff6SErik van der Kouwe 		.src = htonl(srcip),
793c97d4ff6SErik van der Kouwe 		.dst = htonl(dstip),
794c97d4ff6SErik van der Kouwe 		.zero = 0,
795c97d4ff6SErik van der Kouwe 		.prot = prot,
796c97d4ff6SErik van der Kouwe 		.len = htons(packetsize),
797c97d4ff6SErik van der Kouwe 	};
798c97d4ff6SErik van der Kouwe 
799c97d4ff6SErik van der Kouwe 	sum = checksum_udp_sum(&header, sizeof(header)) +
800c97d4ff6SErik van der Kouwe 		checksum_udp_sum(packet, packetsize + packetsize % 2);
801c97d4ff6SErik van der Kouwe 	sum += sum >> 16;
802c97d4ff6SErik van der Kouwe 	return ntohs(~sum);
803c97d4ff6SErik van der Kouwe }
804c97d4ff6SErik van der Kouwe 
send_packet_udp(int fd,enum settings_ip ipsettings,uint8_t tos,uint16_t id,uint8_t ttl,uint8_t prot,uint32_t srcip,uint32_t dstip,enum fragmode_ip fragmode,enum settings_udp udpsettings,uint16_t srcport,uint16_t dstport,const void * payload,size_t payloadsize)805c97d4ff6SErik van der Kouwe static void send_packet_udp(
806c97d4ff6SErik van der Kouwe 	int fd,
807c97d4ff6SErik van der Kouwe 	enum settings_ip ipsettings,
808c97d4ff6SErik van der Kouwe 	uint8_t tos,
809c97d4ff6SErik van der Kouwe 	uint16_t id,
810c97d4ff6SErik van der Kouwe 	uint8_t ttl,
811c97d4ff6SErik van der Kouwe 	uint8_t prot,
812c97d4ff6SErik van der Kouwe 	uint32_t srcip,
813c97d4ff6SErik van der Kouwe 	uint32_t dstip,
814c97d4ff6SErik van der Kouwe 	enum fragmode_ip fragmode,
815c97d4ff6SErik van der Kouwe 	enum settings_udp udpsettings,
816c97d4ff6SErik van der Kouwe 	uint16_t srcport,
817c97d4ff6SErik van der Kouwe 	uint16_t dstport,
818c97d4ff6SErik van der Kouwe 	const void *payload,
819c97d4ff6SErik van der Kouwe 	size_t payloadsize) {
820c97d4ff6SErik van der Kouwe 	uint16_t len;
821c97d4ff6SErik van der Kouwe 	struct header_udp header = {
822c97d4ff6SErik van der Kouwe 		.src = htons(srcport),
823c97d4ff6SErik van der Kouwe 		.dst = htons(dstport),
824c97d4ff6SErik van der Kouwe 		.cs = 0,
825c97d4ff6SErik van der Kouwe 	};
826c97d4ff6SErik van der Kouwe 	char packet[65536];
827c97d4ff6SErik van der Kouwe 	size_t packetsize;
828c97d4ff6SErik van der Kouwe 
829c97d4ff6SErik van der Kouwe 	dbgprintf("sending UDP packet srcport=%d dstport=%d payloadsize=%zu\n",
830c97d4ff6SErik van der Kouwe 		srcport, dstport, payloadsize);
831c97d4ff6SErik van der Kouwe 
832c97d4ff6SErik van der Kouwe 	len = sizeof(struct header_udp) + payloadsize;
833c97d4ff6SErik van der Kouwe 	if (udpsettings & su_bad_len_small) len = sizeof(struct header_udp) - 1;
834c97d4ff6SErik van der Kouwe 	if (udpsettings & su_bad_len_big) len += 1;
835c97d4ff6SErik van der Kouwe 	if (udpsettings & su_bad_len_huge) len = 65535 - sizeof(struct header_ip);
836c97d4ff6SErik van der Kouwe 	header.len = htons(len);
837c97d4ff6SErik van der Kouwe 
838c97d4ff6SErik van der Kouwe 	packetsize = sizeof(header) + payloadsize;
839c97d4ff6SErik van der Kouwe 	assert(packetsize <= sizeof(packet));
840c97d4ff6SErik van der Kouwe 
841c97d4ff6SErik van der Kouwe 	memcpy(packet, &header, sizeof(header));
842c97d4ff6SErik van der Kouwe 	memcpy(packet + sizeof(header), payload, payloadsize);
843c97d4ff6SErik van der Kouwe 	if (packetsize % 2) packet[packetsize] = 0;
844c97d4ff6SErik van der Kouwe 
845c97d4ff6SErik van der Kouwe 	header.cs = checksum_udp(srcip, dstip, prot, packet, packetsize);
846c97d4ff6SErik van der Kouwe 	if (udpsettings & su_zero_cs) header.cs = 0;
847c97d4ff6SErik van der Kouwe 	if (udpsettings & su_bad_cs) header.cs += 1;
848c97d4ff6SErik van der Kouwe 
849c97d4ff6SErik van der Kouwe 	memcpy(packet, &header, sizeof(header));
850c97d4ff6SErik van der Kouwe 	send_packet_ip(
851c97d4ff6SErik van der Kouwe 		fd,
852c97d4ff6SErik van der Kouwe 		ipsettings,
853c97d4ff6SErik van der Kouwe 		tos,
854c97d4ff6SErik van der Kouwe 		id,
855c97d4ff6SErik van der Kouwe 		ttl,
856c97d4ff6SErik van der Kouwe 		prot,
857c97d4ff6SErik van der Kouwe 		srcip,
858c97d4ff6SErik van der Kouwe 		dstip,
859c97d4ff6SErik van der Kouwe 		fragmode,
860c97d4ff6SErik van der Kouwe 		packet,
861c97d4ff6SErik van der Kouwe 		packetsize);
862c97d4ff6SErik van der Kouwe }
863c97d4ff6SErik van der Kouwe 
864c97d4ff6SErik van der Kouwe struct send_packet_udp_simple_params {
865c97d4ff6SErik van der Kouwe 	int fd;
866c97d4ff6SErik van der Kouwe 	enum settings_ip ipsettings;
867c97d4ff6SErik van der Kouwe 	uint8_t tos;
868c97d4ff6SErik van der Kouwe 	uint16_t *id;
869c97d4ff6SErik van der Kouwe 	uint8_t ttl;
870c97d4ff6SErik van der Kouwe 	uint8_t prot;
871c97d4ff6SErik van der Kouwe 	uint32_t srcip;
872c97d4ff6SErik van der Kouwe 	uint32_t dstip;
873c97d4ff6SErik van der Kouwe 	enum fragmode_ip fragmode;
874c97d4ff6SErik van der Kouwe 	enum settings_udp udpsettings;
875c97d4ff6SErik van der Kouwe 	uint16_t srcport;
876c97d4ff6SErik van der Kouwe 	uint16_t dstport;
877c97d4ff6SErik van der Kouwe 	size_t payloadsize;
878c97d4ff6SErik van der Kouwe };
879c97d4ff6SErik van der Kouwe 
send_packet_udp_simple(const struct send_packet_udp_simple_params * params)880c97d4ff6SErik van der Kouwe static void send_packet_udp_simple(
881c97d4ff6SErik van der Kouwe 	const struct send_packet_udp_simple_params *params) {
882c97d4ff6SErik van der Kouwe 	int i;
883c97d4ff6SErik van der Kouwe 	char payload[65536];
884c97d4ff6SErik van der Kouwe 
885c97d4ff6SErik van der Kouwe 	assert(params->payloadsize <= sizeof(payload));
886c97d4ff6SErik van der Kouwe 	for (i = 0; i < params->payloadsize; i++) {
887c97d4ff6SErik van der Kouwe 		payload[i] = *params->id + i;
888c97d4ff6SErik van der Kouwe 	}
889c97d4ff6SErik van der Kouwe 
890c97d4ff6SErik van der Kouwe 	send_packet_udp(
891c97d4ff6SErik van der Kouwe 		params->fd,
892c97d4ff6SErik van der Kouwe 		params->ipsettings,
893c97d4ff6SErik van der Kouwe 		params->tos,
894c97d4ff6SErik van der Kouwe 		*params->id,
895c97d4ff6SErik van der Kouwe 		params->ttl,
896c97d4ff6SErik van der Kouwe 		params->prot,
897c97d4ff6SErik van der Kouwe 		params->srcip,
898c97d4ff6SErik van der Kouwe 		params->dstip,
899c97d4ff6SErik van der Kouwe 		params->fragmode,
900c97d4ff6SErik van der Kouwe 		params->udpsettings,
901c97d4ff6SErik van der Kouwe 		params->srcport,
902c97d4ff6SErik van der Kouwe 		params->dstport,
903c97d4ff6SErik van der Kouwe 		payload,
904c97d4ff6SErik van der Kouwe 		params->payloadsize);
905c97d4ff6SErik van der Kouwe 	*params->id += 5471;
906c97d4ff6SErik van der Kouwe }
907c97d4ff6SErik van der Kouwe 
send_packets_ip_settings(const struct send_packet_udp_simple_params * paramsbase)908c97d4ff6SErik van der Kouwe static void send_packets_ip_settings(
909c97d4ff6SErik van der Kouwe 	const struct send_packet_udp_simple_params *paramsbase) {
910c97d4ff6SErik van der Kouwe 	struct send_packet_udp_simple_params params;
911c97d4ff6SErik van der Kouwe 	int i;
912c97d4ff6SErik van der Kouwe 	enum settings_ip ipsettings[] = {
913c97d4ff6SErik van der Kouwe 		0,
914c97d4ff6SErik van der Kouwe 		si_bad_version,
915c97d4ff6SErik van der Kouwe 		si_bad_ihl_small,
916c97d4ff6SErik van der Kouwe 		si_bad_ihl_big,
917c97d4ff6SErik van der Kouwe 		si_bad_len_small,
918c97d4ff6SErik van der Kouwe 		si_bad_len_big,
919c97d4ff6SErik van der Kouwe 		si_bad_len_huge,
920c97d4ff6SErik van der Kouwe 		si_bad_cs,
921c97d4ff6SErik van der Kouwe 		si_zero_cs,
922c97d4ff6SErik van der Kouwe 		si_flag_evil,
923c97d4ff6SErik van der Kouwe 		si_flag_df,
924c97d4ff6SErik van der Kouwe 		si_flag_mf,
925c97d4ff6SErik van der Kouwe 		si_opt_end,
926c97d4ff6SErik van der Kouwe 		si_opt_topsec,
927c97d4ff6SErik van der Kouwe 		si_opt_nop,
928c97d4ff6SErik van der Kouwe 		si_opt_badopt,
929c97d4ff6SErik van der Kouwe 		si_opt_nop | si_opt_end | si_opt_badpad,
930c97d4ff6SErik van der Kouwe 	};
931c97d4ff6SErik van der Kouwe 	uint8_t ttls[] = { 0, 1, 127, 128, 255 };
932c97d4ff6SErik van der Kouwe 
933c97d4ff6SErik van der Kouwe 	/* various types of flags/options/corruptions */
934c97d4ff6SErik van der Kouwe 	params = *paramsbase;
935c97d4ff6SErik van der Kouwe 	for (i = 0; i < 17; i++) {
936c97d4ff6SErik van der Kouwe 		params.ipsettings = ipsettings[i];
937c97d4ff6SErik van der Kouwe 		send_packet_udp_simple(&params);
938c97d4ff6SErik van der Kouwe 	}
939c97d4ff6SErik van der Kouwe 
940c97d4ff6SErik van der Kouwe 	/* various TTL settings */
941c97d4ff6SErik van der Kouwe 	params = *paramsbase;
942c97d4ff6SErik van der Kouwe 	for (i = 0; i < 5; i++) {
943c97d4ff6SErik van der Kouwe 		params.ttl = ttls[i];
944c97d4ff6SErik van der Kouwe 		send_packet_udp_simple(&params);
945c97d4ff6SErik van der Kouwe 	}
946c97d4ff6SErik van der Kouwe }
947c97d4ff6SErik van der Kouwe 
send_packets_ip(int fd)948c97d4ff6SErik van der Kouwe static void send_packets_ip(int fd) {
949c97d4ff6SErik van der Kouwe 	enum fragmode_ip fragmode;
950c97d4ff6SErik van der Kouwe 	int i, j;
951c97d4ff6SErik van der Kouwe 	uint16_t id = 0;
952c97d4ff6SErik van der Kouwe 	struct send_packet_udp_simple_params params;
953c97d4ff6SErik van der Kouwe 	const struct send_packet_udp_simple_params paramsbase = {
954c97d4ff6SErik van der Kouwe 		.fd            = fd,
955c97d4ff6SErik van der Kouwe 		.ipsettings    = 0,
956c97d4ff6SErik van der Kouwe 		.tos           = 0,
957c97d4ff6SErik van der Kouwe 		.id            = &id,
958c97d4ff6SErik van der Kouwe 		.ttl           = 10,
959c97d4ff6SErik van der Kouwe 		.prot          = IP_PROT_UDP,
960c97d4ff6SErik van der Kouwe 		.srcip         = addrsrc,
961c97d4ff6SErik van der Kouwe 		.dstip         = addrdst,
962c97d4ff6SErik van der Kouwe 		.fragmode      = fi_as_needed,
963c97d4ff6SErik van der Kouwe 		.udpsettings   = 0,
964c97d4ff6SErik van der Kouwe 		.srcport       = PORT_BASE + 0,
965c97d4ff6SErik van der Kouwe 		.dstport       = PORT_BASE + 1,
966c97d4ff6SErik van der Kouwe 		.payloadsize   = 1234,
967c97d4ff6SErik van der Kouwe 	};
968c97d4ff6SErik van der Kouwe 
969c97d4ff6SErik van der Kouwe 	/* send packets with various payload sizes and corruptions */
970c97d4ff6SErik van der Kouwe 	params = paramsbase;
971c97d4ff6SErik van der Kouwe 	for (i = 0; i < PAYLOADSIZE_COUNT; i++) {
972c97d4ff6SErik van der Kouwe 		params.payloadsize = payloadsizes[i];
973c97d4ff6SErik van der Kouwe 		send_packets_ip_settings(&params);
974c97d4ff6SErik van der Kouwe 	}
975c97d4ff6SErik van der Kouwe 
976c97d4ff6SErik van der Kouwe 	/* send packets with various addresses and corruptions */
977c97d4ff6SErik van der Kouwe 	params = paramsbase;
978*ad920fc4SDavid van Moolenbroek 	for (i = 0; i < __arraycount(addrs); i++) {
979*ad920fc4SDavid van Moolenbroek 	for (j = 0; j < __arraycount(addrs); j++) {
980c97d4ff6SErik van der Kouwe 		params.srcip = addrs[i];
981c97d4ff6SErik van der Kouwe 		params.dstip = addrs[j];
982c97d4ff6SErik van der Kouwe 		send_packets_ip_settings(&params);
983c97d4ff6SErik van der Kouwe 	}
984c97d4ff6SErik van der Kouwe 	}
985c97d4ff6SErik van der Kouwe 
986c97d4ff6SErik van der Kouwe 	/* send valid packets with various fragmentation settings */
987c97d4ff6SErik van der Kouwe 	params = paramsbase;
988c97d4ff6SErik van der Kouwe 	for (i = 0; i < PAYLOADSIZE_COUNT; i++) {
989c97d4ff6SErik van der Kouwe 	for (fragmode = fi_as_needed; fragmode <= fi_fo_max; fragmode++) {
990c97d4ff6SErik van der Kouwe 		params.payloadsize = payloadsizes[i];
991c97d4ff6SErik van der Kouwe 		params.fragmode = fragmode;
992c97d4ff6SErik van der Kouwe 		send_packet_udp_simple(&params);
993c97d4ff6SErik van der Kouwe 	}
994c97d4ff6SErik van der Kouwe 	}
995c97d4ff6SErik van der Kouwe 
996c97d4ff6SErik van der Kouwe 	/* send a packet for each protocol */
997c97d4ff6SErik van der Kouwe 	params = paramsbase;
998c97d4ff6SErik van der Kouwe 	for (i = 0; i < 256; i++) {
999c97d4ff6SErik van der Kouwe 		params.prot = i;
1000c97d4ff6SErik van der Kouwe 		send_packet_udp_simple(&params);
1001c97d4ff6SErik van der Kouwe 	}
1002c97d4ff6SErik van der Kouwe 
1003c97d4ff6SErik van der Kouwe 	/* send a packet for each tos */
1004c97d4ff6SErik van der Kouwe 	params = paramsbase;
1005c97d4ff6SErik van der Kouwe 	for (i = 0; i < 256; i++) {
1006c97d4ff6SErik van der Kouwe 		params.tos = i;
1007c97d4ff6SErik van der Kouwe 		send_packet_udp_simple(&params);
1008c97d4ff6SErik van der Kouwe 	}
1009c97d4ff6SErik van der Kouwe }
1010c97d4ff6SErik van der Kouwe 
send_packets_udp(int fd)1011c97d4ff6SErik van der Kouwe static void send_packets_udp(int fd) {
1012c97d4ff6SErik van der Kouwe 	int i, j, k;
1013c97d4ff6SErik van der Kouwe 	uint16_t id = 0;
1014c97d4ff6SErik van der Kouwe 	struct send_packet_udp_simple_params params;
1015c97d4ff6SErik van der Kouwe 	const struct send_packet_udp_simple_params paramsbase = {
1016c97d4ff6SErik van der Kouwe 		.fd            = fd,
1017c97d4ff6SErik van der Kouwe 		.ipsettings    = 0,
1018c97d4ff6SErik van der Kouwe 		.tos           = 0,
1019c97d4ff6SErik van der Kouwe 		.id            = &id,
1020c97d4ff6SErik van der Kouwe 		.ttl           = 10,
1021c97d4ff6SErik van der Kouwe 		.prot          = IP_PROT_UDP,
1022c97d4ff6SErik van der Kouwe 		.srcip         = addrsrc,
1023c97d4ff6SErik van der Kouwe 		.dstip         = addrdst,
1024c97d4ff6SErik van der Kouwe 		.fragmode      = fi_as_needed,
1025c97d4ff6SErik van der Kouwe 		.udpsettings   = 0,
1026c97d4ff6SErik van der Kouwe 		.srcport       = PORT_BASE + 0,
1027c97d4ff6SErik van der Kouwe 		.dstport       = PORT_BASE + 1,
1028c97d4ff6SErik van der Kouwe 		.payloadsize   = 1234,
1029c97d4ff6SErik van der Kouwe 	};
1030c97d4ff6SErik van der Kouwe 	uint16_t ports[] = {
1031c97d4ff6SErik van der Kouwe 		0,
1032c97d4ff6SErik van der Kouwe 		PORT_BASE + 0,
1033c97d4ff6SErik van der Kouwe 		PORT_BASE + 1,
1034c97d4ff6SErik van der Kouwe 		32767,
1035c97d4ff6SErik van der Kouwe 		65535,
1036c97d4ff6SErik van der Kouwe 	};
1037c97d4ff6SErik van der Kouwe 	enum settings_udp udpsettings[] = {
1038c97d4ff6SErik van der Kouwe 		0,
1039c97d4ff6SErik van der Kouwe 		su_bad_len_small,
1040c97d4ff6SErik van der Kouwe 		su_bad_len_big,
1041c97d4ff6SErik van der Kouwe 		su_bad_len_huge,
1042c97d4ff6SErik van der Kouwe 		su_bad_cs,
1043c97d4ff6SErik van der Kouwe 		su_zero_cs,
1044c97d4ff6SErik van der Kouwe 	};
1045c97d4ff6SErik van der Kouwe 
1046c97d4ff6SErik van der Kouwe 	/* send packets with various corruptions */
1047c97d4ff6SErik van der Kouwe 	params = paramsbase;
1048c97d4ff6SErik van der Kouwe 	for (i = 0; i < 6; i++) {
1049c97d4ff6SErik van der Kouwe 		params.udpsettings = udpsettings[i];
1050c97d4ff6SErik van der Kouwe 		send_packet_udp_simple(&params);
1051c97d4ff6SErik van der Kouwe 	}
1052c97d4ff6SErik van der Kouwe 
1053c97d4ff6SErik van der Kouwe 	/* send packets with various addresses and ports */
1054c97d4ff6SErik van der Kouwe 	params = paramsbase;
1055*ad920fc4SDavid van Moolenbroek 	for (i = 0; i < __arraycount(addrs); i++) {
1056*ad920fc4SDavid van Moolenbroek 	for (j = 0; j < __arraycount(addrs); j++) {
1057c97d4ff6SErik van der Kouwe 	for (k = 0; k < 5; k++) {
1058c97d4ff6SErik van der Kouwe 		params.srcip = addrs[i];
1059c97d4ff6SErik van der Kouwe 		params.dstip = addrs[j];
1060c97d4ff6SErik van der Kouwe 		params.dstport = ports[k];
1061c97d4ff6SErik van der Kouwe 		send_packet_udp_simple(&params);
1062c97d4ff6SErik van der Kouwe 	}
1063c97d4ff6SErik van der Kouwe 	}
1064c97d4ff6SErik van der Kouwe 	}
1065c97d4ff6SErik van der Kouwe 	params = paramsbase;
1066*ad920fc4SDavid van Moolenbroek 	for (i = 0; i < __arraycount(addrs); i++) {
1067c97d4ff6SErik van der Kouwe 	for (j = 0; j < 5; j++) {
1068c97d4ff6SErik van der Kouwe 	for (k = 0; k < 5; k++) {
1069c97d4ff6SErik van der Kouwe 		params.dstip = addrs[i];
1070c97d4ff6SErik van der Kouwe 		params.srcport = ports[j];
1071c97d4ff6SErik van der Kouwe 		params.dstport = ports[k];
1072c97d4ff6SErik van der Kouwe 		send_packet_udp_simple(&params);
1073c97d4ff6SErik van der Kouwe 	}
1074c97d4ff6SErik van der Kouwe 	}
1075c97d4ff6SErik van der Kouwe 	}
1076c97d4ff6SErik van der Kouwe }
1077c97d4ff6SErik van der Kouwe 
1078c97d4ff6SErik van der Kouwe enum settings_tcp {
1079c97d4ff6SErik van der Kouwe 	st_bad_doff_small  = (1 <<  0),
1080c97d4ff6SErik van der Kouwe 	st_bad_doff_big    = (1 <<  1),
1081c97d4ff6SErik van der Kouwe 	st_bad_doff_huge   = (1 <<  2),
1082c97d4ff6SErik van der Kouwe 	st_bad_cs          = (1 <<  3),
1083c97d4ff6SErik van der Kouwe 	st_zero_cs         = (1 <<  4),
1084c97d4ff6SErik van der Kouwe 	st_opt_end         = (1 <<  5),
1085c97d4ff6SErik van der Kouwe 	st_opt_nop         = (1 <<  6),
1086c97d4ff6SErik van der Kouwe 	st_opt_mss_small   = (1 <<  7),
1087c97d4ff6SErik van der Kouwe 	st_opt_mss_big     = (1 <<  8),
1088c97d4ff6SErik van der Kouwe 	st_opt_mss_huge    = (1 <<  9),
1089c97d4ff6SErik van der Kouwe 	st_opt_badpad      = (1 << 10),
1090c97d4ff6SErik van der Kouwe };
1091c97d4ff6SErik van der Kouwe 
send_packet_tcp(int fd,enum settings_ip ipsettings,uint8_t tos,uint16_t id,uint8_t ttl,uint8_t prot,uint32_t srcip,uint32_t dstip,enum fragmode_ip fragmode,enum settings_tcp tcpsettings,uint16_t srcport,uint16_t dstport,uint32_t seq,uint32_t ack,uint8_t fl,uint16_t win,uint16_t uptr,const void * payload,size_t payloadsize)1092c97d4ff6SErik van der Kouwe static void send_packet_tcp(
1093c97d4ff6SErik van der Kouwe 	int fd,
1094c97d4ff6SErik van der Kouwe 	enum settings_ip ipsettings,
1095c97d4ff6SErik van der Kouwe 	uint8_t tos,
1096c97d4ff6SErik van der Kouwe 	uint16_t id,
1097c97d4ff6SErik van der Kouwe 	uint8_t ttl,
1098c97d4ff6SErik van der Kouwe 	uint8_t prot,
1099c97d4ff6SErik van der Kouwe 	uint32_t srcip,
1100c97d4ff6SErik van der Kouwe 	uint32_t dstip,
1101c97d4ff6SErik van der Kouwe 	enum fragmode_ip fragmode,
1102c97d4ff6SErik van der Kouwe 	enum settings_tcp tcpsettings,
1103c97d4ff6SErik van der Kouwe 	uint16_t srcport,
1104c97d4ff6SErik van der Kouwe 	uint16_t dstport,
1105c97d4ff6SErik van der Kouwe 	uint32_t seq,
1106c97d4ff6SErik van der Kouwe 	uint32_t ack,
1107c97d4ff6SErik van der Kouwe 	uint8_t fl,
1108c97d4ff6SErik van der Kouwe 	uint16_t win,
1109c97d4ff6SErik van der Kouwe 	uint16_t uptr,
1110c97d4ff6SErik van der Kouwe 	const void *payload,
1111c97d4ff6SErik van der Kouwe 	size_t payloadsize) {
1112c97d4ff6SErik van der Kouwe 	uint8_t doff, doff_fuzzed;
1113c97d4ff6SErik van der Kouwe 	int optlen;
1114c97d4ff6SErik van der Kouwe 	struct header_tcp header = {
1115c97d4ff6SErik van der Kouwe 		.src  = htons(srcport),
1116c97d4ff6SErik van der Kouwe 		.dst  = htons(dstport),
1117c97d4ff6SErik van der Kouwe 		.seq  = htonl(seq),
1118c97d4ff6SErik van der Kouwe 		.ack  = htonl(ack),
1119c97d4ff6SErik van der Kouwe 		.fl   = fl,
1120c97d4ff6SErik van der Kouwe 		.win  = htons(win),
1121c97d4ff6SErik van der Kouwe 		.cs   = 0,
1122c97d4ff6SErik van der Kouwe 		.uptr = htons(uptr),
1123c97d4ff6SErik van der Kouwe 	};
1124c97d4ff6SErik van der Kouwe 	char packet[65536];
1125c97d4ff6SErik van der Kouwe 	size_t packetsize;
1126c97d4ff6SErik van der Kouwe 
1127c97d4ff6SErik van der Kouwe 	dbgprintf("sending TCP packet srcport=%d dstport=%d fl=%s%s%s%s%s%s "
1128c97d4ff6SErik van der Kouwe 		"payloadsize=%zu\n", srcport, dstport,
1129c97d4ff6SErik van der Kouwe 		(fl & TCP_FLAG_URG) ? "  URG" : "",
1130c97d4ff6SErik van der Kouwe 		(fl & TCP_FLAG_ACK) ? "  ACK" : "",
1131c97d4ff6SErik van der Kouwe 		(fl & TCP_FLAG_PSH) ? "  PSH" : "",
1132c97d4ff6SErik van der Kouwe 		(fl & TCP_FLAG_RST) ? "  RST" : "",
1133c97d4ff6SErik van der Kouwe 		(fl & TCP_FLAG_SYN) ? "  SYN" : "",
1134c97d4ff6SErik van der Kouwe 		(fl & TCP_FLAG_FIN) ? "  FIN" : "",
1135c97d4ff6SErik van der Kouwe 		payloadsize);
1136c97d4ff6SErik van der Kouwe 
1137c97d4ff6SErik van der Kouwe 	optlen = 0;
1138c97d4ff6SErik van der Kouwe 	if (tcpsettings & st_opt_badpad) memset(header.opt, -1, sizeof(header.opt));
1139c97d4ff6SErik van der Kouwe 	if (tcpsettings & st_opt_nop) header.opt[optlen++] = 0x01;
1140c97d4ff6SErik van der Kouwe 	if (tcpsettings & st_opt_mss_small) {
1141c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x02;
1142c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x04;
1143c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x00;
1144c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x00;
1145c97d4ff6SErik van der Kouwe 	}
1146c97d4ff6SErik van der Kouwe 	if (tcpsettings & st_opt_mss_big) {
1147c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x02;
1148c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x04;
1149c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x10;
1150c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x00;
1151c97d4ff6SErik van der Kouwe 	}
1152c97d4ff6SErik van der Kouwe 	if (tcpsettings & st_opt_mss_huge) {
1153c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x02;
1154c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0x04;
1155c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0xff;
1156c97d4ff6SErik van der Kouwe 		header.opt[optlen++] = 0xff;
1157c97d4ff6SErik van der Kouwe 	}
1158c97d4ff6SErik van der Kouwe 	if (tcpsettings & st_opt_end) header.opt[optlen++] = 0x00;
1159c97d4ff6SErik van der Kouwe 
1160c97d4ff6SErik van der Kouwe 	doff = doff_fuzzed = (20 + optlen + 3) / 4;
1161c97d4ff6SErik van der Kouwe 	if (tcpsettings & su_bad_len_small) doff_fuzzed -= 1;
1162c97d4ff6SErik van der Kouwe 	if (tcpsettings & su_bad_len_big) doff_fuzzed += 1;
1163c97d4ff6SErik van der Kouwe 	if (tcpsettings & su_bad_len_huge) doff_fuzzed = 15;
1164c97d4ff6SErik van der Kouwe 	header.doff = doff_fuzzed << 4;
1165c97d4ff6SErik van der Kouwe 
1166c97d4ff6SErik van der Kouwe 	packetsize = doff * 4 + payloadsize;
1167c97d4ff6SErik van der Kouwe 	assert(packetsize <= sizeof(packet));
1168c97d4ff6SErik van der Kouwe 
1169c97d4ff6SErik van der Kouwe 	memcpy(packet, &header, sizeof(header));
1170c97d4ff6SErik van der Kouwe 	memcpy(packet + sizeof(header), payload, payloadsize);
1171c97d4ff6SErik van der Kouwe 	if (packetsize % 2) packet[packetsize] = 0;
1172c97d4ff6SErik van der Kouwe 
1173c97d4ff6SErik van der Kouwe 	header.cs = checksum_udp(srcip, dstip, prot, packet, packetsize);
1174c97d4ff6SErik van der Kouwe 	if (tcpsettings & su_zero_cs) header.cs = 0;
1175c97d4ff6SErik van der Kouwe 	if (tcpsettings & su_bad_cs) header.cs += 1;
1176c97d4ff6SErik van der Kouwe 
1177c97d4ff6SErik van der Kouwe 	memcpy(packet, &header, sizeof(header));
1178c97d4ff6SErik van der Kouwe 	send_packet_ip(
1179c97d4ff6SErik van der Kouwe 		fd,
1180c97d4ff6SErik van der Kouwe 		ipsettings,
1181c97d4ff6SErik van der Kouwe 		tos,
1182c97d4ff6SErik van der Kouwe 		id,
1183c97d4ff6SErik van der Kouwe 		ttl,
1184c97d4ff6SErik van der Kouwe 		prot,
1185c97d4ff6SErik van der Kouwe 		srcip,
1186c97d4ff6SErik van der Kouwe 		dstip,
1187c97d4ff6SErik van der Kouwe 		fragmode,
1188c97d4ff6SErik van der Kouwe 		packet,
1189c97d4ff6SErik van der Kouwe 		packetsize);
1190c97d4ff6SErik van der Kouwe }
1191c97d4ff6SErik van der Kouwe 
1192c97d4ff6SErik van der Kouwe struct send_packet_tcp_simple_params {
1193c97d4ff6SErik van der Kouwe 	int fd;
1194c97d4ff6SErik van der Kouwe 	enum settings_ip ipsettings;
1195c97d4ff6SErik van der Kouwe 	uint8_t tos;
1196c97d4ff6SErik van der Kouwe 	uint16_t *id;
1197c97d4ff6SErik van der Kouwe 	uint8_t ttl;
1198c97d4ff6SErik van der Kouwe 	uint8_t prot;
1199c97d4ff6SErik van der Kouwe 	uint32_t srcip;
1200c97d4ff6SErik van der Kouwe 	uint32_t dstip;
1201c97d4ff6SErik van der Kouwe 	enum fragmode_ip fragmode;
1202c97d4ff6SErik van der Kouwe 	enum settings_tcp tcpsettings;
1203c97d4ff6SErik van der Kouwe 	uint16_t srcport;
1204c97d4ff6SErik van der Kouwe 	uint16_t dstport;
1205c97d4ff6SErik van der Kouwe 	uint32_t seq;
1206c97d4ff6SErik van der Kouwe 	uint32_t ack;
1207c97d4ff6SErik van der Kouwe 	uint8_t fl;
1208c97d4ff6SErik van der Kouwe 	uint16_t win;
1209c97d4ff6SErik van der Kouwe 	uint16_t uptr;
1210c97d4ff6SErik van der Kouwe 	size_t payloadsize;
1211c97d4ff6SErik van der Kouwe };
1212c97d4ff6SErik van der Kouwe 
send_packet_tcp_simple(const struct send_packet_tcp_simple_params * params)1213c97d4ff6SErik van der Kouwe static void send_packet_tcp_simple(
1214c97d4ff6SErik van der Kouwe 	const struct send_packet_tcp_simple_params *params) {
1215c97d4ff6SErik van der Kouwe 	int i;
1216c97d4ff6SErik van der Kouwe 	char payload[65536];
1217c97d4ff6SErik van der Kouwe 
1218c97d4ff6SErik van der Kouwe 	if (!params->srcip || !params->dstip) return; /* crashes QEMU */
1219c97d4ff6SErik van der Kouwe 
1220c97d4ff6SErik van der Kouwe 	assert(params->payloadsize <= sizeof(payload));
1221c97d4ff6SErik van der Kouwe 	for (i = 0; i < params->payloadsize; i++) {
1222c97d4ff6SErik van der Kouwe 		payload[i] = *params->id + i;
1223c97d4ff6SErik van der Kouwe 	}
1224c97d4ff6SErik van der Kouwe 	send_packet_tcp(
1225c97d4ff6SErik van der Kouwe 		params->fd,
1226c97d4ff6SErik van der Kouwe 		params->ipsettings,
1227c97d4ff6SErik van der Kouwe 		params->tos,
1228c97d4ff6SErik van der Kouwe 		*params->id,
1229c97d4ff6SErik van der Kouwe 		params->ttl,
1230c97d4ff6SErik van der Kouwe 		params->prot,
1231c97d4ff6SErik van der Kouwe 		params->srcip,
1232c97d4ff6SErik van der Kouwe 		params->dstip,
1233c97d4ff6SErik van der Kouwe 		params->fragmode,
1234c97d4ff6SErik van der Kouwe 		params->tcpsettings,
1235c97d4ff6SErik van der Kouwe 		params->srcport,
1236c97d4ff6SErik van der Kouwe 		params->dstport,
1237c97d4ff6SErik van der Kouwe 		params->seq,
1238c97d4ff6SErik van der Kouwe 		params->ack,
1239c97d4ff6SErik van der Kouwe 		params->fl,
1240c97d4ff6SErik van der Kouwe 		params->win,
1241c97d4ff6SErik van der Kouwe 		params->uptr,
1242c97d4ff6SErik van der Kouwe 		payload,
1243c97d4ff6SErik van der Kouwe 		params->payloadsize);
1244c97d4ff6SErik van der Kouwe 	*params->id += 5471;
1245c97d4ff6SErik van der Kouwe }
1246c97d4ff6SErik van der Kouwe 
send_packets_tcp(int fd)1247c97d4ff6SErik van der Kouwe static void send_packets_tcp(int fd) {
1248c97d4ff6SErik van der Kouwe 	int i, j, k;
1249c97d4ff6SErik van der Kouwe 	uint16_t id = 0;
1250c97d4ff6SErik van der Kouwe 	const struct send_packet_tcp_simple_params paramsbase = {
1251c97d4ff6SErik van der Kouwe 		.fd          = fd,
1252c97d4ff6SErik van der Kouwe 		.ipsettings  = 0,
1253c97d4ff6SErik van der Kouwe 		.tos         = 0,
1254c97d4ff6SErik van der Kouwe 		.id          = &id,
1255c97d4ff6SErik van der Kouwe 		.ttl         = 10,
1256c97d4ff6SErik van der Kouwe 		.prot        = IP_PROT_TCP,
1257c97d4ff6SErik van der Kouwe 		.srcip       = addrsrc,
1258c97d4ff6SErik van der Kouwe 		.dstip       = addrdst,
1259c97d4ff6SErik van der Kouwe 		.fragmode    = fi_as_needed,
1260c97d4ff6SErik van der Kouwe 		.tcpsettings = 0,
1261c97d4ff6SErik van der Kouwe 		.srcport     = PORT_BASE + 0,
1262c97d4ff6SErik van der Kouwe 		.dstport     = PORT_BASE + 1,
1263c97d4ff6SErik van der Kouwe 		.seq         = 0x12345678,
1264c97d4ff6SErik van der Kouwe 		.ack         = 0x87654321,
1265c97d4ff6SErik van der Kouwe 		.fl          = TCP_FLAG_SYN,
1266c97d4ff6SErik van der Kouwe 		.win         = 4096,
1267c97d4ff6SErik van der Kouwe 		.uptr        = 0,
1268c97d4ff6SErik van der Kouwe 		.payloadsize = 1234,
1269c97d4ff6SErik van der Kouwe 	};
1270c97d4ff6SErik van der Kouwe 	uint16_t payloadsizes[] = {
1271c97d4ff6SErik van der Kouwe 		0,
1272c97d4ff6SErik van der Kouwe 		1,
1273c97d4ff6SErik van der Kouwe 		999,
1274c97d4ff6SErik van der Kouwe 		1500,
1275c97d4ff6SErik van der Kouwe 		1600,
1276c97d4ff6SErik van der Kouwe 		9999,
1277c97d4ff6SErik van der Kouwe 	};
1278c97d4ff6SErik van der Kouwe 	uint16_t ports[] = {
1279c97d4ff6SErik van der Kouwe 		0,
1280c97d4ff6SErik van der Kouwe 		PORT_BASE + 0,
1281c97d4ff6SErik van der Kouwe 		PORT_BASE + 1,
1282c97d4ff6SErik van der Kouwe 		PORT_BASE + 2,
1283c97d4ff6SErik van der Kouwe 		PORT_BASE + 3,
1284c97d4ff6SErik van der Kouwe 		32767,
1285c97d4ff6SErik van der Kouwe 		65535,
1286c97d4ff6SErik van der Kouwe 	};
1287c97d4ff6SErik van der Kouwe 	enum settings_tcp tcpsettings[] = {
1288c97d4ff6SErik van der Kouwe 		0,
1289c97d4ff6SErik van der Kouwe 		st_bad_doff_small,
1290c97d4ff6SErik van der Kouwe 		st_bad_doff_big,
1291c97d4ff6SErik van der Kouwe 		st_bad_doff_huge,
1292c97d4ff6SErik van der Kouwe 		st_bad_cs,
1293c97d4ff6SErik van der Kouwe 		st_zero_cs,
1294c97d4ff6SErik van der Kouwe 		st_opt_end,
1295c97d4ff6SErik van der Kouwe 		st_opt_nop,
1296c97d4ff6SErik van der Kouwe 		st_opt_mss_small,
1297c97d4ff6SErik van der Kouwe 		st_opt_mss_big,
1298c97d4ff6SErik van der Kouwe 		st_opt_mss_huge,
1299c97d4ff6SErik van der Kouwe 		st_opt_badpad,
1300c97d4ff6SErik van der Kouwe 	};
1301c97d4ff6SErik van der Kouwe 	struct send_packet_tcp_simple_params params;
1302c97d4ff6SErik van der Kouwe 
1303c97d4ff6SErik van der Kouwe 	/* send packets with various corruptions */
1304c97d4ff6SErik van der Kouwe 	params = paramsbase;
1305c97d4ff6SErik van der Kouwe 	for (i = 0; i < 12; i++) {
1306c97d4ff6SErik van der Kouwe 		params.tcpsettings = tcpsettings[i];
1307c97d4ff6SErik van der Kouwe 		send_packet_tcp_simple(&params);
1308c97d4ff6SErik van der Kouwe 	}
1309c97d4ff6SErik van der Kouwe 
1310c97d4ff6SErik van der Kouwe 	/* send packets with various addresses and ports */
1311c97d4ff6SErik van der Kouwe 	params = paramsbase;
1312*ad920fc4SDavid van Moolenbroek 	for (i = 0; i < __arraycount(addrs); i++) {
1313*ad920fc4SDavid van Moolenbroek 	for (j = 0; j < __arraycount(addrs); j++) {
1314c97d4ff6SErik van der Kouwe 	for (k = 0; k < 7; k++) {
1315c97d4ff6SErik van der Kouwe 		params.srcip = addrs[i];
1316c97d4ff6SErik van der Kouwe 		params.dstip = addrs[j];
1317c97d4ff6SErik van der Kouwe 		params.dstport = ports[k];
1318c97d4ff6SErik van der Kouwe 		send_packet_tcp_simple(&params);
1319c97d4ff6SErik van der Kouwe 	}
1320c97d4ff6SErik van der Kouwe 	}
1321c97d4ff6SErik van der Kouwe 	}
1322c97d4ff6SErik van der Kouwe 	params = paramsbase;
1323*ad920fc4SDavid van Moolenbroek 	for (i = 0; i < __arraycount(addrs); i++) {
1324c97d4ff6SErik van der Kouwe 	for (j = 0; j < 7; j++) {
1325c97d4ff6SErik van der Kouwe 	for (k = 0; k < 7; k++) {
1326c97d4ff6SErik van der Kouwe 		params.dstip = addrs[i];
1327c97d4ff6SErik van der Kouwe 		params.srcport = ports[j];
1328c97d4ff6SErik van der Kouwe 		params.dstport = ports[k];
1329c97d4ff6SErik van der Kouwe 		send_packet_tcp_simple(&params);
1330c97d4ff6SErik van der Kouwe 	}
1331c97d4ff6SErik van der Kouwe 	}
1332c97d4ff6SErik van der Kouwe 	}
1333c97d4ff6SErik van der Kouwe 
1334c97d4ff6SErik van der Kouwe 	/* send packets with different sequence numbers */
1335c97d4ff6SErik van der Kouwe 	params = paramsbase;
1336c97d4ff6SErik van der Kouwe 	for (i = 0; i < 16; i++) {
1337c97d4ff6SErik van der Kouwe 		params.seq = 0x1fffffff;
1338c97d4ff6SErik van der Kouwe 		send_packet_tcp_simple(&params);
1339c97d4ff6SErik van der Kouwe 	}
1340c97d4ff6SErik van der Kouwe 
1341c97d4ff6SErik van der Kouwe 	/* send packets with all combinations of flags */
1342c97d4ff6SErik van der Kouwe 	params = paramsbase;
1343c97d4ff6SErik van der Kouwe 	for (i = 0; i < 256; i++) {
1344c97d4ff6SErik van der Kouwe 		params.fl = i;
1345c97d4ff6SErik van der Kouwe 		send_packet_tcp_simple(&params);
1346c97d4ff6SErik van der Kouwe 	}
1347c97d4ff6SErik van der Kouwe 
1348c97d4ff6SErik van der Kouwe 	/* send packets with different window sizes */
1349c97d4ff6SErik van der Kouwe 	params = paramsbase;
1350c97d4ff6SErik van der Kouwe 	for (i = 0; i < 6; i++) {
1351c97d4ff6SErik van der Kouwe 		params.win = payloadsizes[i];
1352c97d4ff6SErik van der Kouwe 		send_packet_tcp_simple(&params);
1353c97d4ff6SErik van der Kouwe 	}
1354c97d4ff6SErik van der Kouwe 
1355c97d4ff6SErik van der Kouwe 	/* send packets with different payload sizes */
1356c97d4ff6SErik van der Kouwe 	params = paramsbase;
1357c97d4ff6SErik van der Kouwe 	for (i = 0; i < 6; i++) {
1358c97d4ff6SErik van der Kouwe 		params.payloadsize = payloadsizes[i];
1359c97d4ff6SErik van der Kouwe 		send_packet_tcp_simple(&params);
1360c97d4ff6SErik van der Kouwe 	}
1361c97d4ff6SErik van der Kouwe }
1362c97d4ff6SErik van der Kouwe 
recv_packets_nb(int fd)1363c97d4ff6SErik van der Kouwe static void recv_packets_nb(int fd) {
1364c97d4ff6SErik van der Kouwe 	char buf[4096];
1365c97d4ff6SErik van der Kouwe 	int flags;
1366c97d4ff6SErik van der Kouwe 	ssize_t r;
1367c97d4ff6SErik van der Kouwe 
1368c97d4ff6SErik van der Kouwe 	flags = fcntl(fd, F_GETFL);
1369c97d4ff6SErik van der Kouwe 	if (flags < 0) {
1370c97d4ff6SErik van der Kouwe 		efmt("fcntl(F_GETFL) failed");
1371c97d4ff6SErik van der Kouwe 		return;
1372c97d4ff6SErik van der Kouwe 	}
1373c97d4ff6SErik van der Kouwe 
1374c97d4ff6SErik van der Kouwe 	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
1375c97d4ff6SErik van der Kouwe 		efmt("fcntl(F_SETFL) failed");
1376c97d4ff6SErik van der Kouwe 		return;
1377c97d4ff6SErik van der Kouwe 	}
1378c97d4ff6SErik van der Kouwe 
1379c97d4ff6SErik van der Kouwe 	for (;;) {
1380c97d4ff6SErik van der Kouwe 		errno = 0;
1381c97d4ff6SErik van der Kouwe 		r = read(fd, buf, sizeof(buf));
1382c97d4ff6SErik van der Kouwe 		if (r <= 0) {
1383c97d4ff6SErik van der Kouwe 			if (errno != EAGAIN) efmt("nb read failed");
1384c97d4ff6SErik van der Kouwe 			dbgprintf("no more packets to receive\n");
1385c97d4ff6SErik van der Kouwe 			break;
1386c97d4ff6SErik van der Kouwe 		}
1387c97d4ff6SErik van der Kouwe 		dbgprintf("received packet of size %zd\n", r);
1388c97d4ff6SErik van der Kouwe 	}
1389c97d4ff6SErik van der Kouwe 
1390c97d4ff6SErik van der Kouwe 	if (fcntl(fd, F_SETFL, flags) == -1) {
1391c97d4ff6SErik van der Kouwe 		efmt("fcntl(F_SETFL) failed");
1392c97d4ff6SErik van der Kouwe 		return;
1393c97d4ff6SErik van der Kouwe 	}
1394c97d4ff6SErik van der Kouwe }
1395c97d4ff6SErik van der Kouwe 
gettimeofday_checked(void)1396c97d4ff6SErik van der Kouwe static struct timeval gettimeofday_checked(void) {
1397c97d4ff6SErik van der Kouwe 	struct timeval time = {};
1398c97d4ff6SErik van der Kouwe 
1399c97d4ff6SErik van der Kouwe 	if (gettimeofday(&time, NULL) != 0) {
1400c97d4ff6SErik van der Kouwe 		efmt("gettimeofday failed");
1401c97d4ff6SErik van der Kouwe 	}
1402c97d4ff6SErik van der Kouwe 	return time;
1403c97d4ff6SErik van der Kouwe }
1404c97d4ff6SErik van der Kouwe 
timeval_cmp(const struct timeval * x,const struct timeval * y)1405c97d4ff6SErik van der Kouwe static int timeval_cmp(const struct timeval *x, const struct timeval *y) {
1406c97d4ff6SErik van der Kouwe 	if (x->tv_sec < y->tv_sec) return -1;
1407c97d4ff6SErik van der Kouwe 	if (x->tv_sec > y->tv_sec) return 1;
1408c97d4ff6SErik van der Kouwe 	if (x->tv_usec < y->tv_usec) return -1;
1409c97d4ff6SErik van der Kouwe 	if (x->tv_usec > y->tv_usec) return 1;
1410c97d4ff6SErik van der Kouwe 	return 0;
1411c97d4ff6SErik van der Kouwe }
1412c97d4ff6SErik van der Kouwe 
timeval_sub(struct timeval x,struct timeval y)1413c97d4ff6SErik van der Kouwe static struct timeval timeval_sub(struct timeval x, struct timeval y) {
1414c97d4ff6SErik van der Kouwe 	struct timeval z;
1415c97d4ff6SErik van der Kouwe 
1416c97d4ff6SErik van der Kouwe 	/* no negative result allowed */
1417c97d4ff6SErik van der Kouwe 	if (timeval_cmp(&x, &y) < 0) {
1418c97d4ff6SErik van der Kouwe 		memset(&z, 0, sizeof(z));
1419c97d4ff6SErik van der Kouwe 	} else {
1420c97d4ff6SErik van der Kouwe 		/* no negative tv_usec allowed */
1421c97d4ff6SErik van der Kouwe 		if (x.tv_usec < y.tv_usec) {
1422c97d4ff6SErik van der Kouwe 			x.tv_sec -= 1;
1423c97d4ff6SErik van der Kouwe 			x.tv_usec += 1000000;
1424c97d4ff6SErik van der Kouwe 		}
1425c97d4ff6SErik van der Kouwe 
1426c97d4ff6SErik van der Kouwe 		/* perform subtraction */
1427c97d4ff6SErik van der Kouwe 		z.tv_sec = x.tv_sec - y.tv_sec;
1428c97d4ff6SErik van der Kouwe 		z.tv_usec = x.tv_usec - y.tv_usec;
1429c97d4ff6SErik van der Kouwe 	}
1430c97d4ff6SErik van der Kouwe 	return z;
1431c97d4ff6SErik van der Kouwe }
1432c97d4ff6SErik van der Kouwe 
recv_packet_select(int fd,void * buf,size_t size,const struct timeval * deadline)1433c97d4ff6SErik van der Kouwe static size_t recv_packet_select(
1434c97d4ff6SErik van der Kouwe 	int fd,
1435c97d4ff6SErik van der Kouwe 	void *buf,
1436c97d4ff6SErik van der Kouwe 	size_t size,
1437c97d4ff6SErik van der Kouwe 	const struct timeval *deadline) {
1438c97d4ff6SErik van der Kouwe 	int nfds;
1439c97d4ff6SErik van der Kouwe 	ssize_t r;
1440c97d4ff6SErik van der Kouwe 	fd_set readfds;
1441c97d4ff6SErik van der Kouwe 	struct timeval timeout = timeval_sub(*deadline, gettimeofday_checked());
1442c97d4ff6SErik van der Kouwe 
1443c97d4ff6SErik van der Kouwe 	FD_ZERO(&readfds);
1444c97d4ff6SErik van der Kouwe 	FD_SET(fd, &readfds);
1445c97d4ff6SErik van der Kouwe 	errno = 0;
1446c97d4ff6SErik van der Kouwe 	nfds = select(fd + 1, &readfds, NULL, NULL, &timeout);
1447c97d4ff6SErik van der Kouwe 	if (nfds < 0 || nfds > 1) {
1448c97d4ff6SErik van der Kouwe 		efmt("select failed");
1449c97d4ff6SErik van der Kouwe 		return 0;
1450c97d4ff6SErik van der Kouwe 	}
1451c97d4ff6SErik van der Kouwe 
1452c97d4ff6SErik van der Kouwe 	if (nfds == 0) {
1453c97d4ff6SErik van der Kouwe 		if (FD_ISSET(fd, &readfds)) efmt("select spuriously set fd");
1454c97d4ff6SErik van der Kouwe 		dbgprintf("no more packets to receive\n");
1455c97d4ff6SErik van der Kouwe 		return 0;
1456c97d4ff6SErik van der Kouwe 	}
1457c97d4ff6SErik van der Kouwe 
1458c97d4ff6SErik van der Kouwe 	if (!FD_ISSET(fd, &readfds)) {
1459c97d4ff6SErik van der Kouwe 		efmt("select did not set fd");
1460c97d4ff6SErik van der Kouwe 		return 0;
1461c97d4ff6SErik van der Kouwe 	}
1462c97d4ff6SErik van der Kouwe 
1463c97d4ff6SErik van der Kouwe 	r = read(fd, buf, size);
1464c97d4ff6SErik van der Kouwe 	if (r <= 0) {
1465c97d4ff6SErik van der Kouwe 		efmt("read failed");
1466c97d4ff6SErik van der Kouwe 		return 0;
1467c97d4ff6SErik van der Kouwe 	}
1468c97d4ff6SErik van der Kouwe 	dbgprintf("received packet of size %zd\n", r);
1469c97d4ff6SErik van der Kouwe 
1470c97d4ff6SErik van der Kouwe 	return r;
1471c97d4ff6SErik van der Kouwe }
1472c97d4ff6SErik van der Kouwe 
recv_packets_select(int fd)1473c97d4ff6SErik van der Kouwe static void recv_packets_select(int fd) {
1474c97d4ff6SErik van der Kouwe 	char buf[4096];
1475c97d4ff6SErik van der Kouwe 	struct timeval deadline = gettimeofday_checked();
1476c97d4ff6SErik van der Kouwe 
1477c97d4ff6SErik van der Kouwe 	deadline.tv_sec++;
1478c97d4ff6SErik van der Kouwe 	while (recv_packet_select(fd, buf, sizeof(buf), &deadline)) { }
1479c97d4ff6SErik van der Kouwe }
1480c97d4ff6SErik van der Kouwe 
open_raw_socket(int broadcast)1481c97d4ff6SErik van der Kouwe static int open_raw_socket(int broadcast) {
1482*ad920fc4SDavid van Moolenbroek 	int fd, on;
1483c97d4ff6SErik van der Kouwe 
1484*ad920fc4SDavid van Moolenbroek 	fd = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
1485*ad920fc4SDavid van Moolenbroek 	if (fd < 0) efmt("cannot create raw socket");
1486c97d4ff6SErik van der Kouwe 
1487*ad920fc4SDavid van Moolenbroek 	on = 1;
1488*ad920fc4SDavid van Moolenbroek 	if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) != 0)
1489*ad920fc4SDavid van Moolenbroek 		efmt("ioctl(IP_HDRINCL) failed");
1490c97d4ff6SErik van der Kouwe 
1491c97d4ff6SErik van der Kouwe 	return fd;
1492c97d4ff6SErik van der Kouwe }
1493c97d4ff6SErik van der Kouwe 
do_packets(void)1494c97d4ff6SErik van der Kouwe static void do_packets(void) {
1495c97d4ff6SErik van der Kouwe 	int fd;
1496c97d4ff6SErik van der Kouwe 
1497c97d4ff6SErik van der Kouwe 	/* test IP and UDP with broadcast */
1498c97d4ff6SErik van der Kouwe 	fd = open_raw_socket(1 /*broadcast*/);
1499c97d4ff6SErik van der Kouwe 	if (fd < 0) return;
1500c97d4ff6SErik van der Kouwe 
1501c97d4ff6SErik van der Kouwe 	send_packets_ip(fd);
1502c97d4ff6SErik van der Kouwe 	send_packets_udp(fd);
1503c97d4ff6SErik van der Kouwe 	recv_packets_nb(fd);
1504c97d4ff6SErik van der Kouwe 
1505c97d4ff6SErik van der Kouwe 	CLOSE(fd);
1506c97d4ff6SErik van der Kouwe 
1507c97d4ff6SErik van der Kouwe 	/* test TCP locally to avoid crashing QEMU */
1508c97d4ff6SErik van der Kouwe 	fd = open_raw_socket(0 /*broadcast*/);
1509c97d4ff6SErik van der Kouwe 	if (fd < 0) return;
1510c97d4ff6SErik van der Kouwe 
1511c97d4ff6SErik van der Kouwe 	send_packets_tcp(fd);
1512c97d4ff6SErik van der Kouwe 	recv_packets_select(fd);
1513c97d4ff6SErik van der Kouwe 
1514c97d4ff6SErik van der Kouwe 	CLOSE(fd);
1515c97d4ff6SErik van der Kouwe }
1516c97d4ff6SErik van der Kouwe 
main(int argc,char ** argv)1517c97d4ff6SErik van der Kouwe int main(int argc, char **argv)
1518c97d4ff6SErik van der Kouwe {
1519c97d4ff6SErik van der Kouwe 	int i;
1520c97d4ff6SErik van der Kouwe 	pid_t pids[PORT_COUNT];
1521c97d4ff6SErik van der Kouwe 
1522c97d4ff6SErik van der Kouwe 	start(83);
1523c97d4ff6SErik van der Kouwe 
1524c97d4ff6SErik van der Kouwe 	/* start servers so we have someone to talk to */
1525c97d4ff6SErik van der Kouwe 	pids[0] = server_start(SOCK_STREAM, PORT_BASE + 0, sa_close);
1526c97d4ff6SErik van der Kouwe 	pids[1] = server_start(SOCK_STREAM, PORT_BASE + 1, sa_read);
1527c97d4ff6SErik van der Kouwe 	pids[2] = server_start(SOCK_STREAM, PORT_BASE + 2, sa_selectrw);
1528c97d4ff6SErik van der Kouwe 	pids[3] = server_start(SOCK_STREAM, PORT_BASE + 3, sa_write);
1529c97d4ff6SErik van der Kouwe 	pids[4] = server_start(SOCK_DGRAM,  PORT_BASE + 0, sa_read);
1530c97d4ff6SErik van der Kouwe 	pids[5] = server_start(SOCK_DGRAM,  PORT_BASE + 1, sa_selectr);
1531c97d4ff6SErik van der Kouwe 
1532c97d4ff6SErik van der Kouwe 	/* send some bogus packets */
1533*ad920fc4SDavid van Moolenbroek 	do_packets();
1534c97d4ff6SErik van der Kouwe 
1535c97d4ff6SErik van der Kouwe 	/* stop the servers */
1536c97d4ff6SErik van der Kouwe 	for (i = 0; i < PORT_COUNT; i++) server_stop(pids[i]);
1537c97d4ff6SErik van der Kouwe 	for (i = 0; i < PORT_COUNT; i++) server_wait(pids[i]);
1538c97d4ff6SErik van der Kouwe 
1539c97d4ff6SErik van der Kouwe 	quit();
1540c97d4ff6SErik van der Kouwe 	return 0;
1541c97d4ff6SErik van der Kouwe }
1542