1 /*
2  * fragtest.c
3  *
4  * Copyright (c) 2001 Dug Song <dugsong@monkey.org>
5  *
6  * $Id: fragtest.c,v 1.17 2002/04/07 22:55:20 dugsong Exp $
7  */
8 
9 #include "config.h"
10 
11 #include <pcap.h>
12 
13 #include <err.h>
14 #include <errno.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 
20 #include "mod.h"
21 #include "pcaputil.h"
22 
23 #define TEST_PING		0x01
24 #define TEST_IP_OPT		0x02
25 #define TEST_IP_TRACERT		0x04
26 #define TEST_FRAG		0x08
27 #define TEST_FRAG_NEW		0x10
28 #define TEST_FRAG_OLD		0x20
29 #define TEST_FRAG_TIMEOUT	0x40
30 
31 #define READ_TIMEOUT		2
32 #define FRAG_TIMEOUT		300
33 
34 struct ft_ctx {
35 	struct addr		 src, dst;
36 	struct pktq		 pktq;
37 	ip_t			*ip;
38 	pcap_t			*pcap;
39 	rand_t			*rnd;
40 	int			 dloff;
41 };
42 
43 extern struct mod mod_ip_frag;
44 
45 static struct ft_ctx	 ctx;
46 static struct pkt	*ping;
47 static struct timeval	 read_tv = { READ_TIMEOUT, 0 };
48 
49 static void
usage(void)50 usage(void)
51 {
52 	fprintf(stderr, "Usage: fragtest TESTS ... <host>\n\n");
53 	fprintf(stderr, "  where TESTS is any combination of the following "
54 	    "(or \"all\"):\n\n");
55 	fprintf(stderr, "  ping\t\tprerequisite for all tests\n");
56 	fprintf(stderr, "  ip-opt\tdetermine supported IP options\n");
57 	fprintf(stderr, "  ip-tracert\tdetermine path to target\n");
58 	fprintf(stderr, "  frag\t\ttry 8-byte IP fragments\n");
59 	fprintf(stderr, "  frag-new\ttry 8-byte fwd-overlapping IP "
60 	    "fragments, favoring new data\n");
61 	fprintf(stderr, "  frag-old\ttry 8-byte fwd-overlapping IP "
62 	    "fragments, favoring old data\n");
63 	fprintf(stderr, "  frag-timeout\tdetermine IP fragment "
64 	    "reassembly timeout\n");
65 	fprintf(stderr, "\n");
66 	exit(1);
67 }
68 
69 static char *
timeval_ntoa(struct timeval * tv)70 timeval_ntoa(struct timeval *tv)
71 {
72 	static char buf[128];
73 	uint64_t usec;
74 
75 	usec = (tv->tv_sec * 1000000) + tv->tv_usec;
76 
77 	if (usec > 1000000) {
78 		snprintf(buf, sizeof(buf), "%d.%03d sec",
79 		    (int)(usec / 1000000), (int)(usec % 1000000));
80 	} else {
81 		snprintf(buf, sizeof(buf), "%d.%03d ms",
82 		    (int)(usec / 1000), (int)(usec % 1000));
83 	}
84 	return (buf);
85 }
86 
87 static int
send_pkt(struct pkt * pkt)88 send_pkt(struct pkt *pkt)
89 {
90 	int i;
91 
92 	i = ip_send(ctx.ip, pkt->pkt_ip, pkt->pkt_end - pkt->pkt_eth_data);
93 	pkt_free(pkt);
94 	return (i);
95 }
96 
97 static void
send_pktq(struct pktq * pktq)98 send_pktq(struct pktq *pktq)
99 {
100 	struct pkt *pkt, *next;
101 
102 	for (pkt = TAILQ_FIRST(pktq); pkt != TAILQ_END(pktq); pkt = next) {
103 		next = TAILQ_NEXT(pkt, pkt_next);
104 		TAILQ_REMOVE(pktq, pkt, pkt_next);
105 		send_pkt(pkt);
106 	}
107 }
108 
109 static struct pkt *
recv_pkt(struct timeval * tv)110 recv_pkt(struct timeval *tv)
111 {
112 	struct pcap_pkthdr phdr;
113 	struct timeval now, start;
114 	struct pkt *pkt;
115 	u_char *p;
116 	long timeout_usec;
117 	int i;
118 
119 	timeout_usec = tv->tv_sec * 1000000 + tv->tv_usec;
120 	gettimeofday(&start, NULL);
121 
122 	/*
123 	 * XXX - can't select() on pcap_fileno on Solaris,
124 	 * seems to be unreliable on Linux as well. *sigh*
125 	 */
126 	for (;;) {
127 		gettimeofday(&now, NULL);
128 
129 		if ((p = (u_char *)pcap_next(ctx.pcap, &phdr)) != NULL ||
130 		    (now.tv_sec - start.tv_sec) * 1000000 +
131 		    now.tv_usec - start.tv_usec > timeout_usec)
132 			break;
133 	}
134 	if (p == NULL)
135 		return (NULL);
136 
137 	p += ctx.dloff;
138 	i = phdr.caplen - ctx.dloff;
139 
140 	pkt = pkt_new();
141 	memcpy(pkt->pkt_eth_data, p, i);
142 	pkt->pkt_end = pkt->pkt_eth_data + i;
143 	pkt_decorate(pkt);
144 
145 	tv->tv_sec = phdr.ts.tv_sec - start.tv_sec;
146 	tv->tv_usec = phdr.ts.tv_usec - start.tv_usec;
147 
148 	return (pkt);
149 }
150 
151 static int
test_ping(void)152 test_ping(void)
153 {
154 	struct pkt *pkt;
155 	struct timeval tv;
156 
157 	printf("ping: "); fflush(stdout);
158 
159 	ping->pkt_icmp_msg->echo.icmp_id = rand_uint16(ctx.rnd);
160 	pkt = pkt_dup(ping);
161 	pkt->pkt_ip->ip_id = rand_uint16(ctx.rnd);
162 	ip_checksum(pkt->pkt_ip, pkt->pkt_end - pkt->pkt_eth_data);
163 
164 	pcap_filter(ctx.pcap, "icmp[0] = 0 and src %s and dst %s",
165 	    addr_ntoa(&ctx.dst), addr_ntoa(&ctx.src));
166 
167 	send_pkt(pkt);
168 
169 	for (tv = read_tv; (pkt = recv_pkt(&tv)) != NULL; tv = read_tv) {
170 		if (memcmp(&pkt->pkt_icmp_msg->echo,
171 		    &ping->pkt_icmp_msg->echo, 8) == 0)
172 			break;
173 	}
174 	printf("%s\n", pkt ? timeval_ntoa(&tv) : "no reply");
175 
176 	return (0);
177 }
178 
179 static char *optnames[] = {
180 	"eol", "nop", "sec", "lsrr", "ts", "esec", "cipso", "rr",
181 	"satid", "ssrr", "zsu", "mtup", "mtur", "finn", "visa",
182 	"encode", "imitd", "eip", "tr", "addext", "rtralt", "sdb",
183 	"nsapa", "dps", "ump", NULL
184 };
185 
186 static int
test_ip_opt(void)187 test_ip_opt(void)
188 {
189 	struct pkt *pkt;
190 	struct timeval tv;
191 	struct ip_opt opts[IP_OPT_MAX];
192 	int i, len, max;
193 
194 	printf("ip-opt: "); fflush(stdout);
195 
196 	memset(&opts, 0, sizeof(opts));
197 	max = 0;
198 	opts[max].opt_type = IP_OPT_SEC;
199 	opts[max].opt_len = IP_OPT_LEN + 9;
200 	max++;
201 	opts[max].opt_type = IP_OPT_LSRR;
202 	opts[max].opt_len = IP_OPT_LEN + 1 + 4;
203 	opts[max].opt_data.rr.ptr = 8;
204 	opts[max].opt_data.rr.iplist[0] = ping->pkt_ip->ip_src;
205 	max++;
206 	opts[max].opt_type = IP_OPT_TS;
207 	opts[max].opt_len = IP_OPT_LEN + 1 + 1 + 4;
208 	opts[max].opt_data.ts.ptr = 5;
209 	opts[max].opt_data.ts.flg = IP_OPT_TS_TSONLY;
210 	max++;
211 	opts[max].opt_type = IP_OPT_ESEC;
212 	opts[max].opt_len = IP_OPT_LEN;
213 	max++;
214 	opts[max].opt_type = IP_OPT_CIPSO;
215 	opts[max].opt_len = IP_OPT_LEN;
216 	max++;
217 	opts[max].opt_type = IP_OPT_RR;
218 	opts[max].opt_len = IP_OPT_LEN + 1 + 4;
219 	opts[max].opt_data.rr.ptr = 4;
220 	max++;
221 	opts[max].opt_type = IP_OPT_SATID;
222 	opts[max].opt_len = IP_OPT_LEN + 2;
223 	max++;
224 	opts[max].opt_type = IP_OPT_SSRR;
225 	opts[max].opt_len = IP_OPT_LEN + 1 + 4;
226 	opts[max].opt_data.rr.ptr = 8;
227 	opts[max].opt_data.rr.iplist[0] = ping->pkt_ip->ip_src;
228 	max++;
229 
230 	pcap_filter(ctx.pcap, "icmp and src %s and dst %s",
231 	    addr_ntoa(&ctx.dst), addr_ntoa(&ctx.src));
232 
233 	ping->pkt_icmp_msg->echo.icmp_id = rand_uint16(ctx.rnd);
234 
235 	for (i = 0; i < max; i++) {
236 		pkt = pkt_dup(ping);
237 		pkt->pkt_ip->ip_id = rand_uint16(ctx.rnd);
238 		pkt->pkt_icmp_msg->echo.icmp_seq = opts[i].opt_type;
239 		len = ip_add_option(pkt->pkt_ip, PKT_BUF_LEN - ETH_HDR_LEN +
240 		    IP_HDR_LEN, IP_PROTO_IP, &opts[i], opts[i].opt_len);
241 		pkt->pkt_end += len;
242 
243 		ip_checksum(pkt->pkt_ip, pkt->pkt_end - pkt->pkt_eth_data);
244 
245 		send_pkt(pkt);
246 	}
247 	i = 0;
248 	for (tv = read_tv; (pkt = recv_pkt(&tv)) != NULL; tv = read_tv) {
249 		if (pkt->pkt_icmp->icmp_type == ICMP_ECHOREPLY &&
250 		    pkt->pkt_icmp_msg->echo.icmp_id ==
251 		    ping->pkt_icmp_msg->echo.icmp_id) {
252 			i = IP_OPT_NUMBER(pkt->pkt_icmp_msg->echo.icmp_seq);
253 			printf("%s ", optnames[i]);
254 		}
255 	}
256 	printf("%s\n", i ? "" : "none");
257 
258 	return (0);
259 }
260 
261 struct hop {
262 	struct addr	addr;
263 	struct icmp_hdr	icmp;
264 	int		rtt;
265 	int		ttl;
266 };
267 
268 static int
test_ip_tracert(void)269 test_ip_tracert(void)
270 {
271 	struct timeval tv;
272 	struct hop hops[IP_TTL_DEFAULT];
273 	struct pkt *pkt;
274 	struct icmp_msg_echo *echo;
275 	int i, hopcnt, max_ttl;
276 
277 	printf("ip-tracert: "); fflush(stdout);
278 
279 	pcap_filter(ctx.pcap, "icmp[0] = 0 and src %s and dst %s",
280 	    addr_ntoa(&ctx.dst), addr_ntoa(&ctx.src));
281 
282 	ping->pkt_icmp_msg->echo.icmp_id = rand_uint16(ctx.rnd);
283 	pkt = pkt_dup(ping);
284 	pkt->pkt_ip->ip_id = rand_uint16(ctx.rnd);
285 	ip_checksum(pkt->pkt_ip, pkt->pkt_end - pkt->pkt_eth_data);
286 
287 	send_pkt(pkt);
288 	tv = read_tv;
289 
290 	if ((pkt = recv_pkt(&tv)) == NULL) {
291 		printf("no reply\n");
292 		return (0);
293 	}
294 	/* XXX - guess remote stack's starting TTL */
295 	for (i = 2; pkt->pkt_ip->ip_ttl > i; i <<= 1)
296 		;
297 
298 	if ((max_ttl = i - pkt->pkt_ip->ip_ttl + 1) > IP_TTL_DEFAULT)
299 		max_ttl = IP_TTL_DEFAULT;
300 
301 	printf("%s, %d hops max\n", ip_ntoa(&ping->pkt_ip->ip_dst), max_ttl);
302 	pcap_filter(ctx.pcap, "icmp and dst %s", addr_ntoa(&ctx.src));
303 
304 	for (i = 1; i < max_ttl + 1; i++) {
305 		pkt = pkt_dup(ping);
306 		pkt->pkt_ip->ip_id = rand_uint16(ctx.rnd);
307 		pkt->pkt_ip->ip_ttl = i;
308 		pkt->pkt_icmp_msg->echo.icmp_seq = htons(i);
309 		ip_checksum(pkt->pkt_ip, pkt->pkt_end - pkt->pkt_eth_data);
310 		send_pkt(pkt);
311 		usleep(42);	/* XXX */
312 	}
313 	memset(&hops, 0, sizeof(hops));
314 	hopcnt = 0;
315 
316 	for (tv = read_tv; (pkt = recv_pkt(&tv)) != NULL; tv = read_tv) {
317 		if ((pkt->pkt_icmp->icmp_type == ICMP_TIMEXCEED ||
318 		    pkt->pkt_icmp->icmp_type == ICMP_UNREACH) &&
319 		    pkt->pkt_end - pkt->pkt_eth_data >=
320 		    (IP_HDR_LEN + ICMP_LEN_MIN) * 2) {
321 			echo = (struct icmp_msg_echo *)
322 			    (pkt->pkt_icmp_msg->timexceed.icmp_ip +
323 				IP_HDR_LEN + ICMP_HDR_LEN);
324 		} else if (pkt->pkt_icmp->icmp_type == ICMP_ECHOREPLY) {
325 			echo = &pkt->pkt_icmp_msg->echo;
326 		} else
327 			continue;
328 
329 		if (echo->icmp_id != ping->pkt_icmp_msg->echo.icmp_id)
330 			continue;
331 
332 		i = ntohs(echo->icmp_seq);
333 		addr_pack(&hops[i].addr, ADDR_TYPE_IP, IP_ADDR_BITS,
334 		    &pkt->pkt_ip->ip_src, IP_ADDR_LEN);
335 		memcpy(&hops[i].icmp, pkt->pkt_icmp, ICMP_HDR_LEN);
336 		hops[i].ttl = pkt->pkt_ip->ip_ttl;
337 		hopcnt++;
338 
339 		if (pkt->pkt_ip->ip_src == ping->pkt_ip->ip_dst)
340 			break;
341 	}
342 	for (i = 1; i < hopcnt + 1; i++) {
343 		if (hops[i].addr.addr_type == ADDR_TYPE_IP) {
344 			printf("%2d  %s (%d)\n",
345 			    i, addr_ntoa(&hops[i].addr), hops[i].ttl);
346 		} else
347 			printf("%2d  *\n", i);
348 	}
349 	return (0);
350 }
351 
352 static int
test_frag(char * overlap,int drop)353 test_frag(char *overlap, int drop)
354 {
355 	struct timeval tv, save_tv = read_tv;
356 	struct pkt *pkt;
357 	struct icmp_msg_echo *echo;
358 	char *frag_argv[4];
359 
360 	if (overlap != NULL)
361 		printf("frag-%s: ", overlap);
362 	else if (drop)
363 		printf("frag-timeout (please wait): ");
364 	else
365 		printf("frag: ");
366 	fflush(stdout);
367 
368 	ping->pkt_ip->ip_id = rand_uint16(ctx.rnd);
369 	ping->pkt_icmp_msg->echo.icmp_id = rand_uint16(ctx.rnd);
370 	pkt = pkt_dup(ping);
371 	ip_checksum(pkt->pkt_ip, pkt->pkt_end - pkt->pkt_eth_data);
372 	TAILQ_INSERT_TAIL(&ctx.pktq, pkt, pkt_next);
373 
374 	frag_argv[0] = "ip_frag";
375 	frag_argv[1] = "8";
376 	frag_argv[2] = overlap;
377 	frag_argv[3] = NULL;
378 
379 	mod_ip_frag.open(overlap ? 3 : 2, frag_argv);
380 	mod_ip_frag.apply(NULL, &ctx.pktq);
381 
382 	if (drop) {
383 		pkt = TAILQ_LAST(&ctx.pktq, pktq);
384 		TAILQ_REMOVE(&ctx.pktq, pkt, pkt_next);
385 		pkt_free(pkt);
386 		save_tv.tv_sec = FRAG_TIMEOUT;
387 	}
388 	pcap_filter(ctx.pcap, "icmp[0] = %d and src %s and dst %s",
389 	    drop ? 11 : 0, addr_ntoa(&ctx.dst), addr_ntoa(&ctx.src));
390 
391 	send_pktq(&ctx.pktq);
392 
393 	for (tv = save_tv; (pkt = recv_pkt(&tv)) != NULL; tv = save_tv) {
394 		if (drop) {
395 			echo = (struct icmp_msg_echo *)
396 			    (pkt->pkt_icmp_msg->timexceed.icmp_ip +
397 				IP_HDR_LEN + ICMP_HDR_LEN);
398 		} else {
399 			echo = &pkt->pkt_icmp_msg->echo;
400 		}
401 		if (echo->icmp_id == ping->pkt_icmp_msg->echo.icmp_id)
402 			break;
403 	}
404 	printf("%s\n", pkt ? timeval_ntoa(&tv) : "no reply");
405 
406 	return (0);
407 }
408 
409 int
main(int argc,char * argv[])410 main(int argc, char *argv[])
411 {
412 	struct intf_entry ifent;
413 	intf_t *intf;
414 	int i, tests;
415 	char *cmd;
416 
417 	if (argc < 3)
418 		usage();
419 
420 	for (tests = 0, i = 1; i < argc - 1; i++) {
421 		cmd = argv[i];
422 
423 		if (strcmp(cmd, "all") == 0)
424 			tests = ~0;
425 		else if (strcmp(cmd, "ping") == 0)
426 			tests |= TEST_PING;
427 		else if (strcmp(cmd, "ip-opt") == 0)
428 			tests |= TEST_IP_OPT;
429 		else if (strcmp(cmd, "ip-tracert") == 0)
430 			tests |= TEST_IP_TRACERT;
431 		else if (strcmp(cmd, "frag") == 0)
432 			tests |= TEST_FRAG;
433 		else if (strcmp(cmd, "frag-new") == 0)
434 			tests |= TEST_FRAG_NEW;
435 		else if (strcmp(cmd, "frag-old") == 0)
436 			tests |= TEST_FRAG_OLD;
437 		else if (strcmp(cmd, "frag-timeout") == 0)
438 			tests |= TEST_FRAG_TIMEOUT;
439 		else
440 			usage();
441 	}
442 	if (addr_aton(argv[i], &ctx.dst) < 0)
443 		err(1, "invalid host %s", argv[i]);
444 
445 	if ((intf = intf_open()) == NULL)
446 		err(1, "couldn't open interface handle");
447 
448 	ifent.intf_len = sizeof(ifent);
449 
450 	if (intf_get_dst(intf, &ifent, &ctx.dst) < 0)
451 		err(1, "couldn't find interface for %s", addr_ntoa(&ctx.dst));
452 
453 	memcpy(&ctx.src, &ifent.intf_addr, sizeof(ctx.src));
454 	ctx.src.addr_bits = IP_ADDR_BITS;
455 
456 	intf_close(intf);
457 
458 	if ((ctx.ip = ip_open()) == NULL)
459 		err(1, "couldn't open raw IP interface");
460 
461 	if ((ctx.pcap = pcap_open1(ifent.intf_name)) == NULL)
462 		err(1, "couldn't open %s for sniffing", ifent.intf_name);
463 
464 	if ((ctx.dloff = pcap_dloff(ctx.pcap)) < 0)
465 		err(1, "couldn't determine link layer offset");
466 
467 	ctx.rnd = rand_open();
468 	pkt_init(16);
469 	TAILQ_INIT(&ctx.pktq);
470 
471 	ping = pkt_new();
472 	ip_pack_hdr(ping->pkt_ip, 0, IP_HDR_LEN + 8 + 24, 666, 0,
473 	    IP_TTL_DEFAULT, IP_PROTO_ICMP, ctx.src.addr_ip, ctx.dst.addr_ip);
474 	icmp_pack_hdr_echo(ping->pkt_icmp, ICMP_ECHO, ICMP_CODE_NONE,
475 	    666, 1, "AAAAAAAABBBBBBBBCCCCCCCC", 24);
476 	ping->pkt_end = ping->pkt_eth_data + IP_HDR_LEN + 8 + 24;
477 	pkt_decorate(ping);
478 
479 	if ((tests & TEST_PING) != 0)
480 		test_ping();
481 	if ((tests & TEST_IP_OPT) != 0)
482 		test_ip_opt();
483 	if ((tests & TEST_IP_TRACERT) != 0)
484 		test_ip_tracert();
485 	if ((tests & TEST_FRAG) != 0)
486 		test_frag(NULL, 0);
487 	if ((tests & TEST_FRAG_NEW) != 0)
488 		test_frag("new", 0);
489 	if ((tests & TEST_FRAG_OLD) != 0)
490 		test_frag("old", 0);
491 	if ((tests & TEST_FRAG_TIMEOUT) != 0)
492 		test_frag(NULL, 1);
493 
494 	rand_close(ctx.rnd);
495 	pcap_close(ctx.pcap);
496 	ip_close(ctx.ip);
497 
498 	exit(0);
499 }
500