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