2 * netsniff-ng - the packet sniffing beast
3 * By Daniel Borkmann <daniel@netsniff-ng.org>
4 * Copyright 2011 Daniel Borkmann.
5 * Subject to the GPL, version 2.
7 * An Autonomous System trace route utility based on TCP instead of ICMP for
8 * a better passing of firewalls. Supports IPv4 and IPv6. Based on the idea
9 * of tcptraceroute (http://michael.toren.net/code/tcptraceroute/), but hacked
10 * for Autonomous Systems tracing, thus you will know an approximate path of
11 * your curvetun tunneled packets, for instance. However, ashunt was written
12 * from scratch and does not use any libraries. Special thanks to Team CYMRU!
14 * The road must be trod, but it will be very hard. And neither strength nor
15 * wisdom will carry us far upon it. This quest may be attempted by the weak
16 * with as much hope as the strong. Yet such is oft the course of deeds that
17 * move the wheels of the world: small hands do them because they must,
18 * while the eyes of the great are elsewhere.
20 * -- The Lord of the Rings, Elrond, Chapter 'The Council of Elrond'.
22 * ashunt includes GeoLite data created by MaxMind, available from
23 * http://www.maxmind.com/. On Debian you need libgeoip-dev, libgeoip1 and
24 * geoip-database-contrib.
31 ashunt - Autonomous System (AS) trace route utility
35 ashunt -H|--host <host> -i|-d|--dev <dev> [-6|--ipv6]
36 [-n|--numeric] [-N|--dns] [-f|--init-ttl <ttl>]
37 [-m|--max-ttl <ttl>] [-q|--num-probes] [-x|--timeout <sec>]
38 [-S|--syn] [-A|--ack] [-F|--fin] [-P|--psh] [-U|--urg]
39 [-R|--rst] [-E|--ecn-syn] [-t|--tos <tos>] [-G|--nofrag]
40 [-X|--payload <string>] [-Z|--show-packet] [-l|--totlen <len>]
41 [-w|--whois <server>] [-W|--wport <port>] [--city-db <path>]
42 [--country-db <path>] [-v|--version] [-h|--help]
46 This program provides AS information on each hop between the client
53 =item ashunt -i eth0 -N -E -H netsniff-ng.org
55 IPv4 trace of AS with TCP ECN SYN probe
57 =item ashunt -i eth0 -N -S -H netsniff-ng.org
59 IPv4 trace of AS with TCP SYN probe
61 =item ashunt -i eth0 -N -F -H netsniff-ng.org
63 IPv4 trace of AS with TCP FIN probe
65 =item ashunt -i eth0 -N -FPU -H netsniff-ng.org
67 IPv4 trace of AS with Xmas probe
69 =item ashunt -i eth0 -N -H netsniff-ng.org -X "censor-me" -Z
71 IPv4 trace of AS with Null probe with ASCII payload
73 =item ashunt -6 -S -i eth0 -H netsniff-ng.org
75 IPv6 trace of AS up to netsniff-ng.org
85 Print help text and lists all options.
91 =item -H|--host <host>
93 Host/IPv4/IPv6 to lookup AS route to
95 =item i-|-d|--dev <netdev>
97 Networking device, i.e. eth0
99 =item -p|--port <port>
101 Hosts port to lookup AS route to
105 Use IPv4 requests (default)
113 Do not do reverse DNS lookup for hops
117 Do a reverse DNS lookup for hops
119 =item -f|--init-ttl <ttl>
123 =item -m|--max-ttl <ttl>
125 Set maximum TTL (default: 30)
127 =item -q|--num-probes <num>
129 Number of max probes for each hop (default: 3)
131 =item -x|--timeout <sec>
133 Probe response timeout in sec (default: 3)
137 Set TCP SYN flag in packets
141 Set TCP ACK flag in packets
145 Set TCP FIN flag in packets
149 Set TCP PSH flag in packets
153 Set TCP URG flag in packets
157 Set TCP RST flag in packets
161 Send ECN SYN packets (RFC3168)
167 =item -w|--whois <server>
169 Use a different AS whois DB server
170 (default: /etc/netsniff-ng/whois.conf)
172 =item -W|--wport <port>
174 Use a different port to AS whois server
175 (default: /etc/netsniff-ng/whois.conf)
177 =item --city-db <path>
179 Specifiy path for geoip city database
181 =item --country-db <path>
183 Specifiy path for geoip country database
189 Written by Daniel Borkmann <daniel@netsniff-ng.org>
193 Documentation by Emmanuel Roullit <emmanuel@netsniff-ng.org>
197 Please report bugs to <bugs@netsniff-ng.org>
210 #include <sys/types.h>
211 #include <sys/stat.h>
212 #include <sys/socket.h>
215 #include <asm/byteorder.h>
216 #include <linux/tcp.h>
217 #include <netinet/ip.h>
218 #include <netinet/ip6.h>
219 #include <netinet/in.h>
222 #include <arpa/inet.h>
223 #include <linux/if_ether.h>
224 #include <linux/icmp.h>
225 #include <linux/icmpv6.h>
227 #include <GeoIPCity.h>
232 #include "proto_hex.h"
235 #include "aslookup.h"
242 #define WHOIS_SERVER_SOURCE "/etc/netsniff-ng/whois.conf"
244 static int assemble_ipv6_tcp(uint8_t *packet
, size_t len
, int ttl
,
245 struct sockaddr_in
*sin
) __attribute__ ((unused
));
256 int syn
, ack
, ecn
, fin
, psh
, rst
, urg
;
265 sig_atomic_t sigint
= 0;
267 static int show_pkt
= 0;
269 static GeoIP
*gi_country
= NULL
;
270 static GeoIP
*gi_city
= NULL
;
272 static const char *short_options
= "H:p:nNf:m:i:d:q:x:SAEFPURt:Gl:w:W:hv46X:ZLK";
274 static struct option long_options
[] = {
275 {"host", required_argument
, 0, 'H'},
276 {"port", required_argument
, 0, 'p'},
277 {"init-ttl", required_argument
, 0, 'f'},
278 {"max-ttl", required_argument
, 0, 'm'},
279 {"numeric", no_argument
, 0, 'n'},
280 {"dns", no_argument
, 0, 'N'},
281 {"ipv4", no_argument
, 0, '4'},
282 {"ipv6", no_argument
, 0, '6'},
283 {"dev", required_argument
, 0, 'd'},
284 {"num-probes", required_argument
, 0, 'q'},
285 {"timeout", required_argument
, 0, 'x'},
286 {"syn", no_argument
, 0, 'S'},
287 {"ack", no_argument
, 0, 'A'},
288 {"urg", no_argument
, 0, 'U'},
289 {"fin", no_argument
, 0, 'F'},
290 {"psh", no_argument
, 0, 'P'},
291 {"rst", no_argument
, 0, 'R'},
292 {"ecn-syn", no_argument
, 0, 'E'},
293 {"tos", required_argument
, 0, 't'},
294 {"payload", required_argument
, 0, 'X'},
295 {"show-packet", no_argument
, 0, 'Z'},
296 {"nofrag", no_argument
, 0, 'G'},
297 {"totlen", required_argument
, 0, 'l'},
298 {"whois", required_argument
, 0, 'w'},
299 {"wport", required_argument
, 0, 'W'},
300 {"city-db", required_argument
, 0, 'L'},
301 {"country-db", required_argument
, 0, 'K'},
302 {"version", no_argument
, 0, 'v'},
303 {"help", no_argument
, 0, 'h'},
307 static struct sock_filter ipv4_icmp_type_11
[] = {
309 { 0x28, 0, 0, 0x0000000c },
310 /* (001) jeq #0x800 jt 2 jf 10 */
311 { 0x15, 0, 8, 0x00000800 },
313 { 0x30, 0, 0, 0x00000017 },
314 /* (003) jeq #0x1 jt 4 jf 10 */
315 { 0x15, 0, 6, 0x00000001 },
317 { 0x28, 0, 0, 0x00000014 },
318 /* (005) jset #0x1fff jt 10 jf 6 */
319 { 0x45, 4, 0, 0x00001fff },
320 /* (006) ldxb 4*([14]&0xf) */
321 { 0xb1, 0, 0, 0x0000000e },
322 /* (007) ldb [x + 14] */
323 { 0x50, 0, 0, 0x0000000e },
324 /* (008) jeq #0xb jt 9 jf 10 */
325 { 0x15, 0, 1, 0x0000000b },
326 /* (009) ret #65535 */
327 { 0x06, 0, 0, 0xffffffff },
329 { 0x06, 0, 0, 0x00000000 },
332 static struct sock_filter ipv6_icmp6_type_3
[] = {
334 { 0x28, 0, 0, 0x0000000c },
335 /* (001) jeq #0x86dd jt 2 jf 7 */
336 { 0x15, 0, 5, 0x000086dd },
338 { 0x30, 0, 0, 0x00000014 },
339 /* (003) jeq #0x3a jt 4 jf 7 */
340 { 0x15, 0, 3, 0x0000003a },
342 { 0x30, 0, 0, 0x00000036 },
343 /* (005) jeq #0x3 jt 6 jf 7 */
344 { 0x15, 0, 1, 0x00000003 },
345 /* (006) ret #65535 */
346 { 0x06, 0, 0, 0xffffffff },
348 { 0x06, 0, 0, 0x00000000 },
351 static void signal_handler(int number
)
362 static void header(void)
364 printf("%s%s%s\n", colorize_start(bold
), "ashunt "
365 VERSION_STRING
, colorize_end());
368 static void help(void)
371 printf("\nashunt %s, Autonomous System (AS) trace route utility\n",
373 printf("http://www.netsniff-ng.org\n\n");
374 printf("Usage: ashunt [options]\n");
375 printf("Options:\n");
376 printf(" -H|--host <host> Host/IPv4/IPv6 to lookup AS route to\n");
377 printf(" -p|--port <port> Hosts port to lookup AS route to\n");
378 printf(" -i|-d|--dev <device> Networking device, i.e. eth0\n");
379 printf(" -4|--ipv4 Use IPv4 requests (default)\n");
380 printf(" -6|--ipv6 Use IPv6 requests\n");
381 printf(" -n|--numeric Do not do reverse DNS lookup for hops\n");
382 printf(" -N|--dns Do a reverse DNS lookup for hops\n");
383 printf(" -f|--init-ttl <ttl> Set initial TTL\n");
384 printf(" -m|--max-ttl <ttl> Set maximum TTL (default: 30)\n");
385 printf(" -q|--num-probes <num> Number of max probes for each hop (default: 3)\n");
386 printf(" -x|--timeout <sec> Probe response timeout in sec (default: 3)\n");
387 printf(" -S|--syn Set TCP SYN flag in packets\n");
388 printf(" -A|--ack Set TCP ACK flag in packets\n");
389 printf(" -F|--fin Set TCP FIN flag in packets\n");
390 printf(" -P|--psh Set TCP PSH flag in packets\n");
391 printf(" -U|--urg Set TCP URG flag in packets\n");
392 printf(" -R|--rst Set TCP RST flag in packets\n");
393 printf(" -E|--ecn-syn Send ECN SYN packets (RFC3168)\n");
394 printf(" -t|--tos <tos> Set the IP TOS field\n");
395 printf(" -G|--nofrag Set do not fragment bit\n");
396 printf(" -X|--payload <string> Specify a payload string to test DPIs\n");
397 printf(" -Z|--show-packet Show returned packet on each hop\n");
398 printf(" -l|--totlen <len> Specify total packet len\n");
399 printf(" -w|--whois <server> Use a different AS whois DB server\n");
400 printf(" (default: /etc/netsniff-ng/whois.conf)\n");
401 printf(" -W|--wport <port> Use a different port to AS whois server\n");
402 printf(" (default: /etc/netsniff-ng/whois.conf)\n");
403 printf(" --city-db <path> Specifiy path for geoip city database\n");
404 printf(" --country-db <path> Specifiy path for geoip country database\n");
405 printf(" -v|--version Print version\n");
406 printf(" -h|--help Print this help\n");
408 printf("Examples:\n");
409 printf(" IPv4 trace of AS with TCP ECN SYN probe:\n");
410 printf(" ashunt -i eth0 -N -E -H netsniff-ng.org\n");
411 printf(" IPv4 trace of AS with TCP SYN probe (this will most-likely pass):\n");
412 printf(" ashunt -i eth0 -N -S -H netsniff-ng.org\n");
413 printf(" IPv4 trace of AS with TCP FIN probe:\n");
414 printf(" ashunt -i eth0 -N -F -H netsniff-ng.org\n");
415 printf(" IPv4 trace of AS with Xmas probe:\n");
416 printf(" ashunt -i eth0 -N -FPU -H netsniff-ng.org\n");
417 printf(" IPv4 trace of AS with Null probe with ASCII payload:\n");
418 printf(" ashunt -i eth0 -N -H netsniff-ng.org -X \"censor-me\" -Z\n");
419 printf(" IPv6 trace of AS up to netsniff-ng.org:\n");
420 printf(" ashunt -6 -S -i eth0 -H netsniff-ng.org\n");
423 printf(" If the TCP probe did not give any results, then ashunt will\n");
424 printf(" automatically probe for classic ICMP packets! To gather more\n");
425 printf(" information about ashunt's fetched AS numbers, see i.e.\n");
426 printf(" http://bgp.he.net/AS<number>!\n");
428 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
429 printf("Copyright (C) 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n");
430 printf("Swiss federal institute of technology (ETH Zurich)\n");
431 printf("License: GNU GPL version 2\n");
432 printf("This is free software: you are free to change and redistribute it.\n");
433 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
438 static void version(void)
440 printf("\nashunt %s, AS trace route utility\n",
442 printf("http://www.netsniff-ng.org\n\n");
443 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
444 printf("Copyright (C) 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n");
445 printf("Swiss federal institute of technology (ETH Zurich)\n");
446 printf("License: GNU GPL version 2\n");
447 printf("This is free software: you are free to change and redistribute it.\n");
448 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
453 static inline unsigned short csum(unsigned short *buf
, int nwords
)
456 for (sum
= 0; nwords
> 0; nwords
--)
458 sum
= (sum
>> 16) + (sum
& 0xffff);
463 static void assemble_data(uint8_t *packet
, size_t len
, const char *payload
)
466 if (payload
== NULL
) {
467 for (i
= 0; i
< len
; ++i
)
468 packet
[i
] = (uint8_t) mt_rand_int32();
470 int lmin
= min(len
, strlen(payload
));
471 for (i
= 0; i
< lmin
; ++i
)
472 packet
[i
] = (uint8_t) payload
[i
];
473 for (i
= lmin
; i
< len
; ++i
)
474 packet
[i
] = (uint8_t) mt_rand_int32();
478 static void assemble_tcp(uint8_t *packet
, size_t len
, int syn
, int ack
,
479 int urg
, int fin
, int rst
, int psh
, int ecn
, int dport
)
481 struct tcphdr
*tcph
= (struct tcphdr
*) packet
;
482 assert(len
>= sizeof(struct tcphdr
));
483 tcph
->source
= htons((uint16_t) mt_rand_int32());
484 tcph
->dest
= htons((uint16_t) dport
);
485 tcph
->seq
= htonl(mt_rand_int32());
486 tcph
->ack_seq
= (!!ack
? htonl(mt_rand_int32()) : 0);
496 tcph
->window
= htons((uint16_t) (100 + (mt_rand_int32() % 65435)));
498 tcph
->urg_ptr
= (!!urg
? htons((uint16_t) mt_rand_int32()) : 0);
501 /* returns: ipv4 id */
502 static int assemble_ipv4_tcp(uint8_t *packet
, size_t len
, int ttl
,
503 int tos
, const struct sockaddr
*dst
,
504 const struct sockaddr
*src
, int syn
, int ack
,
505 int urg
, int fin
, int rst
, int psh
, int ecn
,
506 int nofrag
, int dport
, const char *payload
)
508 struct iphdr
*iph
= (struct iphdr
*) packet
;
510 assert(src
->sa_family
== PF_INET
&& dst
->sa_family
== PF_INET
);
511 assert(len
>= sizeof(struct iphdr
) + sizeof(struct tcphdr
));
514 iph
->tos
= (uint8_t) tos
;
515 iph
->tot_len
= htons((uint16_t) len
);
516 iph
->id
= htons((uint16_t) mt_rand_int32());
517 iph
->frag_off
= nofrag
? IP_DF
: 0;
518 iph
->ttl
= (uint8_t) ttl
;
519 iph
->protocol
= 6; /* TCP */
520 iph
->saddr
= ((struct sockaddr_in
*) src
)->sin_addr
.s_addr
;
521 iph
->daddr
= ((struct sockaddr_in
*) dst
)->sin_addr
.s_addr
;
522 assemble_tcp(packet
+ sizeof(struct iphdr
),
523 len
- sizeof(struct iphdr
), syn
, ack
, urg
, fin
, rst
,
525 assemble_data(packet
+ sizeof(struct iphdr
) + sizeof(struct tcphdr
),
526 len
- sizeof(struct iphdr
) - sizeof(struct tcphdr
),
528 iph
->check
= csum((unsigned short *) packet
,
529 ntohs(iph
->tot_len
) >> 1);
530 return ntohs(iph
->id
);
533 /* returns: ipv6 flow label */
534 static int assemble_ipv6_tcp(uint8_t *packet
, size_t len
, int ttl
,
535 struct sockaddr_in
*sin
)
540 static void assemble_icmp4(uint8_t *packet
, size_t len
)
542 struct icmphdr
*icmph
= (struct icmphdr
*) packet
;
543 assert(len
>= sizeof(struct icmphdr
));
544 icmph
->type
= ICMP_ECHO
;
549 /* returns: ipv4 id */
550 static int assemble_ipv4_icmp4(uint8_t *packet
, size_t len
, int ttl
,
551 int tos
, const struct sockaddr
*dst
,
552 const struct sockaddr
*src
, int nofrag
,
555 struct iphdr
*iph
= (struct iphdr
*) packet
;
557 assert(src
->sa_family
== PF_INET
&& dst
->sa_family
== PF_INET
);
558 assert(len
>= sizeof(struct iphdr
) + sizeof(struct tcphdr
));
562 iph
->tot_len
= htons((uint16_t) len
);
563 iph
->id
= htons((uint16_t) mt_rand_int32());
564 iph
->frag_off
= nofrag
? IP_DF
: 0;
565 iph
->ttl
= (uint8_t) ttl
;
566 iph
->protocol
= 1; /* ICMP4 */
567 iph
->saddr
= ((struct sockaddr_in
*) src
)->sin_addr
.s_addr
;
568 iph
->daddr
= ((struct sockaddr_in
*) dst
)->sin_addr
.s_addr
;
569 assemble_icmp4(packet
+ sizeof(struct iphdr
),
570 len
- sizeof(struct iphdr
));
571 assemble_data(packet
+ sizeof(struct iphdr
) + sizeof(struct icmphdr
),
572 len
- sizeof(struct iphdr
) - sizeof(struct icmphdr
),
574 iph
->check
= csum((unsigned short *) packet
,
575 ntohs(iph
->tot_len
) >> 1);
576 return ntohs(iph
->id
);
579 static int assemble_packet_or_die(uint8_t *packet
, size_t len
, int ttl
, int icmp
,
580 const struct ash_cfg
*cfg
,
581 const struct sockaddr
*dst
,
582 const struct sockaddr
*src
)
585 return assemble_ipv4_icmp4(packet
, len
, ttl
, cfg
->tos
, dst
, src
,
586 cfg
->nofrag
, cfg
->payload
);
588 return assemble_ipv4_tcp(packet
, len
, ttl
, cfg
->tos
, dst
, src
,
589 cfg
->syn
, cfg
->ack
, cfg
->urg
, cfg
->fin
,
590 cfg
->rst
, cfg
->psh
, cfg
->ecn
,
591 cfg
->nofrag
, atoi(cfg
->port
),
595 #define PKT_NOT_FOR_US 0
598 static inline const char *make_n_a(const char *p
)
603 static int handle_ipv4_icmp(uint8_t *packet
, size_t len
, int ttl
, int id
,
604 const struct sockaddr
*own
, int dns_resolv
)
607 struct iphdr
*iph
= (struct iphdr
*) packet
;
608 struct iphdr
*iph_inner
;
609 struct icmphdr
*icmph
;
611 struct sockaddr_in sa
;
614 if (iph
->protocol
!= 1)
615 return PKT_NOT_FOR_US
;
616 if (iph
->daddr
!= ((struct sockaddr_in
*) own
)->sin_addr
.s_addr
)
617 return PKT_NOT_FOR_US
;
618 icmph
= (struct icmphdr
*) (packet
+ sizeof(struct iphdr
));
619 if (icmph
->type
!= ICMP_TIME_EXCEEDED
)
620 return PKT_NOT_FOR_US
;
621 if (icmph
->code
!= ICMP_EXC_TTL
)
622 return PKT_NOT_FOR_US
;
623 iph_inner
= (struct iphdr
*) (packet
+ sizeof(struct iphdr
) +
624 sizeof(struct icmphdr
));
625 if (ntohs(iph_inner
->id
) != id
)
626 return PKT_NOT_FOR_US
;
627 hbuff
= xzmalloc(NI_MAXHOST
);
628 memset(&sa
, 0, sizeof(sa
));
629 sa
.sin_family
= PF_INET
;
630 sa
.sin_addr
.s_addr
= iph
->saddr
;
631 getnameinfo((struct sockaddr
*) &sa
, sizeof(sa
), hbuff
, NI_MAXHOST
,
632 NULL
, 0, NI_NUMERICHOST
);
633 memset(&rec
, 0, sizeof(rec
));
634 ret
= aslookup(hbuff
, &rec
);
636 panic("AS lookup error %d!\n", ret
);
637 gir
= GeoIP_record_by_ipnum(gi_city
, ntohl(iph
->saddr
));
639 if (strlen(rec
.country
) > 0 && gir
) {
640 const char *city
= make_n_a(gir
->city
);
641 printf("%s in AS%s (%s, %s, %s, %f, %f), %s %s (%s), %s", hbuff
,
642 rec
.number
, rec
.country
,
643 GeoIP_country_name_by_ipnum(gi_country
, ntohl(iph
->saddr
)),
644 city
, gir
->latitude
, gir
->longitude
,
645 rec
.prefix
, rec
.registry
, rec
.since
, rec
.name
);
646 } else if (strlen(rec
.country
) > 0 && !gir
) {
647 printf("%s in AS%s (%s, %s), %s %s (%s), %s", hbuff
,
648 rec
.number
, rec
.country
,
649 GeoIP_country_name_by_ipnum(gi_country
, ntohl(iph
->saddr
)),
650 rec
.prefix
, rec
.registry
, rec
.since
, rec
.name
);
652 printf("%s in unkown AS", hbuff
);
655 struct hostent
*hent
= gethostbyaddr(&sa
.sin_addr
,
658 if (strlen(rec
.country
) > 0 && gir
) {
659 const char *city
= make_n_a(gir
->city
);
660 printf("%s (%s) in AS%s (%s, %s, %s, %f, %f), %s %s (%s), %s",
661 (hent
? hent
->h_name
: hbuff
), hbuff
,
662 rec
.number
, rec
.country
,
663 GeoIP_country_name_by_ipnum(gi_country
, ntohl(iph
->saddr
)),
664 city
, gir
->latitude
, gir
->longitude
,
665 rec
.prefix
, rec
.registry
,
666 rec
.since
, rec
.name
);
667 } else if (strlen(rec
.country
) > 0 && !gir
) {
668 printf("%s (%s) in AS%s (%s, %s), %s %s (%s), %s",
669 (hent
? hent
->h_name
: hbuff
), hbuff
,
670 rec
.number
, rec
.country
,
671 GeoIP_country_name_by_ipnum(gi_country
, ntohl(iph
->saddr
)),
672 rec
.prefix
, rec
.registry
,
673 rec
.since
, rec
.name
);
675 printf("%s (%s) in unkown AS",
676 (hent
? hent
->h_name
: hbuff
), hbuff
);
683 static int handle_packet(uint8_t *packet
, size_t len
, int ip
, int ttl
, int id
,
684 struct sockaddr
*own
, int dns_resolv
)
686 return handle_ipv4_icmp(packet
, len
, ttl
, id
, own
, dns_resolv
);
689 static int do_trace(const struct ash_cfg
*cfg
)
691 int ttl
, query
, fd
= -1, one
= 1, ret
, fd_cap
, ifindex
;
692 int is_okay
= 0, id
, timeout_poll
;
693 uint8_t *packet
, *packet_rcv
;
694 ssize_t err
, real_len
;
696 struct addrinfo hints
, *ahead
, *ai
;
697 char *hbuff1
, *hbuff2
;
698 struct sockaddr_storage ss
, sd
;
699 struct sock_fprog bpf_ops
;
700 struct ring dummy_ring
;
703 mt_init_by_random_device();
705 memset(&hints
, 0, sizeof(hints
));
706 hints
.ai_family
= PF_UNSPEC
;
707 hints
.ai_socktype
= SOCK_STREAM
;
708 hints
.ai_protocol
= IPPROTO_TCP
;
709 hints
.ai_flags
= AI_NUMERICSERV
;
711 ret
= getaddrinfo(cfg
->host
, cfg
->port
, &hints
, &ahead
);
713 whine("Cannot get address info!\n");
717 for (ai
= ahead
; ai
!= NULL
&& fd
< 0; ai
= ai
->ai_next
) {
718 if (!((ai
->ai_family
== PF_INET6
&& cfg
->ip
== 6) ||
719 (ai
->ai_family
== PF_INET
&& cfg
->ip
== 4)))
721 fd
= socket(ai
->ai_family
, SOCK_RAW
, ai
->ai_protocol
);
724 fd_cap
= pf_socket();
725 memset(&ss
, 0, sizeof(ss
));
726 ret
= device_address(cfg
->dev
, ai
->ai_family
, &ss
);
728 panic("Cannot get own device address!\n");
729 ret
= bind(fd
, (struct sockaddr
*) &ss
, sizeof(ss
));
731 panic("Cannot bind socket!\n");
732 memset(&sd
, 0, sizeof(sd
));
733 memcpy(&sd
, ai
->ai_addr
, ai
->ai_addrlen
);
739 whine("Cannot create socket! Does remote support IPv%d?!\n",
746 if (len
< sizeof(struct iphdr
) + sizeof(struct tcphdr
)) {
747 len
= sizeof(struct iphdr
) + sizeof(struct tcphdr
);
749 len
+= strlen(cfg
->payload
);
752 if (len
< sizeof(struct ip6_hdr
) + sizeof(struct tcphdr
)) {
753 len
= sizeof(struct ip6_hdr
) + sizeof(struct tcphdr
);
755 len
+= strlen(cfg
->payload
);
758 if (len
>= device_mtu(cfg
->dev
))
759 panic("Packet len exceeds device MTU!\n");
761 packet
= xmalloc(len
);
762 len_rcv
= device_mtu(cfg
->dev
);
763 packet_rcv
= xmalloc(len_rcv
);
765 hbuff1
= xzmalloc(256);
766 getnameinfo((struct sockaddr
*) &sd
, sizeof(sd
), hbuff1
, 256,
767 NULL
, 0, NI_NUMERICHOST
);
769 hbuff2
= xzmalloc(256);
770 getnameinfo((struct sockaddr
*) &ss
, sizeof(ss
), hbuff2
, 256,
771 NULL
, 0, NI_NUMERICHOST
);
773 ret
= setsockopt(fd
, cfg
->ip
== 4 ? IPPROTO_IP
: IPPROTO_IPV6
,
774 IP_HDRINCL
, &one
, sizeof(one
));
776 panic("Kernel does not support IP_HDRINCL!\n");
778 info("AS path IPv%d TCP trace from %s to %s:%s (%s) with len %u "
779 "Bytes, %u max hops\n", cfg
->ip
, hbuff2
, hbuff1
, cfg
->port
,
780 cfg
->host
, len
, cfg
->max_ttl
);
781 info("Using flags SYN:%d,ACK:%d,ECN:%d,FIN:%d,PSH:%d,RST:%d,URG:%d\n",
782 cfg
->syn
, cfg
->ack
, cfg
->ecn
, cfg
->fin
, cfg
->psh
, cfg
->rst
,
785 info("With payload: \'%s\'\n", cfg
->payload
);
791 hbuff1
= hbuff2
= NULL
;
793 enable_kernel_bpf_jit_compiler();
794 memset(&bpf_ops
, 0, sizeof(bpf_ops
));
796 bpf_ops
.filter
= ipv4_icmp_type_11
;
797 bpf_ops
.len
= (sizeof(ipv4_icmp_type_11
) /
798 sizeof(ipv4_icmp_type_11
[0]));
800 bpf_ops
.filter
= ipv6_icmp6_type_3
;
801 bpf_ops
.len
= (sizeof(ipv6_icmp6_type_3
) /
802 sizeof(ipv6_icmp6_type_3
[0]));
804 bpf_attach_to_sock(fd_cap
, &bpf_ops
);
805 ifindex
= device_ifindex(cfg
->dev
);
806 bind_rx_ring(fd_cap
, &dummy_ring
, ifindex
);
807 prepare_polling(fd_cap
, &pfd
);
808 timeout_poll
= (cfg
->timeout
> 0 ? cfg
->timeout
: 3) * 1000;
810 for (ttl
= cfg
->init_ttl
; ttl
<= cfg
->max_ttl
; ++ttl
) {
814 if ((ttl
== cfg
->init_ttl
&& !show_pkt
) ||
815 (ttl
> cfg
->init_ttl
)) {
820 for (query
= 0; query
< cfg
->queries
&& !is_okay
; ++query
) {
821 id
= assemble_packet_or_die(packet
, len
, ttl
, icmp
, cfg
,
822 (struct sockaddr
*) &sd
,
823 (struct sockaddr
*) &ss
);
824 if (ttl
== cfg
->init_ttl
&& query
== 0 && show_pkt
) {
825 info("Original packet:\n");
828 info("\n%2d: ", ttl
);
832 err
= sendto(fd
, packet
, len
, 0, (struct sockaddr
*) &sd
,
835 panic("sendto failed: %s\n", strerror(errno
));
837 err
= poll(&pfd
, 1, timeout_poll
);
838 if (err
> 0 && pfd
.revents
& POLLIN
) {
839 real_len
= recvfrom(fd_cap
, packet_rcv
, len_rcv
,
841 if (real_len
< sizeof(struct ethhdr
) +
842 (cfg
->ip
? sizeof(struct iphdr
) +
843 sizeof(struct icmphdr
) :
844 sizeof(struct ip6_hdr
) +
845 sizeof(struct icmp6hdr
)))
848 is_okay
= handle_packet(packet_rcv
+ sizeof(struct ethhdr
),
849 real_len
- sizeof(struct ethhdr
),
851 (struct sockaddr
*) &ss
,
853 if (is_okay
&& show_pkt
) {
854 info("\n Received packet:\n");
855 hex(packet_rcv
, real_len
);
865 if (is_okay
== 0 && icmp
== 0) {
881 void parse_whois_or_die(struct ash_cfg
*cfg
)
885 char tmp
[512], *ptr
, *ptr2
;
887 fd
= open_or_die(WHOIS_SERVER_SOURCE
, O_RDONLY
);
888 while ((ret
= read(fd
, tmp
, sizeof(tmp
))) > 0) {
889 tmp
[sizeof(tmp
) - 1] = 0;
892 while (*ptr2
!= ' ' && ptr2
< &tmp
[sizeof(tmp
) - 1])
895 panic("Parser error!\n");
897 cfg
->whois
= xstrdup(ptr
);
899 if (ptr
>= &tmp
[sizeof(tmp
) - 1])
900 panic("Parser error!\n");
902 ptr
[strlen(ptr
) - 1] = 0;
903 cfg
->whois_port
= xstrdup(ptr
);
909 int main(int argc
, char **argv
)
911 int c
, opt_index
, ret
;
913 char *path_city_db
= NULL
, *path_country_db
= NULL
;
915 check_for_root_maybe_die();
917 memset(&cfg
, 0, sizeof(cfg
));
924 cfg
.dev
= xstrdup("eth0");
925 cfg
.port
= xstrdup("80");
927 while ((c
= getopt_long(argc
, argv
, short_options
, long_options
,
928 &opt_index
)) != EOF
) {
937 cfg
.host
= xstrdup(optarg
);
942 cfg
.port
= xstrdup(optarg
);
960 cfg
.init_ttl
= atoi(optarg
);
961 if (cfg
.init_ttl
<= 0)
965 cfg
.max_ttl
= atoi(optarg
);
966 if (cfg
.max_ttl
<= 0)
973 cfg
.dev
= xstrdup(optarg
);
976 cfg
.queries
= atoi(optarg
);
977 if (cfg
.queries
<= 0)
981 cfg
.timeout
= atoi(optarg
);
982 if (cfg
.timeout
<= 0)
1008 cfg
.tos
= atoi(optarg
);
1016 cfg
.payload
= xstrdup(optarg
);
1019 cfg
.totlen
= atoi(optarg
);
1020 if (cfg
.totlen
<= 0)
1024 cfg
.whois
= xstrdup(optarg
);
1027 cfg
.whois_port
= xstrdup(optarg
);
1030 path_city_db
= xstrdup(optarg
);
1033 path_country_db
= xstrdup(optarg
);
1052 panic("Option -%c requires an argument!\n",
1055 if (isprint(optopt
))
1056 whine("Unknown option character "
1057 "`0x%X\'!\n", optopt
);
1066 !cfg
.host
|| !cfg
.port
||
1067 cfg
.init_ttl
> cfg
.max_ttl
||
1068 cfg
.init_ttl
> MAXTTL
||
1069 cfg
.max_ttl
> MAXTTL
)
1071 if (!device_up_and_running(cfg
.dev
))
1072 panic("Networking device not up and running!\n");
1073 if (!cfg
.whois
|| !cfg
.whois_port
)
1074 parse_whois_or_die(&cfg
);
1075 if (device_mtu(cfg
.dev
) <= cfg
.totlen
)
1076 panic("Packet larger than device MTU!\n");
1077 register_signal(SIGHUP
, signal_handler
);
1080 ret
= aslookup_prepare(cfg
.whois
, cfg
.whois_port
);
1082 panic("Cannot resolve whois server!\n");
1083 if (path_country_db
)
1084 gi_country
= GeoIP_open(path_country_db
, GEOIP_MMAP_CACHE
);
1086 gi_country
= GeoIP_open_type(GEOIP_COUNTRY_EDITION
,
1089 gi_city
= GeoIP_open(path_city_db
, GEOIP_MMAP_CACHE
);
1091 gi_city
= GeoIP_open_type(GEOIP_CITY_EDITION_REV1
,
1093 if (!gi_country
|| !gi_city
)
1094 panic("Cannot open GeoIP database! Wrong path?!\n");
1095 GeoIP_set_charset(gi_country
, GEOIP_CHARSET_UTF8
);
1096 GeoIP_set_charset(gi_city
, GEOIP_CHARSET_UTF8
);
1097 ret
= do_trace(&cfg
);
1098 GeoIP_delete(gi_city
);
1099 GeoIP_delete(gi_country
);
1102 xfree(cfg
.whois_port
);
1114 xfree(path_city_db
);
1115 if (path_country_db
)
1116 xfree(path_country_db
);