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>
233 #include "proto_hex.h"
236 #include "aslookup.h"
244 #define WHOIS_SERVER_SOURCE "/etc/netsniff-ng/whois.conf"
246 static int assemble_ipv6_tcp(uint8_t *packet
, size_t len
, int ttl
,
247 struct sockaddr_in
*sin
) __attribute__ ((unused
));
258 int syn
, ack
, ecn
, fin
, psh
, rst
, urg
;
267 sig_atomic_t sigint
= 0;
269 static int show_pkt
= 0;
271 static GeoIP
*gi_country
= NULL
;
272 static GeoIP
*gi_city
= NULL
;
274 static const char *short_options
= "H:p:nNf:m:i:d:q:x:SAEFPURt:Gl:w:W:hv46X:ZLK";
276 static struct option long_options
[] = {
277 {"host", required_argument
, 0, 'H'},
278 {"port", required_argument
, 0, 'p'},
279 {"init-ttl", required_argument
, 0, 'f'},
280 {"max-ttl", required_argument
, 0, 'm'},
281 {"numeric", no_argument
, 0, 'n'},
282 {"dns", no_argument
, 0, 'N'},
283 {"ipv4", no_argument
, 0, '4'},
284 {"ipv6", no_argument
, 0, '6'},
285 {"dev", required_argument
, 0, 'd'},
286 {"num-probes", required_argument
, 0, 'q'},
287 {"timeout", required_argument
, 0, 'x'},
288 {"syn", no_argument
, 0, 'S'},
289 {"ack", no_argument
, 0, 'A'},
290 {"urg", no_argument
, 0, 'U'},
291 {"fin", no_argument
, 0, 'F'},
292 {"psh", no_argument
, 0, 'P'},
293 {"rst", no_argument
, 0, 'R'},
294 {"ecn-syn", no_argument
, 0, 'E'},
295 {"tos", required_argument
, 0, 't'},
296 {"payload", required_argument
, 0, 'X'},
297 {"show-packet", no_argument
, 0, 'Z'},
298 {"nofrag", no_argument
, 0, 'G'},
299 {"totlen", required_argument
, 0, 'l'},
300 {"whois", required_argument
, 0, 'w'},
301 {"wport", required_argument
, 0, 'W'},
302 {"city-db", required_argument
, 0, 'L'},
303 {"country-db", required_argument
, 0, 'K'},
304 {"version", no_argument
, 0, 'v'},
305 {"help", no_argument
, 0, 'h'},
309 static struct sock_filter ipv4_icmp_type_11
[] = {
311 { 0x28, 0, 0, 0x0000000c },
312 /* (001) jeq #0x800 jt 2 jf 10 */
313 { 0x15, 0, 8, 0x00000800 },
315 { 0x30, 0, 0, 0x00000017 },
316 /* (003) jeq #0x1 jt 4 jf 10 */
317 { 0x15, 0, 6, 0x00000001 },
319 { 0x28, 0, 0, 0x00000014 },
320 /* (005) jset #0x1fff jt 10 jf 6 */
321 { 0x45, 4, 0, 0x00001fff },
322 /* (006) ldxb 4*([14]&0xf) */
323 { 0xb1, 0, 0, 0x0000000e },
324 /* (007) ldb [x + 14] */
325 { 0x50, 0, 0, 0x0000000e },
326 /* (008) jeq #0xb jt 9 jf 10 */
327 { 0x15, 0, 1, 0x0000000b },
328 /* (009) ret #65535 */
329 { 0x06, 0, 0, 0xffffffff },
331 { 0x06, 0, 0, 0x00000000 },
334 static struct sock_filter ipv6_icmp6_type_3
[] = {
336 { 0x28, 0, 0, 0x0000000c },
337 /* (001) jeq #0x86dd jt 2 jf 7 */
338 { 0x15, 0, 5, 0x000086dd },
340 { 0x30, 0, 0, 0x00000014 },
341 /* (003) jeq #0x3a jt 4 jf 7 */
342 { 0x15, 0, 3, 0x0000003a },
344 { 0x30, 0, 0, 0x00000036 },
345 /* (005) jeq #0x3 jt 6 jf 7 */
346 { 0x15, 0, 1, 0x00000003 },
347 /* (006) ret #65535 */
348 { 0x06, 0, 0, 0xffffffff },
350 { 0x06, 0, 0, 0x00000000 },
353 static void signal_handler(int number
)
364 static void header(void)
366 printf("%s%s%s\n", colorize_start(bold
), "ashunt "
367 VERSION_STRING
, colorize_end());
370 static void help(void)
373 printf("\nashunt %s, Autonomous System (AS) trace route utility\n",
375 printf("http://www.netsniff-ng.org\n\n");
376 printf("Usage: ashunt [options]\n");
377 printf("Options:\n");
378 printf(" -H|--host <host> Host/IPv4/IPv6 to lookup AS route to\n");
379 printf(" -p|--port <port> Hosts port to lookup AS route to\n");
380 printf(" -i|-d|--dev <device> Networking device, i.e. eth0\n");
381 printf(" -4|--ipv4 Use IPv4 requests (default)\n");
382 printf(" -6|--ipv6 Use IPv6 requests\n");
383 printf(" -n|--numeric Do not do reverse DNS lookup for hops\n");
384 printf(" -N|--dns Do a reverse DNS lookup for hops\n");
385 printf(" -f|--init-ttl <ttl> Set initial TTL\n");
386 printf(" -m|--max-ttl <ttl> Set maximum TTL (default: 30)\n");
387 printf(" -q|--num-probes <num> Number of max probes for each hop (default: 3)\n");
388 printf(" -x|--timeout <sec> Probe response timeout in sec (default: 3)\n");
389 printf(" -S|--syn Set TCP SYN flag in packets\n");
390 printf(" -A|--ack Set TCP ACK flag in packets\n");
391 printf(" -F|--fin Set TCP FIN flag in packets\n");
392 printf(" -P|--psh Set TCP PSH flag in packets\n");
393 printf(" -U|--urg Set TCP URG flag in packets\n");
394 printf(" -R|--rst Set TCP RST flag in packets\n");
395 printf(" -E|--ecn-syn Send ECN SYN packets (RFC3168)\n");
396 printf(" -t|--tos <tos> Set the IP TOS field\n");
397 printf(" -G|--nofrag Set do not fragment bit\n");
398 printf(" -X|--payload <string> Specify a payload string to test DPIs\n");
399 printf(" -Z|--show-packet Show returned packet on each hop\n");
400 printf(" -l|--totlen <len> Specify total packet len\n");
401 printf(" -w|--whois <server> Use a different AS whois DB server\n");
402 printf(" (default: /etc/netsniff-ng/whois.conf)\n");
403 printf(" -W|--wport <port> Use a different port to AS whois server\n");
404 printf(" (default: /etc/netsniff-ng/whois.conf)\n");
405 printf(" --city-db <path> Specifiy path for geoip city database\n");
406 printf(" --country-db <path> Specifiy path for geoip country database\n");
407 printf(" -v|--version Print version\n");
408 printf(" -h|--help Print this help\n");
410 printf("Examples:\n");
411 printf(" IPv4 trace of AS with TCP ECN SYN probe:\n");
412 printf(" ashunt -i eth0 -N -E -H netsniff-ng.org\n");
413 printf(" IPv4 trace of AS with TCP SYN probe (this will most-likely pass):\n");
414 printf(" ashunt -i eth0 -N -S -H netsniff-ng.org\n");
415 printf(" IPv4 trace of AS with TCP FIN probe:\n");
416 printf(" ashunt -i eth0 -N -F -H netsniff-ng.org\n");
417 printf(" IPv4 trace of AS with Xmas probe:\n");
418 printf(" ashunt -i eth0 -N -FPU -H netsniff-ng.org\n");
419 printf(" IPv4 trace of AS with Null probe with ASCII payload:\n");
420 printf(" ashunt -i eth0 -N -H netsniff-ng.org -X \"censor-me\" -Z\n");
421 printf(" IPv6 trace of AS up to netsniff-ng.org:\n");
422 printf(" ashunt -6 -S -i eth0 -H netsniff-ng.org\n");
425 printf(" If the TCP probe did not give any results, then ashunt will\n");
426 printf(" automatically probe for classic ICMP packets! To gather more\n");
427 printf(" information about ashunt's fetched AS numbers, see i.e.\n");
428 printf(" http://bgp.he.net/AS<number>!\n");
430 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
431 printf("Copyright (C) 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n");
432 printf("Swiss federal institute of technology (ETH Zurich)\n");
433 printf("License: GNU GPL version 2\n");
434 printf("This is free software: you are free to change and redistribute it.\n");
435 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
440 static void version(void)
442 printf("\nashunt %s, AS trace route utility\n",
444 printf("http://www.netsniff-ng.org\n\n");
445 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
446 printf("Copyright (C) 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n");
447 printf("Swiss federal institute of technology (ETH Zurich)\n");
448 printf("License: GNU GPL version 2\n");
449 printf("This is free software: you are free to change and redistribute it.\n");
450 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
455 static inline unsigned short csum(unsigned short *buf
, int nwords
)
458 for (sum
= 0; nwords
> 0; nwords
--)
460 sum
= (sum
>> 16) + (sum
& 0xffff);
465 static void assemble_data(uint8_t *packet
, size_t len
, const char *payload
)
468 if (payload
== NULL
) {
469 for (i
= 0; i
< len
; ++i
)
470 packet
[i
] = (uint8_t) mt_rand_int32();
472 int lmin
= min(len
, strlen(payload
));
473 for (i
= 0; i
< lmin
; ++i
)
474 packet
[i
] = (uint8_t) payload
[i
];
475 for (i
= lmin
; i
< len
; ++i
)
476 packet
[i
] = (uint8_t) mt_rand_int32();
480 static void assemble_tcp(uint8_t *packet
, size_t len
, int syn
, int ack
,
481 int urg
, int fin
, int rst
, int psh
, int ecn
, int dport
)
483 struct tcphdr
*tcph
= (struct tcphdr
*) packet
;
484 assert(len
>= sizeof(struct tcphdr
));
485 tcph
->source
= htons((uint16_t) mt_rand_int32());
486 tcph
->dest
= htons((uint16_t) dport
);
487 tcph
->seq
= htonl(mt_rand_int32());
488 tcph
->ack_seq
= (!!ack
? htonl(mt_rand_int32()) : 0);
498 tcph
->window
= htons((uint16_t) (100 + (mt_rand_int32() % 65435)));
500 tcph
->urg_ptr
= (!!urg
? htons((uint16_t) mt_rand_int32()) : 0);
503 /* returns: ipv4 id */
504 static int assemble_ipv4_tcp(uint8_t *packet
, size_t len
, int ttl
,
505 int tos
, const struct sockaddr
*dst
,
506 const struct sockaddr
*src
, int syn
, int ack
,
507 int urg
, int fin
, int rst
, int psh
, int ecn
,
508 int nofrag
, int dport
, const char *payload
)
510 struct iphdr
*iph
= (struct iphdr
*) packet
;
512 assert(src
->sa_family
== PF_INET
&& dst
->sa_family
== PF_INET
);
513 assert(len
>= sizeof(struct iphdr
) + sizeof(struct tcphdr
));
516 iph
->tos
= (uint8_t) tos
;
517 iph
->tot_len
= htons((uint16_t) len
);
518 iph
->id
= htons((uint16_t) mt_rand_int32());
519 iph
->frag_off
= nofrag
? IP_DF
: 0;
520 iph
->ttl
= (uint8_t) ttl
;
521 iph
->protocol
= 6; /* TCP */
522 iph
->saddr
= ((struct sockaddr_in
*) src
)->sin_addr
.s_addr
;
523 iph
->daddr
= ((struct sockaddr_in
*) dst
)->sin_addr
.s_addr
;
524 assemble_tcp(packet
+ sizeof(struct iphdr
),
525 len
- sizeof(struct iphdr
), syn
, ack
, urg
, fin
, rst
,
527 assemble_data(packet
+ sizeof(struct iphdr
) + sizeof(struct tcphdr
),
528 len
- sizeof(struct iphdr
) - sizeof(struct tcphdr
),
530 iph
->check
= csum((unsigned short *) packet
,
531 ntohs(iph
->tot_len
) >> 1);
532 return ntohs(iph
->id
);
535 /* returns: ipv6 flow label */
536 static int assemble_ipv6_tcp(uint8_t *packet
, size_t len
, int ttl
,
537 struct sockaddr_in
*sin
)
542 static void assemble_icmp4(uint8_t *packet
, size_t len
)
544 struct icmphdr
*icmph
= (struct icmphdr
*) packet
;
545 assert(len
>= sizeof(struct icmphdr
));
546 icmph
->type
= ICMP_ECHO
;
551 /* returns: ipv4 id */
552 static int assemble_ipv4_icmp4(uint8_t *packet
, size_t len
, int ttl
,
553 int tos
, const struct sockaddr
*dst
,
554 const struct sockaddr
*src
, int nofrag
,
557 struct iphdr
*iph
= (struct iphdr
*) packet
;
559 assert(src
->sa_family
== PF_INET
&& dst
->sa_family
== PF_INET
);
560 assert(len
>= sizeof(struct iphdr
) + sizeof(struct tcphdr
));
564 iph
->tot_len
= htons((uint16_t) len
);
565 iph
->id
= htons((uint16_t) mt_rand_int32());
566 iph
->frag_off
= nofrag
? IP_DF
: 0;
567 iph
->ttl
= (uint8_t) ttl
;
568 iph
->protocol
= 1; /* ICMP4 */
569 iph
->saddr
= ((struct sockaddr_in
*) src
)->sin_addr
.s_addr
;
570 iph
->daddr
= ((struct sockaddr_in
*) dst
)->sin_addr
.s_addr
;
571 assemble_icmp4(packet
+ sizeof(struct iphdr
),
572 len
- sizeof(struct iphdr
));
573 assemble_data(packet
+ sizeof(struct iphdr
) + sizeof(struct icmphdr
),
574 len
- sizeof(struct iphdr
) - sizeof(struct icmphdr
),
576 iph
->check
= csum((unsigned short *) packet
,
577 ntohs(iph
->tot_len
) >> 1);
578 return ntohs(iph
->id
);
581 static int assemble_packet_or_die(uint8_t *packet
, size_t len
, int ttl
, int icmp
,
582 const struct ash_cfg
*cfg
,
583 const struct sockaddr
*dst
,
584 const struct sockaddr
*src
)
587 return assemble_ipv4_icmp4(packet
, len
, ttl
, cfg
->tos
, dst
, src
,
588 cfg
->nofrag
, cfg
->payload
);
590 return assemble_ipv4_tcp(packet
, len
, ttl
, cfg
->tos
, dst
, src
,
591 cfg
->syn
, cfg
->ack
, cfg
->urg
, cfg
->fin
,
592 cfg
->rst
, cfg
->psh
, cfg
->ecn
,
593 cfg
->nofrag
, atoi(cfg
->port
),
597 #define PKT_NOT_FOR_US 0
600 static inline const char *make_n_a(const char *p
)
605 static int handle_ipv4_icmp(uint8_t *packet
, size_t len
, int ttl
, int id
,
606 const struct sockaddr
*own
, int dns_resolv
)
609 struct iphdr
*iph
= (struct iphdr
*) packet
;
610 struct iphdr
*iph_inner
;
611 struct icmphdr
*icmph
;
613 struct sockaddr_in sa
;
616 if (iph
->protocol
!= 1)
617 return PKT_NOT_FOR_US
;
618 if (iph
->daddr
!= ((struct sockaddr_in
*) own
)->sin_addr
.s_addr
)
619 return PKT_NOT_FOR_US
;
620 icmph
= (struct icmphdr
*) (packet
+ sizeof(struct iphdr
));
621 if (icmph
->type
!= ICMP_TIME_EXCEEDED
)
622 return PKT_NOT_FOR_US
;
623 if (icmph
->code
!= ICMP_EXC_TTL
)
624 return PKT_NOT_FOR_US
;
625 iph_inner
= (struct iphdr
*) (packet
+ sizeof(struct iphdr
) +
626 sizeof(struct icmphdr
));
627 if (ntohs(iph_inner
->id
) != id
)
628 return PKT_NOT_FOR_US
;
629 hbuff
= xzmalloc(NI_MAXHOST
);
630 memset(&sa
, 0, sizeof(sa
));
631 sa
.sin_family
= PF_INET
;
632 sa
.sin_addr
.s_addr
= iph
->saddr
;
633 getnameinfo((struct sockaddr
*) &sa
, sizeof(sa
), hbuff
, NI_MAXHOST
,
634 NULL
, 0, NI_NUMERICHOST
);
635 memset(&rec
, 0, sizeof(rec
));
636 ret
= aslookup(hbuff
, &rec
);
638 panic("AS lookup error %d!\n", ret
);
639 gir
= GeoIP_record_by_ipnum(gi_city
, ntohl(iph
->saddr
));
641 if (strlen(rec
.country
) > 0 && gir
) {
642 const char *city
= make_n_a(gir
->city
);
643 printf("%s in AS%s (%s, %s, %s, %f, %f), %s %s (%s), %s", hbuff
,
644 rec
.number
, rec
.country
,
645 GeoIP_country_name_by_ipnum(gi_country
, ntohl(iph
->saddr
)),
646 city
, gir
->latitude
, gir
->longitude
,
647 rec
.prefix
, rec
.registry
, rec
.since
, rec
.name
);
648 } else if (strlen(rec
.country
) > 0 && !gir
) {
649 printf("%s in AS%s (%s, %s), %s %s (%s), %s", hbuff
,
650 rec
.number
, rec
.country
,
651 GeoIP_country_name_by_ipnum(gi_country
, ntohl(iph
->saddr
)),
652 rec
.prefix
, rec
.registry
, rec
.since
, rec
.name
);
654 printf("%s in unkown AS", hbuff
);
657 struct hostent
*hent
= gethostbyaddr(&sa
.sin_addr
,
660 if (strlen(rec
.country
) > 0 && gir
) {
661 const char *city
= make_n_a(gir
->city
);
662 printf("%s (%s) in AS%s (%s, %s, %s, %f, %f), %s %s (%s), %s",
663 (hent
? hent
->h_name
: hbuff
), hbuff
,
664 rec
.number
, rec
.country
,
665 GeoIP_country_name_by_ipnum(gi_country
, ntohl(iph
->saddr
)),
666 city
, gir
->latitude
, gir
->longitude
,
667 rec
.prefix
, rec
.registry
,
668 rec
.since
, rec
.name
);
669 } else if (strlen(rec
.country
) > 0 && !gir
) {
670 printf("%s (%s) in AS%s (%s, %s), %s %s (%s), %s",
671 (hent
? hent
->h_name
: hbuff
), hbuff
,
672 rec
.number
, rec
.country
,
673 GeoIP_country_name_by_ipnum(gi_country
, ntohl(iph
->saddr
)),
674 rec
.prefix
, rec
.registry
,
675 rec
.since
, rec
.name
);
677 printf("%s (%s) in unkown AS",
678 (hent
? hent
->h_name
: hbuff
), hbuff
);
685 static int handle_packet(uint8_t *packet
, size_t len
, int ip
, int ttl
, int id
,
686 struct sockaddr
*own
, int dns_resolv
)
688 return handle_ipv4_icmp(packet
, len
, ttl
, id
, own
, dns_resolv
);
691 static int do_trace(const struct ash_cfg
*cfg
)
693 int ttl
, query
, fd
= -1, one
= 1, ret
, fd_cap
, ifindex
;
694 int is_okay
= 0, id
, timeout_poll
;
695 uint8_t *packet
, *packet_rcv
;
696 ssize_t err
, real_len
;
698 struct addrinfo hints
, *ahead
, *ai
;
699 char *hbuff1
, *hbuff2
;
700 struct sockaddr_storage ss
, sd
;
701 struct sock_fprog bpf_ops
;
702 struct ring dummy_ring
;
705 mt_init_by_random_device();
707 memset(&hints
, 0, sizeof(hints
));
708 hints
.ai_family
= PF_UNSPEC
;
709 hints
.ai_socktype
= SOCK_STREAM
;
710 hints
.ai_protocol
= IPPROTO_TCP
;
711 hints
.ai_flags
= AI_NUMERICSERV
;
713 ret
= getaddrinfo(cfg
->host
, cfg
->port
, &hints
, &ahead
);
715 whine("Cannot get address info!\n");
719 for (ai
= ahead
; ai
!= NULL
&& fd
< 0; ai
= ai
->ai_next
) {
720 if (!((ai
->ai_family
== PF_INET6
&& cfg
->ip
== 6) ||
721 (ai
->ai_family
== PF_INET
&& cfg
->ip
== 4)))
723 fd
= socket(ai
->ai_family
, SOCK_RAW
, ai
->ai_protocol
);
726 fd_cap
= pf_socket();
727 memset(&ss
, 0, sizeof(ss
));
728 ret
= device_address(cfg
->dev
, ai
->ai_family
, &ss
);
730 panic("Cannot get own device address!\n");
731 ret
= bind(fd
, (struct sockaddr
*) &ss
, sizeof(ss
));
733 panic("Cannot bind socket!\n");
734 memset(&sd
, 0, sizeof(sd
));
735 memcpy(&sd
, ai
->ai_addr
, ai
->ai_addrlen
);
741 whine("Cannot create socket! Does remote support IPv%d?!\n",
748 if (len
< sizeof(struct iphdr
) + sizeof(struct tcphdr
)) {
749 len
= sizeof(struct iphdr
) + sizeof(struct tcphdr
);
751 len
+= strlen(cfg
->payload
);
754 if (len
< sizeof(struct ip6_hdr
) + sizeof(struct tcphdr
)) {
755 len
= sizeof(struct ip6_hdr
) + sizeof(struct tcphdr
);
757 len
+= strlen(cfg
->payload
);
760 if (len
>= device_mtu(cfg
->dev
))
761 panic("Packet len exceeds device MTU!\n");
763 packet
= xmalloc(len
);
764 len_rcv
= device_mtu(cfg
->dev
);
765 packet_rcv
= xmalloc(len_rcv
);
767 hbuff1
= xzmalloc(256);
768 getnameinfo((struct sockaddr
*) &sd
, sizeof(sd
), hbuff1
, 256,
769 NULL
, 0, NI_NUMERICHOST
);
771 hbuff2
= xzmalloc(256);
772 getnameinfo((struct sockaddr
*) &ss
, sizeof(ss
), hbuff2
, 256,
773 NULL
, 0, NI_NUMERICHOST
);
775 ret
= setsockopt(fd
, cfg
->ip
== 4 ? IPPROTO_IP
: IPPROTO_IPV6
,
776 IP_HDRINCL
, &one
, sizeof(one
));
778 panic("Kernel does not support IP_HDRINCL!\n");
780 info("AS path IPv%d TCP trace from %s to %s:%s (%s) with len %u "
781 "Bytes, %u max hops\n", cfg
->ip
, hbuff2
, hbuff1
, cfg
->port
,
782 cfg
->host
, len
, cfg
->max_ttl
);
783 info("Using flags SYN:%d,ACK:%d,ECN:%d,FIN:%d,PSH:%d,RST:%d,URG:%d\n",
784 cfg
->syn
, cfg
->ack
, cfg
->ecn
, cfg
->fin
, cfg
->psh
, cfg
->rst
,
787 info("With payload: \'%s\'\n", cfg
->payload
);
793 hbuff1
= hbuff2
= NULL
;
795 enable_kernel_bpf_jit_compiler();
796 memset(&bpf_ops
, 0, sizeof(bpf_ops
));
798 bpf_ops
.filter
= ipv4_icmp_type_11
;
799 bpf_ops
.len
= (sizeof(ipv4_icmp_type_11
) /
800 sizeof(ipv4_icmp_type_11
[0]));
802 bpf_ops
.filter
= ipv6_icmp6_type_3
;
803 bpf_ops
.len
= (sizeof(ipv6_icmp6_type_3
) /
804 sizeof(ipv6_icmp6_type_3
[0]));
806 bpf_attach_to_sock(fd_cap
, &bpf_ops
);
807 ifindex
= device_ifindex(cfg
->dev
);
808 bind_rx_ring(fd_cap
, &dummy_ring
, ifindex
);
809 prepare_polling(fd_cap
, &pfd
);
810 timeout_poll
= (cfg
->timeout
> 0 ? cfg
->timeout
: 3) * 1000;
812 for (ttl
= cfg
->init_ttl
; ttl
<= cfg
->max_ttl
; ++ttl
) {
816 if ((ttl
== cfg
->init_ttl
&& !show_pkt
) ||
817 (ttl
> cfg
->init_ttl
)) {
822 for (query
= 0; query
< cfg
->queries
&& !is_okay
; ++query
) {
823 id
= assemble_packet_or_die(packet
, len
, ttl
, icmp
, cfg
,
824 (struct sockaddr
*) &sd
,
825 (struct sockaddr
*) &ss
);
826 if (ttl
== cfg
->init_ttl
&& query
== 0 && show_pkt
) {
827 info("Original packet:\n");
830 info("\n%2d: ", ttl
);
834 err
= sendto(fd
, packet
, len
, 0, (struct sockaddr
*) &sd
,
837 panic("sendto failed: %s\n", strerror(errno
));
839 err
= poll(&pfd
, 1, timeout_poll
);
840 if (err
> 0 && pfd
.revents
& POLLIN
) {
841 real_len
= recvfrom(fd_cap
, packet_rcv
, len_rcv
,
843 if (real_len
< sizeof(struct ethhdr
) +
844 (cfg
->ip
? sizeof(struct iphdr
) +
845 sizeof(struct icmphdr
) :
846 sizeof(struct ip6_hdr
) +
847 sizeof(struct icmp6hdr
)))
850 is_okay
= handle_packet(packet_rcv
+ sizeof(struct ethhdr
),
851 real_len
- sizeof(struct ethhdr
),
853 (struct sockaddr
*) &ss
,
855 if (is_okay
&& show_pkt
) {
856 info("\n Received packet:\n");
857 hex(packet_rcv
, real_len
);
867 if (is_okay
== 0 && icmp
== 0) {
883 void parse_whois_or_die(struct ash_cfg
*cfg
)
887 char tmp
[512], *ptr
, *ptr2
;
889 fd
= open_or_die(WHOIS_SERVER_SOURCE
, O_RDONLY
);
890 while ((ret
= read(fd
, tmp
, sizeof(tmp
))) > 0) {
891 tmp
[sizeof(tmp
) - 1] = 0;
894 while (*ptr2
!= ' ' && ptr2
< &tmp
[sizeof(tmp
) - 1])
897 panic("Parser error!\n");
899 cfg
->whois
= xstrdup(ptr
);
901 if (ptr
>= &tmp
[sizeof(tmp
) - 1])
902 panic("Parser error!\n");
904 ptr
[strlen(ptr
) - 1] = 0;
905 cfg
->whois_port
= xstrdup(ptr
);
911 int main(int argc
, char **argv
)
913 int c
, opt_index
, ret
;
915 char *path_city_db
= NULL
, *path_country_db
= NULL
;
917 check_for_root_maybe_die();
919 memset(&cfg
, 0, sizeof(cfg
));
926 cfg
.dev
= xstrdup("eth0");
927 cfg
.port
= xstrdup("80");
929 while ((c
= getopt_long(argc
, argv
, short_options
, long_options
,
930 &opt_index
)) != EOF
) {
939 cfg
.host
= xstrdup(optarg
);
944 cfg
.port
= xstrdup(optarg
);
962 cfg
.init_ttl
= atoi(optarg
);
963 if (cfg
.init_ttl
<= 0)
967 cfg
.max_ttl
= atoi(optarg
);
968 if (cfg
.max_ttl
<= 0)
975 cfg
.dev
= xstrdup(optarg
);
978 cfg
.queries
= atoi(optarg
);
979 if (cfg
.queries
<= 0)
983 cfg
.timeout
= atoi(optarg
);
984 if (cfg
.timeout
<= 0)
1010 cfg
.tos
= atoi(optarg
);
1018 cfg
.payload
= xstrdup(optarg
);
1021 cfg
.totlen
= atoi(optarg
);
1022 if (cfg
.totlen
<= 0)
1026 cfg
.whois
= xstrdup(optarg
);
1029 cfg
.whois_port
= xstrdup(optarg
);
1032 path_city_db
= xstrdup(optarg
);
1035 path_country_db
= xstrdup(optarg
);
1054 panic("Option -%c requires an argument!\n",
1057 if (isprint(optopt
))
1058 whine("Unknown option character "
1059 "`0x%X\'!\n", optopt
);
1068 !cfg
.host
|| !cfg
.port
||
1069 cfg
.init_ttl
> cfg
.max_ttl
||
1070 cfg
.init_ttl
> MAXTTL
||
1071 cfg
.max_ttl
> MAXTTL
)
1073 if (!device_up_and_running(cfg
.dev
))
1074 panic("Networking device not up and running!\n");
1075 if (!cfg
.whois
|| !cfg
.whois_port
)
1076 parse_whois_or_die(&cfg
);
1077 if (device_mtu(cfg
.dev
) <= cfg
.totlen
)
1078 panic("Packet larger than device MTU!\n");
1079 register_signal(SIGHUP
, signal_handler
);
1082 ret
= aslookup_prepare(cfg
.whois
, cfg
.whois_port
);
1084 panic("Cannot resolve whois server!\n");
1085 if (path_country_db
)
1086 gi_country
= GeoIP_open(path_country_db
, GEOIP_MMAP_CACHE
);
1088 gi_country
= GeoIP_open_type(GEOIP_COUNTRY_EDITION
,
1091 gi_city
= GeoIP_open(path_city_db
, GEOIP_MMAP_CACHE
);
1093 gi_city
= GeoIP_open_type(GEOIP_CITY_EDITION_REV1
,
1095 if (!gi_country
|| !gi_city
)
1096 panic("Cannot open GeoIP database! Wrong path?!\n");
1097 GeoIP_set_charset(gi_country
, GEOIP_CHARSET_UTF8
);
1098 GeoIP_set_charset(gi_city
, GEOIP_CHARSET_UTF8
);
1099 ret
= do_trace(&cfg
);
1100 GeoIP_delete(gi_city
);
1101 GeoIP_delete(gi_country
);
1104 xfree(cfg
.whois_port
);
1116 xfree(path_city_db
);
1117 if (path_country_db
)
1118 xfree(path_country_db
);