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"
236 #include "aslookup.h"
241 #define WHOIS_SERVER_SOURCE "/etc/netsniff-ng/whois.conf"
243 static int assemble_ipv6_tcp(uint8_t *packet
, size_t len
, int ttl
,
244 struct sockaddr_in
*sin
) __attribute__ ((unused
));
255 int syn
, ack
, ecn
, fin
, psh
, rst
, urg
;
264 volatile sig_atomic_t sigint
= 0;
266 static int show_pkt
= 0;
268 static GeoIP
*gi_country
= NULL
;
269 static GeoIP
*gi_city
= NULL
;
271 static const char *short_options
= "H:p:nNf:m:i:d:q:x:SAEFPURt:Gl:w:W:hv46X:ZLK";
273 static struct option long_options
[] = {
274 {"host", required_argument
, 0, 'H'},
275 {"port", required_argument
, 0, 'p'},
276 {"init-ttl", required_argument
, 0, 'f'},
277 {"max-ttl", required_argument
, 0, 'm'},
278 {"numeric", no_argument
, 0, 'n'},
279 {"dns", no_argument
, 0, 'N'},
280 {"ipv4", no_argument
, 0, '4'},
281 {"ipv6", no_argument
, 0, '6'},
282 {"dev", required_argument
, 0, 'd'},
283 {"num-probes", required_argument
, 0, 'q'},
284 {"timeout", required_argument
, 0, 'x'},
285 {"syn", no_argument
, 0, 'S'},
286 {"ack", no_argument
, 0, 'A'},
287 {"urg", no_argument
, 0, 'U'},
288 {"fin", no_argument
, 0, 'F'},
289 {"psh", no_argument
, 0, 'P'},
290 {"rst", no_argument
, 0, 'R'},
291 {"ecn-syn", no_argument
, 0, 'E'},
292 {"tos", required_argument
, 0, 't'},
293 {"payload", required_argument
, 0, 'X'},
294 {"show-packet", no_argument
, 0, 'Z'},
295 {"nofrag", no_argument
, 0, 'G'},
296 {"totlen", required_argument
, 0, 'l'},
297 {"whois", required_argument
, 0, 'w'},
298 {"wport", required_argument
, 0, 'W'},
299 {"city-db", required_argument
, 0, 'L'},
300 {"country-db", required_argument
, 0, 'K'},
301 {"version", no_argument
, 0, 'v'},
302 {"help", no_argument
, 0, 'h'},
306 static struct sock_filter ipv4_icmp_type_11
[] = {
308 { 0x28, 0, 0, 0x0000000c },
309 /* (001) jeq #0x800 jt 2 jf 10 */
310 { 0x15, 0, 8, 0x00000800 },
312 { 0x30, 0, 0, 0x00000017 },
313 /* (003) jeq #0x1 jt 4 jf 10 */
314 { 0x15, 0, 6, 0x00000001 },
316 { 0x28, 0, 0, 0x00000014 },
317 /* (005) jset #0x1fff jt 10 jf 6 */
318 { 0x45, 4, 0, 0x00001fff },
319 /* (006) ldxb 4*([14]&0xf) */
320 { 0xb1, 0, 0, 0x0000000e },
321 /* (007) ldb [x + 14] */
322 { 0x50, 0, 0, 0x0000000e },
323 /* (008) jeq #0xb jt 9 jf 10 */
324 { 0x15, 0, 1, 0x0000000b },
325 /* (009) ret #65535 */
326 { 0x06, 0, 0, 0xffffffff },
328 { 0x06, 0, 0, 0x00000000 },
331 static struct sock_filter ipv6_icmp6_type_3
[] = {
333 { 0x28, 0, 0, 0x0000000c },
334 /* (001) jeq #0x86dd jt 2 jf 7 */
335 { 0x15, 0, 5, 0x000086dd },
337 { 0x30, 0, 0, 0x00000014 },
338 /* (003) jeq #0x3a jt 4 jf 7 */
339 { 0x15, 0, 3, 0x0000003a },
341 { 0x30, 0, 0, 0x00000036 },
342 /* (005) jeq #0x3 jt 6 jf 7 */
343 { 0x15, 0, 1, 0x00000003 },
344 /* (006) ret #65535 */
345 { 0x06, 0, 0, 0xffffffff },
347 { 0x06, 0, 0, 0x00000000 },
350 static void signal_handler(int number
)
361 static void header(void)
363 printf("%s%s%s\n", colorize_start(bold
), "ashunt "
364 VERSION_STRING
, colorize_end());
367 static void help(void)
370 printf("\nashunt %s, Autonomous System (AS) trace route utility\n",
372 printf("http://www.netsniff-ng.org\n\n");
373 printf("Usage: ashunt [options]\n");
374 printf("Options:\n");
375 printf(" -H|--host <host> Host/IPv4/IPv6 to lookup AS route to\n");
376 printf(" -p|--port <port> Hosts port to lookup AS route to\n");
377 printf(" -i|-d|--dev <device> Networking device, i.e. eth0\n");
378 printf(" -4|--ipv4 Use IPv4 requests (default)\n");
379 printf(" -6|--ipv6 Use IPv6 requests\n");
380 printf(" -n|--numeric Do not do reverse DNS lookup for hops\n");
381 printf(" -N|--dns Do a reverse DNS lookup for hops\n");
382 printf(" -f|--init-ttl <ttl> Set initial TTL\n");
383 printf(" -m|--max-ttl <ttl> Set maximum TTL (default: 30)\n");
384 printf(" -q|--num-probes <num> Number of max probes for each hop (default: 3)\n");
385 printf(" -x|--timeout <sec> Probe response timeout in sec (default: 3)\n");
386 printf(" -S|--syn Set TCP SYN flag in packets\n");
387 printf(" -A|--ack Set TCP ACK flag in packets\n");
388 printf(" -F|--fin Set TCP FIN flag in packets\n");
389 printf(" -P|--psh Set TCP PSH flag in packets\n");
390 printf(" -U|--urg Set TCP URG flag in packets\n");
391 printf(" -R|--rst Set TCP RST flag in packets\n");
392 printf(" -E|--ecn-syn Send ECN SYN packets (RFC3168)\n");
393 printf(" -t|--tos <tos> Set the IP TOS field\n");
394 printf(" -G|--nofrag Set do not fragment bit\n");
395 printf(" -X|--payload <string> Specify a payload string to test DPIs\n");
396 printf(" -Z|--show-packet Show returned packet on each hop\n");
397 printf(" -l|--totlen <len> Specify total packet len\n");
398 printf(" -w|--whois <server> Use a different AS whois DB server\n");
399 printf(" (default: /etc/netsniff-ng/whois.conf)\n");
400 printf(" -W|--wport <port> Use a different port to AS whois server\n");
401 printf(" (default: /etc/netsniff-ng/whois.conf)\n");
402 printf(" --city-db <path> Specifiy path for geoip city database\n");
403 printf(" --country-db <path> Specifiy path for geoip country database\n");
404 printf(" -v|--version Print version\n");
405 printf(" -h|--help Print this help\n");
407 printf("Examples:\n");
408 printf(" IPv4 trace of AS with TCP ECN SYN probe:\n");
409 printf(" ashunt -i eth0 -N -E -H netsniff-ng.org\n");
410 printf(" IPv4 trace of AS with TCP SYN probe (this will most-likely pass):\n");
411 printf(" ashunt -i eth0 -N -S -H netsniff-ng.org\n");
412 printf(" IPv4 trace of AS with TCP FIN probe:\n");
413 printf(" ashunt -i eth0 -N -F -H netsniff-ng.org\n");
414 printf(" IPv4 trace of AS with Xmas probe:\n");
415 printf(" ashunt -i eth0 -N -FPU -H netsniff-ng.org\n");
416 printf(" IPv4 trace of AS with Null probe with ASCII payload:\n");
417 printf(" ashunt -i eth0 -N -H netsniff-ng.org -X \"censor-me\" -Z\n");
418 printf(" IPv6 trace of AS up to netsniff-ng.org:\n");
419 printf(" ashunt -6 -S -i eth0 -H netsniff-ng.org\n");
422 printf(" If the TCP probe did not give any results, then ashunt will\n");
423 printf(" automatically probe for classic ICMP packets! To gather more\n");
424 printf(" information about ashunt's fetched AS numbers, see i.e.\n");
425 printf(" http://bgp.he.net/AS<number>!\n");
427 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
428 printf("Copyright (C) 2011-2012 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n");
429 printf("Swiss federal institute of technology (ETH Zurich)\n");
430 printf("License: GNU GPL version 2\n");
431 printf("This is free software: you are free to change and redistribute it.\n");
432 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
436 static void version(void)
438 printf("\nashunt %s, AS trace route utility\n",
440 printf("http://www.netsniff-ng.org\n\n");
441 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
442 printf("Copyright (C) 2011-2012 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n");
443 printf("Swiss federal institute of technology (ETH Zurich)\n");
444 printf("License: GNU GPL version 2\n");
445 printf("This is free software: you are free to change and redistribute it.\n");
446 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
450 static inline unsigned short csum(unsigned short *buf
, int nwords
)
453 for (sum
= 0; nwords
> 0; nwords
--)
455 sum
= (sum
>> 16) + (sum
& 0xffff);
460 static void assemble_data(uint8_t *packet
, size_t len
, const char *payload
)
463 if (payload
== NULL
) {
464 for (i
= 0; i
< len
; ++i
)
465 packet
[i
] = (uint8_t) mt_rand_int32();
467 int lmin
= min(len
, strlen(payload
));
468 for (i
= 0; i
< lmin
; ++i
)
469 packet
[i
] = (uint8_t) payload
[i
];
470 for (i
= lmin
; i
< len
; ++i
)
471 packet
[i
] = (uint8_t) mt_rand_int32();
475 static void assemble_tcp(uint8_t *packet
, size_t len
, int syn
, int ack
,
476 int urg
, int fin
, int rst
, int psh
, int ecn
, int dport
)
478 struct tcphdr
*tcph
= (struct tcphdr
*) packet
;
479 assert(len
>= sizeof(struct tcphdr
));
480 tcph
->source
= htons((uint16_t) mt_rand_int32());
481 tcph
->dest
= htons((uint16_t) dport
);
482 tcph
->seq
= htonl(mt_rand_int32());
483 tcph
->ack_seq
= (!!ack
? htonl(mt_rand_int32()) : 0);
493 tcph
->window
= htons((uint16_t) (100 + (mt_rand_int32() % 65435)));
495 tcph
->urg_ptr
= (!!urg
? htons((uint16_t) mt_rand_int32()) : 0);
498 /* returns: ipv4 id */
499 static int assemble_ipv4_tcp(uint8_t *packet
, size_t len
, int ttl
,
500 int tos
, const struct sockaddr
*dst
,
501 const struct sockaddr
*src
, int syn
, int ack
,
502 int urg
, int fin
, int rst
, int psh
, int ecn
,
503 int nofrag
, int dport
, const char *payload
)
505 struct iphdr
*iph
= (struct iphdr
*) packet
;
507 assert(src
->sa_family
== PF_INET
&& dst
->sa_family
== PF_INET
);
508 assert(len
>= sizeof(struct iphdr
) + sizeof(struct tcphdr
));
511 iph
->tos
= (uint8_t) tos
;
512 iph
->tot_len
= htons((uint16_t) len
);
513 iph
->id
= htons((uint16_t) mt_rand_int32());
514 iph
->frag_off
= nofrag
? IP_DF
: 0;
515 iph
->ttl
= (uint8_t) ttl
;
516 iph
->protocol
= 6; /* TCP */
517 iph
->saddr
= ((const struct sockaddr_in
*) src
)->sin_addr
.s_addr
;
518 iph
->daddr
= ((const struct sockaddr_in
*) dst
)->sin_addr
.s_addr
;
519 assemble_tcp(packet
+ sizeof(struct iphdr
),
520 len
- sizeof(struct iphdr
), syn
, ack
, urg
, fin
, rst
,
522 assemble_data(packet
+ sizeof(struct iphdr
) + sizeof(struct tcphdr
),
523 len
- sizeof(struct iphdr
) - sizeof(struct tcphdr
),
525 iph
->check
= csum((unsigned short *) packet
,
526 ntohs(iph
->tot_len
) >> 1);
527 return ntohs(iph
->id
);
530 /* returns: ipv6 flow label */
531 static int assemble_ipv6_tcp(uint8_t *packet
, size_t len
, int ttl
,
532 struct sockaddr_in
*sin
)
537 static void assemble_icmp4(uint8_t *packet
, size_t len
)
539 struct icmphdr
*icmph
= (struct icmphdr
*) packet
;
540 assert(len
>= sizeof(struct icmphdr
));
541 icmph
->type
= ICMP_ECHO
;
546 /* returns: ipv4 id */
547 static int assemble_ipv4_icmp4(uint8_t *packet
, size_t len
, int ttl
,
548 int tos
, const struct sockaddr
*dst
,
549 const struct sockaddr
*src
, int nofrag
,
552 struct iphdr
*iph
= (struct iphdr
*) packet
;
554 assert(src
->sa_family
== PF_INET
&& dst
->sa_family
== PF_INET
);
555 assert(len
>= sizeof(struct iphdr
) + sizeof(struct tcphdr
));
559 iph
->tot_len
= htons((uint16_t) len
);
560 iph
->id
= htons((uint16_t) mt_rand_int32());
561 iph
->frag_off
= nofrag
? IP_DF
: 0;
562 iph
->ttl
= (uint8_t) ttl
;
563 iph
->protocol
= 1; /* ICMP4 */
564 iph
->saddr
= ((const struct sockaddr_in
*) src
)->sin_addr
.s_addr
;
565 iph
->daddr
= ((const struct sockaddr_in
*) dst
)->sin_addr
.s_addr
;
566 assemble_icmp4(packet
+ sizeof(struct iphdr
),
567 len
- sizeof(struct iphdr
));
568 assemble_data(packet
+ sizeof(struct iphdr
) + sizeof(struct icmphdr
),
569 len
- sizeof(struct iphdr
) - sizeof(struct icmphdr
),
571 iph
->check
= csum((unsigned short *) packet
,
572 ntohs(iph
->tot_len
) >> 1);
573 return ntohs(iph
->id
);
576 static int assemble_packet_or_die(uint8_t *packet
, size_t len
, int ttl
, int icmp
,
577 const struct ash_cfg
*cfg
,
578 const struct sockaddr
*dst
,
579 const struct sockaddr
*src
)
582 return assemble_ipv4_icmp4(packet
, len
, ttl
, cfg
->tos
, dst
, src
,
583 cfg
->nofrag
, cfg
->payload
);
585 return assemble_ipv4_tcp(packet
, len
, ttl
, cfg
->tos
, dst
, src
,
586 cfg
->syn
, cfg
->ack
, cfg
->urg
, cfg
->fin
,
587 cfg
->rst
, cfg
->psh
, cfg
->ecn
,
588 cfg
->nofrag
, atoi(cfg
->port
),
592 #define PKT_NOT_FOR_US 0
595 static inline const char *make_n_a(const char *p
)
600 static int handle_ipv4_icmp(uint8_t *packet
, size_t len
, int ttl
, int id
,
601 const struct sockaddr
*own
, int dns_resolv
)
604 struct iphdr
*iph
= (struct iphdr
*) packet
;
605 struct iphdr
*iph_inner
;
606 struct icmphdr
*icmph
;
608 struct sockaddr_in sa
;
611 if (iph
->protocol
!= 1)
612 return PKT_NOT_FOR_US
;
613 if (iph
->daddr
!= ((const struct sockaddr_in
*) own
)->sin_addr
.s_addr
)
614 return PKT_NOT_FOR_US
;
615 icmph
= (struct icmphdr
*) (packet
+ sizeof(struct iphdr
));
616 if (icmph
->type
!= ICMP_TIME_EXCEEDED
)
617 return PKT_NOT_FOR_US
;
618 if (icmph
->code
!= ICMP_EXC_TTL
)
619 return PKT_NOT_FOR_US
;
620 iph_inner
= (struct iphdr
*) (packet
+ sizeof(struct iphdr
) +
621 sizeof(struct icmphdr
));
622 if (ntohs(iph_inner
->id
) != id
)
623 return PKT_NOT_FOR_US
;
624 hbuff
= xzmalloc(NI_MAXHOST
);
625 memset(&sa
, 0, sizeof(sa
));
626 sa
.sin_family
= PF_INET
;
627 sa
.sin_addr
.s_addr
= iph
->saddr
;
628 getnameinfo((struct sockaddr
*) &sa
, sizeof(sa
), hbuff
, NI_MAXHOST
,
629 NULL
, 0, NI_NUMERICHOST
);
630 memset(&rec
, 0, sizeof(rec
));
631 ret
= aslookup(hbuff
, &rec
);
633 panic("AS lookup error %d!\n", ret
);
634 gir
= GeoIP_record_by_ipnum(gi_city
, ntohl(iph
->saddr
));
636 if (strlen(rec
.country
) > 0 && gir
) {
637 const char *city
= make_n_a(gir
->city
);
638 printf("%s in AS%s (%s, %s, %s, %f, %f), %s %s (%s), %s", hbuff
,
639 rec
.number
, rec
.country
,
640 GeoIP_country_name_by_ipnum(gi_country
, ntohl(iph
->saddr
)),
641 city
, gir
->latitude
, gir
->longitude
,
642 rec
.prefix
, rec
.registry
, rec
.since
, rec
.name
);
643 } else if (strlen(rec
.country
) > 0 && !gir
) {
644 printf("%s in AS%s (%s, %s), %s %s (%s), %s", hbuff
,
645 rec
.number
, rec
.country
,
646 GeoIP_country_name_by_ipnum(gi_country
, ntohl(iph
->saddr
)),
647 rec
.prefix
, rec
.registry
, rec
.since
, rec
.name
);
649 printf("%s in unkown AS", hbuff
);
652 struct hostent
*hent
= gethostbyaddr(&sa
.sin_addr
,
655 if (strlen(rec
.country
) > 0 && gir
) {
656 const char *city
= make_n_a(gir
->city
);
657 printf("%s (%s) in AS%s (%s, %s, %s, %f, %f), %s %s (%s), %s",
658 (hent
? hent
->h_name
: hbuff
), hbuff
,
659 rec
.number
, rec
.country
,
660 GeoIP_country_name_by_ipnum(gi_country
, ntohl(iph
->saddr
)),
661 city
, gir
->latitude
, gir
->longitude
,
662 rec
.prefix
, rec
.registry
,
663 rec
.since
, rec
.name
);
664 } else if (strlen(rec
.country
) > 0 && !gir
) {
665 printf("%s (%s) in AS%s (%s, %s), %s %s (%s), %s",
666 (hent
? hent
->h_name
: hbuff
), hbuff
,
667 rec
.number
, rec
.country
,
668 GeoIP_country_name_by_ipnum(gi_country
, ntohl(iph
->saddr
)),
669 rec
.prefix
, rec
.registry
,
670 rec
.since
, rec
.name
);
672 printf("%s (%s) in unkown AS",
673 (hent
? hent
->h_name
: hbuff
), hbuff
);
680 static int handle_packet(uint8_t *packet
, size_t len
, int ip
, int ttl
, int id
,
681 struct sockaddr
*own
, int dns_resolv
)
683 return handle_ipv4_icmp(packet
, len
, ttl
, id
, own
, dns_resolv
);
686 static int do_trace(const struct ash_cfg
*cfg
)
688 int ttl
, query
, fd
= -1, one
= 1, ret
, fd_cap
, ifindex
;
689 int is_okay
= 0, id
, timeout_poll
;
690 uint8_t *packet
, *packet_rcv
;
691 ssize_t err
, real_len
;
693 struct addrinfo hints
, *ahead
, *ai
;
694 char *hbuff1
, *hbuff2
;
695 struct sockaddr_storage ss
, sd
;
696 struct sock_fprog bpf_ops
;
697 struct ring dummy_ring
;
700 mt_init_by_random_device();
702 memset(&hints
, 0, sizeof(hints
));
703 hints
.ai_family
= PF_UNSPEC
;
704 hints
.ai_socktype
= SOCK_STREAM
;
705 hints
.ai_protocol
= IPPROTO_TCP
;
706 hints
.ai_flags
= AI_NUMERICSERV
;
708 ret
= getaddrinfo(cfg
->host
, cfg
->port
, &hints
, &ahead
);
710 whine("Cannot get address info!\n");
714 for (ai
= ahead
; ai
!= NULL
&& fd
< 0; ai
= ai
->ai_next
) {
715 if (!((ai
->ai_family
== PF_INET6
&& cfg
->ip
== 6) ||
716 (ai
->ai_family
== PF_INET
&& cfg
->ip
== 4)))
718 fd
= socket(ai
->ai_family
, SOCK_RAW
, ai
->ai_protocol
);
721 fd_cap
= pf_socket();
722 memset(&ss
, 0, sizeof(ss
));
723 ret
= device_address(cfg
->dev
, ai
->ai_family
, &ss
);
725 panic("Cannot get own device address!\n");
726 ret
= bind(fd
, (struct sockaddr
*) &ss
, sizeof(ss
));
728 panic("Cannot bind socket!\n");
729 memset(&sd
, 0, sizeof(sd
));
730 memcpy(&sd
, ai
->ai_addr
, ai
->ai_addrlen
);
736 whine("Cannot create socket! Does remote support IPv%d?!\n",
743 if (len
< sizeof(struct iphdr
) + sizeof(struct tcphdr
)) {
744 len
= sizeof(struct iphdr
) + sizeof(struct tcphdr
);
746 len
+= strlen(cfg
->payload
);
749 if (len
< sizeof(struct ip6_hdr
) + sizeof(struct tcphdr
)) {
750 len
= sizeof(struct ip6_hdr
) + sizeof(struct tcphdr
);
752 len
+= strlen(cfg
->payload
);
755 if (len
>= device_mtu(cfg
->dev
))
756 panic("Packet len exceeds device MTU!\n");
758 packet
= xmalloc(len
);
759 len_rcv
= device_mtu(cfg
->dev
);
760 packet_rcv
= xmalloc(len_rcv
);
762 hbuff1
= xzmalloc(256);
763 getnameinfo((struct sockaddr
*) &sd
, sizeof(sd
), hbuff1
, 256,
764 NULL
, 0, NI_NUMERICHOST
);
766 hbuff2
= xzmalloc(256);
767 getnameinfo((struct sockaddr
*) &ss
, sizeof(ss
), hbuff2
, 256,
768 NULL
, 0, NI_NUMERICHOST
);
770 ret
= setsockopt(fd
, cfg
->ip
== 4 ? IPPROTO_IP
: IPPROTO_IPV6
,
771 IP_HDRINCL
, &one
, sizeof(one
));
773 panic("Kernel does not support IP_HDRINCL!\n");
775 printf("AS path IPv%d TCP trace from %s to %s:%s (%s) with len %zu "
776 "Bytes, %u max hops\n", cfg
->ip
, hbuff2
, hbuff1
, cfg
->port
,
777 cfg
->host
, len
, cfg
->max_ttl
);
778 printf("Using flags SYN:%d,ACK:%d,ECN:%d,FIN:%d,PSH:%d,RST:%d,URG:%d\n",
779 cfg
->syn
, cfg
->ack
, cfg
->ecn
, cfg
->fin
, cfg
->psh
, cfg
->rst
,
782 printf("With payload: \'%s\'\n", cfg
->payload
);
788 hbuff1
= hbuff2
= NULL
;
790 enable_kernel_bpf_jit_compiler();
791 memset(&bpf_ops
, 0, sizeof(bpf_ops
));
793 bpf_ops
.filter
= ipv4_icmp_type_11
;
794 bpf_ops
.len
= (sizeof(ipv4_icmp_type_11
) /
795 sizeof(ipv4_icmp_type_11
[0]));
797 bpf_ops
.filter
= ipv6_icmp6_type_3
;
798 bpf_ops
.len
= (sizeof(ipv6_icmp6_type_3
) /
799 sizeof(ipv6_icmp6_type_3
[0]));
801 bpf_attach_to_sock(fd_cap
, &bpf_ops
);
802 ifindex
= device_ifindex(cfg
->dev
);
803 bind_rx_ring(fd_cap
, &dummy_ring
, ifindex
);
804 prepare_polling(fd_cap
, &pfd
);
805 timeout_poll
= (cfg
->timeout
> 0 ? cfg
->timeout
: 3) * 1000;
807 for (ttl
= cfg
->init_ttl
; ttl
<= cfg
->max_ttl
; ++ttl
) {
811 if ((ttl
== cfg
->init_ttl
&& !show_pkt
) ||
812 (ttl
> cfg
->init_ttl
)) {
813 printf("%2d: ", ttl
);
817 for (query
= 0; query
< cfg
->queries
&& !is_okay
; ++query
) {
818 id
= assemble_packet_or_die(packet
, len
, ttl
, icmp
, cfg
,
819 (struct sockaddr
*) &sd
,
820 (struct sockaddr
*) &ss
);
821 if (ttl
== cfg
->init_ttl
&& query
== 0 && show_pkt
) {
822 printf("Original packet:\n");
823 hex_pay(packet
, len
);
825 printf("\n%2d: ", ttl
);
829 err
= sendto(fd
, packet
, len
, 0, (struct sockaddr
*) &sd
,
832 panic("sendto failed: %s\n", strerror(errno
));
834 err
= poll(&pfd
, 1, timeout_poll
);
835 if (err
> 0 && pfd
.revents
& POLLIN
) {
836 real_len
= recvfrom(fd_cap
, packet_rcv
, len_rcv
,
838 if (real_len
< sizeof(struct ethhdr
) +
839 (cfg
->ip
? sizeof(struct iphdr
) +
840 sizeof(struct icmphdr
) :
841 sizeof(struct ip6_hdr
) +
842 sizeof(struct icmp6hdr
)))
845 is_okay
= handle_packet(packet_rcv
+ sizeof(struct ethhdr
),
846 real_len
- sizeof(struct ethhdr
),
848 (struct sockaddr
*) &ss
,
850 if (is_okay
&& show_pkt
) {
851 printf("\n Received packet:\n");
852 hex_pay(packet_rcv
, real_len
);
862 if (is_okay
== 0 && icmp
== 0) {
878 static void parse_whois_or_die(struct ash_cfg
*cfg
)
882 char tmp
[512], *ptr
, *ptr2
;
884 fd
= open_or_die(WHOIS_SERVER_SOURCE
, O_RDONLY
);
885 while ((ret
= read(fd
, tmp
, sizeof(tmp
))) > 0) {
886 tmp
[sizeof(tmp
) - 1] = 0;
889 while (*ptr2
!= ' ' && ptr2
< &tmp
[sizeof(tmp
) - 1])
892 panic("Parser error!\n");
894 cfg
->whois
= xstrdup(ptr
);
896 if (ptr
>= &tmp
[sizeof(tmp
) - 1])
897 panic("Parser error!\n");
899 ptr
[strlen(ptr
) - 1] = 0;
900 cfg
->whois_port
= xstrdup(ptr
);
906 int main(int argc
, char **argv
)
908 int c
, opt_index
, ret
;
910 char *path_city_db
= NULL
, *path_country_db
= NULL
;
912 check_for_root_maybe_die();
914 memset(&cfg
, 0, sizeof(cfg
));
921 cfg
.dev
= xstrdup("eth0");
922 cfg
.port
= xstrdup("80");
924 while ((c
= getopt_long(argc
, argv
, short_options
, long_options
,
925 &opt_index
)) != EOF
) {
934 cfg
.host
= xstrdup(optarg
);
939 cfg
.port
= xstrdup(optarg
);
957 cfg
.init_ttl
= atoi(optarg
);
958 if (cfg
.init_ttl
<= 0)
962 cfg
.max_ttl
= atoi(optarg
);
963 if (cfg
.max_ttl
<= 0)
970 cfg
.dev
= xstrdup(optarg
);
973 cfg
.queries
= atoi(optarg
);
974 if (cfg
.queries
<= 0)
978 cfg
.timeout
= atoi(optarg
);
979 if (cfg
.timeout
<= 0)
1005 cfg
.tos
= atoi(optarg
);
1013 cfg
.payload
= xstrdup(optarg
);
1016 cfg
.totlen
= atoi(optarg
);
1017 if (cfg
.totlen
<= 0)
1021 cfg
.whois
= xstrdup(optarg
);
1024 cfg
.whois_port
= xstrdup(optarg
);
1027 path_city_db
= xstrdup(optarg
);
1030 path_country_db
= xstrdup(optarg
);
1049 panic("Option -%c requires an argument!\n",
1052 if (isprint(optopt
))
1053 whine("Unknown option character "
1054 "`0x%X\'!\n", optopt
);
1063 !cfg
.host
|| !cfg
.port
||
1064 cfg
.init_ttl
> cfg
.max_ttl
||
1065 cfg
.init_ttl
> MAXTTL
||
1066 cfg
.max_ttl
> MAXTTL
)
1068 if (!device_up_and_running(cfg
.dev
))
1069 panic("Networking device not up and running!\n");
1070 if (!cfg
.whois
|| !cfg
.whois_port
)
1071 parse_whois_or_die(&cfg
);
1072 if (device_mtu(cfg
.dev
) <= cfg
.totlen
)
1073 panic("Packet larger than device MTU!\n");
1074 register_signal(SIGHUP
, signal_handler
);
1077 ret
= aslookup_prepare(cfg
.whois
, cfg
.whois_port
);
1079 panic("Cannot resolve whois server!\n");
1080 if (path_country_db
)
1081 gi_country
= GeoIP_open(path_country_db
, GEOIP_MMAP_CACHE
);
1083 gi_country
= GeoIP_open_type(GEOIP_COUNTRY_EDITION
,
1086 gi_city
= GeoIP_open(path_city_db
, GEOIP_MMAP_CACHE
);
1088 gi_city
= GeoIP_open_type(GEOIP_CITY_EDITION_REV1
,
1090 if (!gi_country
|| !gi_city
)
1091 panic("Cannot open GeoIP database! Wrong path?!\n");
1092 GeoIP_set_charset(gi_country
, GEOIP_CHARSET_UTF8
);
1093 GeoIP_set_charset(gi_city
, GEOIP_CHARSET_UTF8
);
1094 ret
= do_trace(&cfg
);
1095 GeoIP_delete(gi_city
);
1096 GeoIP_delete(gi_country
);
1099 xfree(cfg
.whois_port
);
1111 xfree(path_city_db
);
1112 if (path_country_db
)
1113 xfree(path_country_db
);