2 * netsniff-ng - the packet sniffing beast
3 * By Daniel Borkmann <daniel@netsniff-ng.org>
4 * Copyright 2011 Daniel Borkmann.
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.
13 * The road must be trod, but it will be very hard. And neither strength nor
14 * wisdom will carry us far upon it. This quest may be attempted by the weak
15 * with as much hope as the strong. Yet such is oft the course of deeds that
16 * move the wheels of the world: small hands do them because they must,
17 * while the eyes of the great are elsewhere.
19 * -- The Lord of the Rings, Elrond, Chapter 'The Council of Elrond'.
29 #include <sys/types.h>
31 #include <sys/socket.h>
34 #include <asm/byteorder.h>
35 #include <linux/tcp.h>
36 #include <netinet/ip.h>
37 #include <netinet/ip6.h>
38 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <linux/if_ether.h>
43 #include <linux/icmp.h>
44 #include <linux/icmpv6.h>
50 #include "write_or_die.h"
60 #define WHOIS_SERVER_SOURCE "/etc/netsniff-ng/whois.conf"
71 int syn
, ack
, ecn
, fin
, psh
, rst
, urg
;
79 sig_atomic_t sigint
= 0;
81 static const char *short_options
= "H:p:nNf:m:i:d:q:x:SAEFPURt:Gl:w:W:hv46";
83 static struct option long_options
[] = {
84 {"host", required_argument
, 0, 'H'},
85 {"port", required_argument
, 0, 'p'},
86 {"init-ttl", required_argument
, 0, 'f'},
87 {"max-ttl", required_argument
, 0, 'm'},
88 {"numeric", no_argument
, 0, 'n'},
89 {"dns", no_argument
, 0, 'N'},
90 {"ipv4", no_argument
, 0, '4'},
91 {"ipv6", no_argument
, 0, '6'},
92 {"dev", required_argument
, 0, 'd'},
93 {"num-probes", required_argument
, 0, 'q'},
94 {"timeout", required_argument
, 0, 'x'},
95 {"syn", no_argument
, 0, 'S'},
96 {"ack", no_argument
, 0, 'A'},
97 {"urg", no_argument
, 0, 'U'},
98 {"fin", no_argument
, 0, 'F'},
99 {"psh", no_argument
, 0, 'P'},
100 {"rst", no_argument
, 0, 'R'},
101 {"ecn-syn", no_argument
, 0, 'E'},
102 {"tos", required_argument
, 0, 't'},
103 {"nofrag", no_argument
, 0, 'G'},
104 {"totlen", required_argument
, 0, 'l'},
105 {"whois", required_argument
, 0, 'w'},
106 {"wport", required_argument
, 0, 'W'},
107 {"version", no_argument
, 0, 'v'},
108 {"help", no_argument
, 0, 'h'},
112 static struct sock_filter ipv4_icmp_type_11
[] = {
114 { 0x28, 0, 0, 0x0000000c },
115 /* (001) jeq #0x800 jt 2 jf 10 */
116 { 0x15, 0, 8, 0x00000800 },
118 { 0x30, 0, 0, 0x00000017 },
119 /* (003) jeq #0x1 jt 4 jf 10 */
120 { 0x15, 0, 6, 0x00000001 },
122 { 0x28, 0, 0, 0x00000014 },
123 /* (005) jset #0x1fff jt 10 jf 6 */
124 { 0x45, 4, 0, 0x00001fff },
125 /* (006) ldxb 4*([14]&0xf) */
126 { 0xb1, 0, 0, 0x0000000e },
127 /* (007) ldb [x + 14] */
128 { 0x50, 0, 0, 0x0000000e },
129 /* (008) jeq #0xb jt 9 jf 10 */
130 { 0x15, 0, 1, 0x0000000b },
131 /* (009) ret #65535 */
132 { 0x06, 0, 0, 0xffffffff },
134 { 0x06, 0, 0, 0x00000000 },
137 static struct sock_filter ipv6_icmp6_type_3
[] = {
139 { 0x28, 0, 0, 0x0000000c },
140 /* (001) jeq #0x86dd jt 2 jf 7 */
141 { 0x15, 0, 5, 0x000086dd },
143 { 0x30, 0, 0, 0x00000014 },
144 /* (003) jeq #0x3a jt 4 jf 7 */
145 { 0x15, 0, 3, 0x0000003a },
147 { 0x30, 0, 0, 0x00000036 },
148 /* (005) jeq #0x3 jt 6 jf 7 */
149 { 0x15, 0, 1, 0x00000003 },
150 /* (006) ret #65535 */
151 { 0x06, 0, 0, 0xffffffff },
153 { 0x06, 0, 0, 0x00000000 },
156 static void signal_handler(int number
)
167 static void header(void)
169 printf("%s%s%s\n", colorize_start(bold
), "ashunt "
170 VERSION_STRING
, colorize_end());
173 static void help(void)
176 printf("\nashunt %s, AS trace route utility\n",
178 printf("http://www.netsniff-ng.org\n\n");
179 printf("Usage: ashunt [options]\n");
180 printf("Options:\n");
181 printf(" -H|--host <host> Host/IPv4/IPv6 to lookup AS route to\n");
182 printf(" -p|--port <port> Hosts port to lookup AS route to\n");
183 printf(" -i|-d|--dev <device> Networking device, i.e. eth0\n");
184 printf(" -4|--ipv4 Use IPv4 requests (default)\n");
185 printf(" -6|--ipv6 Use IPv6 requests\n");
186 printf(" -n|--numeric Do not do reverse DNS lookup for hops\n");
187 printf(" -N|--dns Do a reverse DNS lookup for hops\n");
188 printf(" -f|--init-ttl <ttl> Set initial TTL\n");
189 printf(" -m|--max-ttl <ttl> Set maximum TTL (default: 30)\n");
190 printf(" -q|--num-probes <num> Number of max probes for each hop (default: 3)\n");
191 printf(" -x|--timeout <sec> Probe response timeout in sec (default: 3)\n");
192 printf(" -S|--syn Set TCP SYN flag in packets\n");
193 printf(" -A|--ack Set TCP ACK flag in packets\n");
194 printf(" -F|--fin Set TCP FIN flag in packets\n");
195 printf(" -P|--psh Set TCP PSH flag in packets\n");
196 printf(" -U|--urg Set TCP URG flag in packets\n");
197 printf(" -R|--rst Set TCP RST flag in packets\n");
198 printf(" -E|--ecn-syn Send ECN SYN packets (RFC3168)\n");
199 printf(" -t|--tos <tos> Set the IP TOS field\n");
200 printf(" -G|--nofrag Set do not fragment bit\n");
201 printf(" -l|--totlen <len> Specify total packet len\n");
202 printf(" -w|--whois <server> Use a different AS whois DB server\n");
203 printf(" (default: /etc/netsniff-ng/whois.conf)\n");
204 printf(" -W|--wport <port> Use a different port to AS whois server\n");
205 printf(" (default: /etc/netsniff-ng/whois.conf)\n");
206 printf(" -v|--version Print version\n");
207 printf(" -h|--help Print this help\n");
209 printf("Examples:\n");
210 printf(" IPv4 trace of AS up to netsniff-ng.org:\n");
211 printf(" ashunt -i eth0 -N -E -H netsniff-ng.org\n");
212 printf(" IPv6 trace of AS up to netsniff-ng.org:\n");
213 printf(" ashunt -6 -i eth0 -H netsniff-ng.org\n");
215 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
216 printf("Copyright (C) 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n");
217 printf("Swiss federal institute of technology (ETH Zurich)\n");
218 printf("License: GNU GPL version 2\n");
219 printf("This is free software: you are free to change and redistribute it.\n");
220 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
225 static void version(void)
227 printf("\nashunt %s, AS trace route utility\n",
229 printf("http://www.netsniff-ng.org\n\n");
230 printf("Please report bugs to <bugs@netsniff-ng.org>\n");
231 printf("Copyright (C) 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n");
232 printf("Swiss federal institute of technology (ETH Zurich)\n");
233 printf("License: GNU GPL version 2\n");
234 printf("This is free software: you are free to change and redistribute it.\n");
235 printf("There is NO WARRANTY, to the extent permitted by law.\n\n");
240 static inline unsigned short csum(unsigned short *buf
, int nwords
)
243 for (sum
= 0; nwords
> 0; nwords
--)
245 sum
= (sum
>> 16) + (sum
& 0xffff);
250 static void assemble_data(uint8_t *packet
, size_t len
)
253 for (i
= 0; i
< len
; ++i
)
254 packet
[i
] = (uint8_t) mt_rand_int32();
257 static void assemble_tcp(uint8_t *packet
, size_t len
, int syn
, int ack
,
258 int urg
, int fin
, int rst
, int psh
, int ecn
, int dport
)
260 struct tcphdr
*tcph
= (struct tcphdr
*) packet
;
261 assert(len
>= sizeof(struct tcphdr
));
262 tcph
->source
= htons((uint16_t) mt_rand_int32());
263 tcph
->dest
= htons((uint16_t) dport
);
264 tcph
->seq
= htonl(mt_rand_int32());
265 tcph
->ack_seq
= (!!ack
? htonl(mt_rand_int32()) : 0);
275 tcph
->window
= htons((uint16_t) (100 + (mt_rand_int32() % 65435)));
277 tcph
->urg_ptr
= (!!urg
? htons((uint16_t) mt_rand_int32()) : 0);
280 /* returns: ipv4 id */
281 static int assemble_ipv4_tcp(uint8_t *packet
, size_t len
, int ttl
,
282 int tos
, const struct sockaddr
*dst
,
283 const struct sockaddr
*src
, int syn
, int ack
,
284 int urg
, int fin
, int rst
, int psh
, int ecn
,
285 int nofrag
, int dport
)
287 struct iphdr
*iph
= (struct iphdr
*) packet
;
289 assert(src
->sa_family
== PF_INET
&& dst
->sa_family
== PF_INET
);
290 assert(len
>= sizeof(struct iphdr
) + sizeof(struct tcphdr
));
293 iph
->tos
= (uint8_t) tos
;
294 iph
->tot_len
= htons((uint16_t) len
);
295 iph
->id
= htons((uint16_t) mt_rand_int32());
296 iph
->frag_off
= nofrag
? IP_DF
: 0;
297 iph
->ttl
= (uint8_t) ttl
;
298 iph
->protocol
= 6; /* TCP */
299 iph
->saddr
= ((struct sockaddr_in
*) src
)->sin_addr
.s_addr
;
300 iph
->daddr
= ((struct sockaddr_in
*) dst
)->sin_addr
.s_addr
;
301 assemble_tcp(packet
+ sizeof(struct iphdr
),
302 len
- sizeof(struct iphdr
), syn
, ack
, urg
, fin
, rst
,
304 assemble_data(packet
+ sizeof(struct iphdr
) + sizeof(struct tcphdr
),
305 len
- sizeof(struct iphdr
) - sizeof(struct tcphdr
));
306 iph
->check
= csum((unsigned short *) packet
,
307 ntohs(iph
->tot_len
) >> 1);
308 return ntohs(iph
->id
);
311 /* returns: ipv6 flow label */
312 static int assemble_ipv6_tcp(uint8_t *packet
, size_t len
, int ttl
,
313 struct sockaddr_in
*sin
)
318 static int assemble_packet_or_die(uint8_t *packet
, size_t len
, int ttl
,
319 const struct ash_cfg
*cfg
,
320 const struct sockaddr
*dst
,
321 const struct sockaddr
*src
)
323 return assemble_ipv4_tcp(packet
, len
, ttl
, cfg
->tos
, dst
, src
, cfg
->syn
,
324 cfg
->ack
, cfg
->urg
, cfg
->fin
, cfg
->rst
,
325 cfg
->psh
, cfg
->ecn
, cfg
->nofrag
, atoi(cfg
->port
));
328 #define PKT_NOT_FOR_US 0
331 static int handle_ipv4_icmp(uint8_t *packet
, size_t len
, int ttl
, int id
,
332 const struct sockaddr
*own
, int dns_resolv
)
335 struct iphdr
*iph
= (struct iphdr
*) packet
;
336 struct iphdr
*iph_inner
;
337 struct icmphdr
*icmph
;
339 struct sockaddr_in sa
;
342 if (iph
->protocol
!= 1)
343 return PKT_NOT_FOR_US
;
344 if (iph
->daddr
!= ((struct sockaddr_in
*) own
)->sin_addr
.s_addr
)
345 return PKT_NOT_FOR_US
;
346 icmph
= (struct icmphdr
*) (packet
+ sizeof(struct iphdr
));
347 if (icmph
->type
!= ICMP_TIME_EXCEEDED
)
348 return PKT_NOT_FOR_US
;
349 if (icmph
->code
!= ICMP_EXC_TTL
)
350 return PKT_NOT_FOR_US
;
351 iph_inner
= (struct iphdr
*) (packet
+ sizeof(struct iphdr
) +
352 sizeof(struct icmphdr
));
353 if (ntohs(iph_inner
->id
) != id
)
354 return PKT_NOT_FOR_US
;
356 hbuff
= xzmalloc(NI_MAXHOST
);
357 memset(&sa
, 0, sizeof(sa
));
359 sa
.sin_family
= PF_INET
;
360 sa
.sin_addr
.s_addr
= iph
->saddr
;
361 getnameinfo((struct sockaddr
*) &sa
, sizeof(sa
), hbuff
, NI_MAXHOST
,
362 NULL
, 0, NI_NUMERICHOST
);
364 memset(&rec
, 0, sizeof(rec
));
365 ret
= aslookup(hbuff
, &rec
);
367 panic("AS lookup error %d!\n", ret
);
370 if (strlen(rec
.country
) > 0) {
371 printf("%s in AS%s (%s), %s %s (%s), %s", hbuff
,
372 rec
.number
, rec
.country
, rec
.prefix
,
373 rec
.registry
, rec
.since
, rec
.name
);
375 printf("%s in unkown AS", hbuff
);
378 struct hostent
*hent
= gethostbyaddr(&sa
.sin_addr
,
381 if (hent
!= NULL
&& strlen(rec
.country
) > 0) {
382 printf("%s (%s) in AS%s (%s), %s %s (%s), %s",
383 hent
->h_name
, hbuff
, rec
.number
, rec
.country
,
384 rec
.prefix
, rec
.registry
, rec
.since
, rec
.name
);
386 printf("%s in unkown AS", hbuff
);
394 static int handle_packet(uint8_t *packet
, size_t len
, int ip
, int ttl
, int id
,
395 struct sockaddr
*own
, int dns_resolv
)
397 return handle_ipv4_icmp(packet
, len
, ttl
, id
, own
, dns_resolv
);
400 static int do_trace(const struct ash_cfg
*cfg
)
402 int ttl
, query
, fd
= -1, one
= 1, ret
, fd_cap
, ifindex
;
403 int is_okay
= 0, id
, timeout_poll
;
404 uint8_t *packet
, *packet_rcv
;
405 ssize_t err
, real_len
;
407 struct addrinfo hints
, *ahead
, *ai
;
408 char *hbuff1
, *hbuff2
;
409 struct sockaddr_storage ss
, sd
;
410 struct sock_fprog bpf_ops
;
411 struct ring dummy_ring
;
414 mt_init_by_random_device();
416 memset(&hints
, 0, sizeof(hints
));
417 hints
.ai_family
= PF_UNSPEC
;
418 hints
.ai_socktype
= SOCK_STREAM
;
419 hints
.ai_protocol
= IPPROTO_TCP
;
420 hints
.ai_flags
= AI_NUMERICSERV
;
422 ret
= getaddrinfo(cfg
->host
, cfg
->port
, &hints
, &ahead
);
424 whine("Cannot get address info!\n");
428 for (ai
= ahead
; ai
!= NULL
&& fd
< 0; ai
= ai
->ai_next
) {
429 if (!((ai
->ai_family
== PF_INET6
&& cfg
->ip
== 6) ||
430 (ai
->ai_family
== PF_INET
&& cfg
->ip
== 4)))
432 fd
= socket(ai
->ai_family
, SOCK_RAW
, ai
->ai_protocol
);
435 fd_cap
= pf_socket();
436 memset(&ss
, 0, sizeof(ss
));
437 ret
= device_address(cfg
->dev
, ai
->ai_family
, &ss
);
439 panic("Cannot get own device address!\n");
440 ret
= bind(fd
, (struct sockaddr
*) &ss
, sizeof(ss
));
442 panic("Cannot bind socket!\n");
443 memset(&sd
, 0, sizeof(sd
));
444 memcpy(&sd
, ai
->ai_addr
, ai
->ai_addrlen
);
450 whine("Cannot create socket! Does remote support IPv%d?!\n",
457 if (len
< sizeof(struct iphdr
) + sizeof(struct tcphdr
))
458 len
= sizeof(struct iphdr
) + sizeof(struct tcphdr
);
460 if (len
< sizeof(struct ip6_hdr
) + sizeof(struct tcphdr
))
461 len
= sizeof(struct ip6_hdr
) + sizeof(struct tcphdr
);
463 if (len
>= device_mtu(cfg
->dev
))
464 panic("Packet len exceeds device MTU!\n");
466 packet
= xmalloc(len
);
467 len_rcv
= device_mtu(cfg
->dev
);
468 packet_rcv
= xmalloc(len_rcv
);
470 hbuff1
= xzmalloc(256);
471 getnameinfo((struct sockaddr
*) &sd
, sizeof(sd
), hbuff1
, 256,
472 NULL
, 0, NI_NUMERICHOST
);
474 hbuff2
= xzmalloc(256);
475 getnameinfo((struct sockaddr
*) &ss
, sizeof(ss
), hbuff2
, 256,
476 NULL
, 0, NI_NUMERICHOST
);
478 ret
= setsockopt(fd
, cfg
->ip
== 4 ? IPPROTO_IP
: IPPROTO_IPV6
,
479 IP_HDRINCL
, &one
, sizeof(one
));
481 panic("Kernel does not support IP_HDRINCL!\n");
483 info("AS path IPv%d TCP trace from %s to %s:%s (%s) with len %u "
484 "Bytes, %u max hops\n", cfg
->ip
, hbuff2
, hbuff1
, cfg
->port
,
485 cfg
->host
, len
, cfg
->max_ttl
);
486 info("Using flags SYN:%d,ACK:%d,ECN:%d,FIN:%d,PSH:%d,RST:%d,URG:%d\n",
487 cfg
->syn
, cfg
->ack
, cfg
->ecn
, cfg
->fin
, cfg
->psh
, cfg
->rst
,
493 hbuff1
= hbuff2
= NULL
;
495 memset(&bpf_ops
, 0, sizeof(bpf_ops
));
497 bpf_ops
.filter
= ipv4_icmp_type_11
;
498 bpf_ops
.len
= (sizeof(ipv4_icmp_type_11
) /
499 sizeof(ipv4_icmp_type_11
[0]));
501 bpf_ops
.filter
= ipv6_icmp6_type_3
;
502 bpf_ops
.len
= (sizeof(ipv6_icmp6_type_3
) /
503 sizeof(ipv6_icmp6_type_3
[0]));
505 bpf_attach_to_sock(fd_cap
, &bpf_ops
);
506 enable_kernel_bpf_jit_compiler();
507 ifindex
= device_ifindex(cfg
->dev
);
508 bind_rx_ring(fd_cap
, &dummy_ring
, ifindex
);
509 prepare_polling(fd_cap
, &pfd
);
510 timeout_poll
= (cfg
->timeout
> 0 ? cfg
->timeout
: 3) * 1000;
512 for (ttl
= cfg
->init_ttl
; ttl
<= cfg
->max_ttl
; ++ttl
) {
516 for (query
= 0; query
< cfg
->queries
&& !is_okay
; ++query
) {
517 id
= assemble_packet_or_die(packet
, len
, ttl
, cfg
,
518 (struct sockaddr
*) &sd
,
519 (struct sockaddr
*) &ss
);
521 err
= sendto(fd
, packet
, len
, 0, (struct sockaddr
*) &sd
,
524 panic("sendto failed: %s\n", strerror(errno
));
526 err
= poll(&pfd
, 1, timeout_poll
);
527 if (err
> 0 && pfd
.revents
& POLLIN
) {
528 real_len
= recvfrom(fd_cap
, packet_rcv
, len_rcv
,
530 if (real_len
< sizeof(struct ethhdr
) +
531 (cfg
->ip
? sizeof(struct iphdr
) +
532 sizeof(struct icmphdr
) :
533 sizeof(struct ip6_hdr
) +
534 sizeof(struct icmp6hdr
)))
537 is_okay
= handle_packet(packet_rcv
+ sizeof(struct ethhdr
),
538 real_len
- sizeof(struct ethhdr
),
540 (struct sockaddr
*) &ss
,
559 void parse_whois_or_die(struct ash_cfg
*cfg
)
563 char tmp
[512], *ptr
, *ptr2
;
565 fd
= open_or_die(WHOIS_SERVER_SOURCE
, O_RDONLY
);
566 while ((ret
= read(fd
, tmp
, sizeof(tmp
))) > 0) {
567 tmp
[sizeof(tmp
) - 1] = 0;
570 while (*ptr2
!= ' ' && ptr2
< &tmp
[sizeof(tmp
) - 1])
573 panic("Parser error!\n");
575 cfg
->whois
= xstrdup(ptr
);
577 if (ptr
>= &tmp
[sizeof(tmp
) - 1])
578 panic("Parser error!\n");
580 ptr
[strlen(ptr
) - 1] = 0;
581 cfg
->whois_port
= xstrdup(ptr
);
587 int main(int argc
, char **argv
)
589 int c
, opt_index
, ret
;
592 check_for_root_maybe_die();
594 memset(&cfg
, 0, sizeof(cfg
));
600 cfg
.dev
= xstrdup("eth0");
601 cfg
.port
= xstrdup("80");
603 while ((c
= getopt_long(argc
, argv
, short_options
, long_options
,
604 &opt_index
)) != EOF
) {
613 cfg
.host
= xstrdup(optarg
);
618 cfg
.port
= xstrdup(optarg
);
633 cfg
.init_ttl
= atoi(optarg
);
634 if (cfg
.init_ttl
<= 0)
638 cfg
.max_ttl
= atoi(optarg
);
639 if (cfg
.max_ttl
<= 0)
646 cfg
.dev
= xstrdup(optarg
);
649 cfg
.queries
= atoi(optarg
);
650 if (cfg
.queries
<= 0)
654 cfg
.timeout
= atoi(optarg
);
655 if (cfg
.timeout
<= 0)
681 cfg
.tos
= atoi(optarg
);
689 cfg
.totlen
= atoi(optarg
);
694 cfg
.whois
= xstrdup(optarg
);
697 cfg
.whois_port
= xstrdup(optarg
);
713 panic("Option -%c requires an argument!\n",
717 whine("Unknown option character "
718 "`0x%X\'!\n", optopt
);
727 !cfg
.host
|| !cfg
.port
||
728 cfg
.init_ttl
> cfg
.max_ttl
||
729 cfg
.init_ttl
> MAXTTL
||
730 cfg
.max_ttl
> MAXTTL
)
732 if (!device_up_and_running(cfg
.dev
))
733 panic("Networking device not up and running!\n");
734 if (!cfg
.whois
|| !cfg
.whois_port
)
735 parse_whois_or_die(&cfg
);
736 if (device_mtu(cfg
.dev
) <= cfg
.totlen
)
737 panic("Packet larger than device MTU!\n");
738 if (!cfg
.syn
&& !cfg
.ack
&& !cfg
.fin
&& !cfg
.urg
&& !cfg
.psh
&&
739 !cfg
.ecn
&& !cfg
.rst
)
741 register_signal(SIGHUP
, signal_handler
);
744 ret
= aslookup_prepare(cfg
.whois
, cfg
.whois_port
);
746 panic("Cannot resolve whois server!\n");
747 ret
= do_trace(&cfg
);
750 xfree(cfg
.whois_port
);