docs: add todo's from Sibir's repo
[netsniff-ng.git] / src / astraceroute.c
blob0e0bf7e29f792bdb710c5708b09c7821a974e27f
1 /*
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, astraceroute was
12 * written from scratch and does not use any libraries. Special thanks to
13 * Team CYMRU!
15 * The road must be trod, but it will be very hard. And neither strength nor
16 * wisdom will carry us far upon it. This quest may be attempted by the weak
17 * with as much hope as the strong. Yet such is oft the course of deeds that
18 * move the wheels of the world: small hands do them because they must,
19 * while the eyes of the great are elsewhere.
21 * -- The Lord of the Rings, Elrond, Chapter 'The Council of Elrond'.
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <signal.h>
27 #include <getopt.h>
28 #include <ctype.h>
29 #include <stdint.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/socket.h>
33 #include <fcntl.h>
34 #include <string.h>
35 #include <asm/byteorder.h>
36 #include <linux/tcp.h>
37 #include <netinet/ip.h>
38 #include <netinet/ip6.h>
39 #include <netinet/in.h>
40 #include <errno.h>
41 #include <netdb.h>
42 #include <arpa/inet.h>
43 #include <linux/if_ether.h>
44 #include <linux/icmp.h>
45 #include <linux/icmpv6.h>
46 #include <GeoIP.h>
47 #include <GeoIPCity.h>
49 #include "bpf.h"
50 #include "die.h"
51 #include "tprintf.h"
52 #include "pkt_buff.h"
53 #include "proto.h"
54 #include "xmalloc.h"
55 #include "xio.h"
56 #include "aslookup.h"
57 #include "xutils.h"
58 #include "mtrand.h"
59 #include "ring_rx.h"
60 #include "built_in.h"
62 #define WHOIS_SERVER_SOURCE "/etc/netsniff-ng/whois.conf"
64 struct ash_cfg {
65 char *host;
66 char *port;
67 int init_ttl;
68 int max_ttl;
69 int dns_resolv;
70 char *dev;
71 int queries;
72 int timeout;
73 int syn, ack, ecn, fin, psh, rst, urg;
74 int tos, nofrag;
75 int totlen;
76 char *whois;
77 char *whois_port;
78 int ip;
79 char *payload;
82 volatile sig_atomic_t sigint = 0;
84 static int show_pkt = 0;
86 static GeoIP *gi_country = NULL;
87 static GeoIP *gi_city = NULL;
89 static const char *short_options = "H:p:nNf:m:i:d:q:x:SAEFPURt:Gl:w:W:hv46X:ZLK";
90 static const struct option long_options[] = {
91 {"host", required_argument, NULL, 'H'},
92 {"port", required_argument, NULL, 'p'},
93 {"init-ttl", required_argument, NULL, 'f'},
94 {"max-ttl", required_argument, NULL, 'm'},
95 {"dev", required_argument, NULL, 'd'},
96 {"num-probes", required_argument, NULL, 'q'},
97 {"timeout", required_argument, NULL, 'x'},
98 {"tos", required_argument, NULL, 't'},
99 {"payload", required_argument, NULL, 'X'},
100 {"totlen", required_argument, NULL, 'l'},
101 {"whois", required_argument, NULL, 'w'},
102 {"wport", required_argument, NULL, 'W'},
103 {"city-db", required_argument, NULL, 'L'},
104 {"country-db", required_argument, NULL, 'K'},
105 {"numeric", no_argument, NULL, 'n'},
106 {"dns", no_argument, NULL, 'N'},
107 {"ipv4", no_argument, NULL, '4'},
108 {"ipv6", no_argument, NULL, '6'},
109 {"syn", no_argument, NULL, 'S'},
110 {"ack", no_argument, NULL, 'A'},
111 {"urg", no_argument, NULL, 'U'},
112 {"fin", no_argument, NULL, 'F'},
113 {"psh", no_argument, NULL, 'P'},
114 {"rst", no_argument, NULL, 'R'},
115 {"ecn-syn", no_argument, NULL, 'E'},
116 {"show-packet", no_argument, NULL, 'Z'},
117 {"nofrag", no_argument, NULL, 'G'},
118 {"version", no_argument, NULL, 'v'},
119 {"help", no_argument, NULL, 'h'},
120 {NULL, 0, NULL, 0}
123 static struct sock_filter ipv4_icmp_type_11[] = {
124 /* (000) ldh [12] */
125 { 0x28, 0, 0, 0x0000000c },
126 /* (001) jeq #0x800 jt 2 jf 10 */
127 { 0x15, 0, 8, 0x00000800 },
128 /* (002) ldb [23] */
129 { 0x30, 0, 0, 0x00000017 },
130 /* (003) jeq #0x1 jt 4 jf 10 */
131 { 0x15, 0, 6, 0x00000001 },
132 /* (004) ldh [20] */
133 { 0x28, 0, 0, 0x00000014 },
134 /* (005) jset #0x1fff jt 10 jf 6 */
135 { 0x45, 4, 0, 0x00001fff },
136 /* (006) ldxb 4*([14]&0xf) */
137 { 0xb1, 0, 0, 0x0000000e },
138 /* (007) ldb [x + 14] */
139 { 0x50, 0, 0, 0x0000000e },
140 /* (008) jeq #0xb jt 9 jf 10 */
141 { 0x15, 0, 1, 0x0000000b },
142 /* (009) ret #65535 */
143 { 0x06, 0, 0, 0xffffffff },
144 /* (010) ret #0 */
145 { 0x06, 0, 0, 0x00000000 },
148 static struct sock_filter ipv6_icmp6_type_3[] = {
149 /* (000) ldh [12] */
150 { 0x28, 0, 0, 0x0000000c },
151 /* (001) jeq #0x86dd jt 2 jf 7 */
152 { 0x15, 0, 5, 0x000086dd },
153 /* (002) ldb [20] */
154 { 0x30, 0, 0, 0x00000014 },
155 /* (003) jeq #0x3a jt 4 jf 7 */
156 { 0x15, 0, 3, 0x0000003a },
157 /* (004) ldb [54] */
158 { 0x30, 0, 0, 0x00000036 },
159 /* (005) jeq #0x3 jt 6 jf 7 */
160 { 0x15, 0, 1, 0x00000003 },
161 /* (006) ret #65535 */
162 { 0x06, 0, 0, 0xffffffff },
163 /* (007) ret #0 */
164 { 0x06, 0, 0, 0x00000000 },
167 #define PKT_NOT_FOR_US 0
168 #define PKT_GOOD 1
170 static inline const char *make_n_a(const char *p)
172 return p ? : "N/A";
175 static void signal_handler(int number)
177 switch (number) {
178 case SIGINT:
179 sigint = 1;
180 break;
181 default:
182 break;
186 static void header(void)
188 printf("%s%s%s\n", colorize_start(bold), "astraceroute "
189 VERSION_STRING, colorize_end());
192 static void help(void)
195 printf("\nastraceroute %s, autonomous system trace route utility\n",
196 VERSION_STRING);
197 puts("http://www.netsniff-ng.org\n\n"
198 "Usage: astraceroute [options]\n"
199 "Options:\n"
200 " -H|--host <host> Host/IPv4/IPv6 to lookup AS route to\n"
201 " -p|--port <port> Hosts port to lookup AS route to\n"
202 " -i|-d|--dev <device> Networking device, i.e. eth0\n"
203 " -4|--ipv4 Use IPv4 requests (default)\n"
204 " -6|--ipv6 Use IPv6 requests\n"
205 " -n|--numeric Do not do reverse DNS lookup for hops\n"
206 " -N|--dns Do a reverse DNS lookup for hops\n"
207 " -f|--init-ttl <ttl> Set initial TTL\n"
208 " -m|--max-ttl <ttl> Set maximum TTL (default: 30)\n"
209 " -q|--num-probes <num> Number of max probes for each hop (default: 3)\n"
210 " -x|--timeout <sec> Probe response timeout in sec (default: 3)\n"
211 " -S|--syn Set TCP SYN flag in packets\n"
212 " -A|--ack Set TCP ACK flag in packets\n"
213 " -F|--fin Set TCP FIN flag in packets\n"
214 " -P|--psh Set TCP PSH flag in packets\n"
215 " -U|--urg Set TCP URG flag in packets\n"
216 " -R|--rst Set TCP RST flag in packets\n"
217 " -E|--ecn-syn Send ECN SYN packets (RFC3168)\n"
218 " -t|--tos <tos> Set the IP TOS field\n"
219 " -G|--nofrag Set do not fragment bit\n"
220 " -X|--payload <string> Specify a payload string to test DPIs\n"
221 " -Z|--show-packet Show returned packet on each hop\n"
222 " -l|--totlen <len> Specify total packet len\n"
223 " -w|--whois <server> Use a different AS whois DB server\n"
224 " (default: /etc/netsniff-ng/whois.conf)\n"
225 " -W|--wport <port> Use a different port to AS whois server\n"
226 " (default: /etc/netsniff-ng/whois.conf)\n"
227 " --city-db <path> Specifiy path for geoip city database\n"
228 " --country-db <path> Specifiy path for geoip country database\n"
229 " -v|--version Print version\n"
230 " -h|--help Print this help\n\n"
231 "Examples:\n"
232 " IPv4 trace of AS with TCP SYN probe (this will most-likely pass):\n"
233 " astraceroute -i eth0 -N -S -H netsniff-ng.org\n"
234 " IPv4 trace of AS with TCP ECN SYN probe:\n"
235 " astraceroute -i eth0 -N -E -H netsniff-ng.org\n"
236 " IPv4 trace of AS with TCP FIN probe:\n"
237 " astraceroute -i eth0 -N -F -H netsniff-ng.org\n"
238 " IPv4 trace of AS with Xmas probe:\n"
239 " astraceroute -i eth0 -N -FPU -H netsniff-ng.org\n"
240 " IPv4 trace of AS with Null probe with ASCII payload:\n"
241 " astraceroute -i eth0 -N -H netsniff-ng.org -X \"censor-me\" -Z\n"
242 " IPv6 trace of AS up to www.6bone.net:\n"
243 " astraceroute -6 -i eth0 -S -E -N -H www.6bone.net\n\n"
244 "Note:\n"
245 " If the TCP probe did not give any results, then astraceroute will\n"
246 " automatically probe for classic ICMP packets! To gather more\n"
247 " information about astraceroute's fetched AS numbers, see i.e.\n"
248 " http://bgp.he.net/AS<number>!\n\n"
249 "Please report bugs to <bugs@netsniff-ng.org>\n"
250 "Copyright (C) 2011-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
251 "Swiss federal institute of technology (ETH Zurich)\n"
252 "License: GNU GPL version 2.0\n"
253 "This is free software: you are free to change and redistribute it.\n"
254 "There is NO WARRANTY, to the extent permitted by law.\n");
255 die();
258 static void version(void)
260 printf("\nastraceroute %s, autonomous system trace route utility\n",
261 VERSION_STRING);
262 puts("http://www.netsniff-ng.org\n\n"
263 "Please report bugs to <bugs@netsniff-ng.org>\n"
264 "Copyright (C) 2011-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
265 "Swiss federal institute of technology (ETH Zurich)\n"
266 "License: GNU GPL version 2.0\n"
267 "This is free software: you are free to change and redistribute it.\n"
268 "There is NO WARRANTY, to the extent permitted by law.\n");
269 die();
272 static inline unsigned short csum(unsigned short *buf, int nwords)
274 unsigned long sum;
276 for (sum = 0; nwords > 0; nwords--)
277 sum += *buf++;
278 sum = (sum >> 16) + (sum & 0xffff);
279 sum += (sum >> 16);
281 return ~sum;
284 static void assemble_data(uint8_t *packet, size_t len, const char *payload)
286 int i;
288 if (payload == NULL) {
289 for (i = 0; i < len; ++i)
290 packet[i] = (uint8_t) mt_rand_int32();
291 } else {
292 int lmin = min(len, strlen(payload));
293 for (i = 0; i < lmin; ++i)
294 packet[i] = (uint8_t) payload[i];
295 for (i = lmin; i < len; ++i)
296 packet[i] = (uint8_t) mt_rand_int32();
300 static void assemble_icmp4(uint8_t *packet, size_t len)
302 struct icmphdr *icmph = (struct icmphdr *) packet;
304 bug_on(len < sizeof(struct icmphdr));
306 icmph->type = ICMP_ECHO;
307 icmph->code = 0;
308 icmph->checksum = 0;
311 static void assemble_icmp6(uint8_t *packet, size_t len)
313 struct icmp6hdr *icmp6h = (struct icmp6hdr *) packet;
315 bug_on(len < sizeof(struct icmp6hdr));
317 icmp6h->icmp6_type = ICMPV6_ECHO_REQUEST;
318 icmp6h->icmp6_code = 0;
319 icmp6h->icmp6_cksum = 0;
322 static void assemble_tcp(uint8_t *packet, size_t len, int syn, int ack,
323 int urg, int fin, int rst, int psh, int ecn, int dport)
325 struct tcphdr *tcph = (struct tcphdr *) packet;
327 bug_on(len < sizeof(struct tcphdr));
329 tcph->source = htons((uint16_t) mt_rand_int32());
330 tcph->dest = htons((uint16_t) dport);
331 tcph->seq = htonl(mt_rand_int32());
332 tcph->ack_seq = (!!ack ? htonl(mt_rand_int32()) : 0);
333 tcph->doff = 5;
334 tcph->syn = !!syn;
335 tcph->ack = !!ack;
336 tcph->urg = !!urg;
337 tcph->fin = !!fin;
338 tcph->rst = !!rst;
339 tcph->psh = !!psh;
340 tcph->ece = !!ecn;
341 tcph->cwr = !!ecn;
342 tcph->window = htons((uint16_t) (100 + (mt_rand_int32() % 65435)));
343 tcph->check = 0;
344 tcph->urg_ptr = (!!urg ? htons((uint16_t) mt_rand_int32()) : 0);
347 static int assemble_ipv4_tcp(uint8_t *packet, size_t len, int ttl,
348 int tos, const struct sockaddr *dst,
349 const struct sockaddr *src, int syn, int ack,
350 int urg, int fin, int rst, int psh, int ecn,
351 int nofrag, int dport, const char *payload)
353 struct iphdr *iph = (struct iphdr *) packet;
354 uint8_t *data;
355 size_t data_len;
357 bug_on(!src || !dst);
358 bug_on(src->sa_family != PF_INET || dst->sa_family != PF_INET);
359 bug_on(len < sizeof(*iph) + sizeof(struct tcphdr));
361 iph->ihl = 5;
362 iph->version = 4;
363 iph->tos = (uint8_t) tos;
364 iph->tot_len = htons((uint16_t) len);
365 iph->id = htons((uint16_t) mt_rand_int32());
366 iph->frag_off = nofrag ? IP_DF : 0;
367 iph->ttl = (uint8_t) ttl;
368 iph->protocol = 6; /* TCP */
369 iph->saddr = ((const struct sockaddr_in *) src)->sin_addr.s_addr;
370 iph->daddr = ((const struct sockaddr_in *) dst)->sin_addr.s_addr;
372 data = packet + sizeof(*iph);
373 data_len = len - sizeof(*iph);
374 assemble_tcp(data, data_len, syn, ack, urg, fin, rst, psh, ecn, dport);
376 data = packet + sizeof(*iph) + sizeof(struct tcphdr);
377 data_len = len - sizeof(*iph) - sizeof(struct tcphdr);
378 assemble_data(data, data_len, payload);
380 iph->check = csum((unsigned short *) packet, ntohs(iph->tot_len) >> 1);
382 return ntohs(iph->id);
385 static int assemble_ipv6_tcp(uint8_t *packet, size_t len, int ttl,
386 const struct sockaddr *dst,
387 const struct sockaddr *src, int syn, int ack,
388 int urg, int fin, int rst, int psh, int ecn,
389 int dport, const char *payload)
391 struct ip6_hdr *ip6h = (struct ip6_hdr *) packet;
392 uint8_t *data;
393 size_t data_len;
395 bug_on(!src || !dst);
396 bug_on(src->sa_family != PF_INET6 || dst->sa_family != PF_INET6);
397 bug_on(len < sizeof(*ip6h) + sizeof(struct tcphdr));
399 ip6h->ip6_flow = htonl(mt_rand_int32() & 0x000fffff);
400 ip6h->ip6_vfc = 0x60;
401 ip6h->ip6_plen = htons((uint16_t) len - sizeof(*ip6h));
402 ip6h->ip6_nxt = 6; /* TCP */
403 ip6h->ip6_hlim = (uint8_t) ttl;
404 memcpy(&ip6h->ip6_src, &(((const struct sockaddr_in6 *)
405 src)->sin6_addr), sizeof(ip6h->ip6_src));
406 memcpy(&ip6h->ip6_dst, &(((const struct sockaddr_in6 *)
407 dst)->sin6_addr), sizeof(ip6h->ip6_dst));
409 data = packet + sizeof(*ip6h);
410 data_len = len - sizeof(*ip6h);
411 assemble_tcp(data, data_len, syn, ack, urg, fin, rst, psh, ecn, dport);
413 data = packet + sizeof(*ip6h) + sizeof(struct tcphdr);
414 data_len = len - sizeof(*ip6h) - sizeof(struct tcphdr);
415 assemble_data(data, data_len, payload);
417 return ntohl(ip6h->ip6_flow) & 0x000fffff;
420 static int assemble_ipv6_icmp6(uint8_t *packet, size_t len, int ttl,
421 const struct sockaddr *dst,
422 const struct sockaddr *src,
423 const char *payload)
425 struct ip6_hdr *ip6h = (struct ip6_hdr *) packet;
426 uint8_t *data;
427 size_t data_len;
429 bug_on(!src || !dst);
430 bug_on(src->sa_family != PF_INET6 || dst->sa_family != PF_INET6);
431 bug_on(len < sizeof(*ip6h) + sizeof(struct icmp6hdr));
433 ip6h->ip6_flow = htonl(mt_rand_int32() & 0x000fffff);
434 ip6h->ip6_vfc = 0x60;
435 ip6h->ip6_plen = htons((uint16_t) len - sizeof(*ip6h));
436 ip6h->ip6_nxt = 0x3a; /* ICMP6 */
437 ip6h->ip6_hlim = (uint8_t) ttl;
438 memcpy(&ip6h->ip6_src, &(((const struct sockaddr_in6 *)
439 src)->sin6_addr), sizeof(ip6h->ip6_src));
440 memcpy(&ip6h->ip6_dst, &(((const struct sockaddr_in6 *)
441 dst)->sin6_addr), sizeof(ip6h->ip6_dst));
443 data = packet + sizeof(*ip6h);
444 data_len = len - sizeof(*ip6h);
445 assemble_icmp6(data, data_len);
447 data = packet + sizeof(*ip6h) + sizeof(struct icmp6hdr);
448 data_len = len - sizeof(*ip6h) - sizeof(struct icmp6hdr);
449 assemble_data(data, data_len, payload);
451 return ntohl(ip6h->ip6_flow) & 0x000fffff;
454 static int assemble_ipv4_icmp4(uint8_t *packet, size_t len, int ttl,
455 int tos, const struct sockaddr *dst,
456 const struct sockaddr *src, int nofrag,
457 const char *payload)
459 struct iphdr *iph = (struct iphdr *) packet;
460 uint8_t *data;
461 size_t data_len;
463 bug_on(!src || !dst);
464 bug_on(src->sa_family != PF_INET || dst->sa_family != PF_INET);
465 bug_on(len < sizeof(struct iphdr) + sizeof(struct icmphdr));
467 iph->ihl = 5;
468 iph->version = 4;
469 iph->tos = 0;
470 iph->tot_len = htons((uint16_t) len);
471 iph->id = htons((uint16_t) mt_rand_int32());
472 iph->frag_off = nofrag ? IP_DF : 0;
473 iph->ttl = (uint8_t) ttl;
474 iph->protocol = 1; /* ICMP4 */
475 iph->saddr = ((const struct sockaddr_in *) src)->sin_addr.s_addr;
476 iph->daddr = ((const struct sockaddr_in *) dst)->sin_addr.s_addr;
478 data = packet + sizeof(*iph);
479 data_len = len - sizeof(*iph);
480 assemble_icmp4(data, data_len);
482 data = packet + sizeof(*iph) + sizeof(struct icmphdr);
483 data_len = len - sizeof(*iph) - sizeof(struct icmphdr);
484 assemble_data(data, data_len, payload);
486 iph->check = csum((unsigned short *) packet, ntohs(iph->tot_len) >> 1);
488 return ntohs(iph->id);
491 static int assemble_packet_or_die(uint8_t *packet, size_t len,
492 int ttl, int icmp,
493 const struct ash_cfg *cfg,
494 const struct sockaddr *dst,
495 const struct sockaddr *src)
497 if (icmp) {
498 if (cfg->ip == 4) {
499 return assemble_ipv4_icmp4(packet, len, ttl, cfg->tos,
500 dst, src, cfg->nofrag,
501 cfg->payload);
502 } else {
503 return assemble_ipv6_icmp6(packet, len, ttl, dst, src,
504 cfg->payload);
506 } else {
507 if (cfg->ip == 4) {
508 return assemble_ipv4_tcp(packet, len, ttl, cfg->tos,
509 dst, src, cfg->syn, cfg->ack,
510 cfg->urg, cfg->fin, cfg->rst,
511 cfg->psh, cfg->ecn,
512 cfg->nofrag, atoi(cfg->port),
513 cfg->payload);
514 } else {
515 return assemble_ipv6_tcp(packet, len, ttl, dst, src,
516 cfg->syn, cfg->ack, cfg->urg,
517 cfg->fin, cfg->rst, cfg->psh,
518 cfg->ecn, atoi(cfg->port),
519 cfg->payload);
523 return -EIO;
526 static int handle_ipv4_icmp(uint8_t *packet, size_t len, int ttl, int id,
527 const struct sockaddr *own, int dns_resolv)
529 int ret;
530 struct iphdr *iph = (struct iphdr *) packet;
531 struct iphdr *iph_inner;
532 struct icmphdr *icmph;
533 char *hbuff;
534 struct sockaddr_in sa;
535 struct asrecord rec;
536 GeoIPRecord *gir;
538 if (iph->protocol != 1)
539 return PKT_NOT_FOR_US;
540 if (iph->daddr != ((const struct sockaddr_in *) own)->sin_addr.s_addr)
541 return PKT_NOT_FOR_US;
543 icmph = (struct icmphdr *) (packet + sizeof(struct iphdr));
544 if (icmph->type != ICMP_TIME_EXCEEDED)
545 return PKT_NOT_FOR_US;
546 if (icmph->code != ICMP_EXC_TTL)
547 return PKT_NOT_FOR_US;
549 iph_inner = (struct iphdr *) (packet + sizeof(struct iphdr) +
550 sizeof(struct icmphdr));
551 if (ntohs(iph_inner->id) != id)
552 return PKT_NOT_FOR_US;
554 hbuff = xzmalloc(NI_MAXHOST);
556 memset(&sa, 0, sizeof(sa));
557 sa.sin_family = PF_INET;
558 sa.sin_addr.s_addr = iph->saddr;
560 getnameinfo((struct sockaddr *) &sa, sizeof(sa), hbuff, NI_MAXHOST,
561 NULL, 0, NI_NUMERICHOST);
563 memset(&rec, 0, sizeof(rec));
564 ret = aslookup(hbuff, &rec);
565 if (ret < 0)
566 panic("AS lookup error %d!\n", ret);
568 gir = GeoIP_record_by_ipnum(gi_city, ntohl(iph->saddr));
569 if (!dns_resolv) {
570 if (strlen(rec.country) > 0 && gir) {
571 const char *city = make_n_a(gir->city);
573 printf("%s in AS%s (%s, %s, %s, %f, %f), %s %s (%s), %s", hbuff,
574 rec.number, rec.country,
575 GeoIP_country_name_by_ipnum(gi_country, ntohl(iph->saddr)),
576 city, gir->latitude, gir->longitude,
577 rec.prefix, rec.registry, rec.since, rec.name);
578 } else if (strlen(rec.country) > 0 && !gir) {
579 printf("%s in AS%s (%s, %s), %s %s (%s), %s", hbuff,
580 rec.number, rec.country,
581 GeoIP_country_name_by_ipnum(gi_country, ntohl(iph->saddr)),
582 rec.prefix, rec.registry, rec.since, rec.name);
583 } else {
584 printf("%s in unkown AS", hbuff);
586 } else {
587 struct hostent *hent = gethostbyaddr(&sa.sin_addr,
588 sizeof(sa.sin_addr),
589 PF_INET);
591 if (strlen(rec.country) > 0 && gir) {
592 const char *city = make_n_a(gir->city);
593 printf("%s (%s) in AS%s (%s, %s, %s, %f, %f), %s %s (%s), %s",
594 (hent ? hent->h_name : hbuff), hbuff,
595 rec.number, rec.country,
596 GeoIP_country_name_by_ipnum(gi_country, ntohl(iph->saddr)),
597 city, gir->latitude, gir->longitude,
598 rec.prefix, rec.registry,
599 rec.since, rec.name);
600 } else if (strlen(rec.country) > 0 && !gir) {
601 printf("%s (%s) in AS%s (%s, %s), %s %s (%s), %s",
602 (hent ? hent->h_name : hbuff), hbuff,
603 rec.number, rec.country,
604 GeoIP_country_name_by_ipnum(gi_country, ntohl(iph->saddr)),
605 rec.prefix, rec.registry,
606 rec.since, rec.name);
607 } else {
608 printf("%s (%s) in unkown AS",
609 (hent ? hent->h_name : hbuff), hbuff);
613 xfree(hbuff);
615 return PKT_GOOD;
618 static int handle_ipv6_icmp(uint8_t *packet, size_t len, int ttl, int id,
619 const struct sockaddr *own, int dns_resolv)
621 int ret;
622 struct ip6_hdr *ip6h = (struct ip6_hdr *) packet;
623 struct ip6_hdr *ip6h_inner;
624 struct icmp6hdr *icmp6h;
625 char *hbuff;
626 struct sockaddr_in6 sa;
627 struct asrecord rec;
628 GeoIPRecord *gir;
630 if (ip6h->ip6_nxt != 0x3a)
631 return PKT_NOT_FOR_US;
632 if (memcmp(&ip6h->ip6_dst, &(((const struct sockaddr_in6 *)
633 own)->sin6_addr), sizeof(ip6h->ip6_dst)))
634 return PKT_NOT_FOR_US;
636 icmp6h = (struct icmp6hdr *) (packet + sizeof(*ip6h));
637 if (icmp6h->icmp6_type != ICMPV6_TIME_EXCEED)
638 return PKT_NOT_FOR_US;
639 if (icmp6h->icmp6_code != ICMPV6_EXC_HOPLIMIT)
640 return PKT_NOT_FOR_US;
642 ip6h_inner = (struct ip6_hdr *) (packet + sizeof(*ip6h) + sizeof(*icmp6h));
643 if ((ntohl(ip6h_inner->ip6_flow) & 0x000fffff) != id)
644 return PKT_NOT_FOR_US;
646 hbuff = xzmalloc(NI_MAXHOST);
648 memset(&sa, 0, sizeof(sa));
649 sa.sin6_family = PF_INET6;
650 memcpy(&sa.sin6_addr, &ip6h->ip6_src, sizeof(ip6h->ip6_src));
652 getnameinfo((struct sockaddr *) &sa, sizeof(sa), hbuff, NI_MAXHOST,
653 NULL, 0, NI_NUMERICHOST);
655 memset(&rec, 0, sizeof(rec));
656 ret = aslookup(hbuff, &rec);
657 if (ret < 0)
658 panic("AS lookup error %d!\n", ret);
660 gir = GeoIP_record_by_ipnum_v6(gi_city, sa.sin6_addr);
661 if (!dns_resolv) {
662 if (strlen(rec.country) > 0 && gir) {
663 const char *city = make_n_a(gir->city);
665 printf("%s in AS%s (%s, %s, %s, %f, %f), %s %s (%s), %s", hbuff,
666 rec.number, rec.country,
667 GeoIP_country_name_by_ipnum_v6(gi_country, sa.sin6_addr),
668 city, gir->latitude, gir->longitude,
669 rec.prefix, rec.registry, rec.since, rec.name);
670 } else if (strlen(rec.country) > 0 && !gir) {
671 printf("%s in AS%s (%s, %s), %s %s (%s), %s", hbuff,
672 rec.number, rec.country,
673 GeoIP_country_name_by_ipnum_v6(gi_country, sa.sin6_addr),
674 rec.prefix, rec.registry, rec.since, rec.name);
675 } else {
676 printf("%s in unkown AS", hbuff);
678 } else {
679 struct hostent *hent = gethostbyaddr(&sa.sin6_addr,
680 sizeof(sa.sin6_addr),
681 PF_INET6);
683 if (strlen(rec.country) > 0 && gir) {
684 const char *city = make_n_a(gir->city);
685 printf("%s (%s) in AS%s (%s, %s, %s, %f, %f), %s %s (%s), %s",
686 (hent ? hent->h_name : hbuff), hbuff,
687 rec.number, rec.country,
688 GeoIP_country_name_by_ipnum_v6(gi_country, sa.sin6_addr),
689 city, gir->latitude, gir->longitude,
690 rec.prefix, rec.registry,
691 rec.since, rec.name);
692 } else if (strlen(rec.country) > 0 && !gir) {
693 printf("%s (%s) in AS%s (%s, %s), %s %s (%s), %s",
694 (hent ? hent->h_name : hbuff), hbuff,
695 rec.number, rec.country,
696 GeoIP_country_name_by_ipnum_v6(gi_country, sa.sin6_addr),
697 rec.prefix, rec.registry,
698 rec.since, rec.name);
699 } else {
700 printf("%s (%s) in unkown AS",
701 (hent ? hent->h_name : hbuff), hbuff);
705 xfree(hbuff);
707 return PKT_GOOD;
710 static int handle_packet(uint8_t *packet, size_t len, int ip, int ttl, int id,
711 struct sockaddr *own, int dns_resolv)
713 if (ip == 4)
714 return handle_ipv4_icmp(packet, len, ttl, id, own, dns_resolv);
715 else
716 return handle_ipv6_icmp(packet, len, ttl, id, own, dns_resolv);
719 static int do_trace(const struct ash_cfg *cfg)
721 int ttl, query, fd = -1, one = 1, ret, fd_cap, ifindex;
722 int is_okay = 0, id, timeout_poll;
723 uint8_t *packet, *packet_rcv;
724 ssize_t err, real_len, sd_len;
725 size_t len, len_rcv;
726 struct addrinfo hints, *ahead, *ai;
727 char *hbuff1, *hbuff2;
728 struct sockaddr_storage ss, sd;
729 struct sock_fprog bpf_ops;
730 struct ring dummy_ring;
731 struct pollfd pfd;
733 mt_init_by_random_device();
735 memset(&hints, 0, sizeof(hints));
736 hints.ai_family = PF_UNSPEC;
737 hints.ai_socktype = SOCK_STREAM;
738 hints.ai_protocol = IPPROTO_TCP;
739 hints.ai_flags = AI_NUMERICSERV;
741 ret = getaddrinfo(cfg->host, cfg->port, &hints, &ahead);
742 if (ret < 0) {
743 whine("Cannot get address info!\n");
744 return -EIO;
747 for (ai = ahead; ai != NULL && fd < 0; ai = ai->ai_next) {
748 if (!((ai->ai_family == PF_INET6 && cfg->ip == 6) ||
749 (ai->ai_family == PF_INET && cfg->ip == 4)))
750 continue;
751 fd = socket(ai->ai_family, SOCK_RAW, IPPROTO_RAW);
752 if (fd < 0)
753 continue;
754 fd_cap = pf_socket();
756 memset(&ss, 0, sizeof(ss));
757 ret = device_address(cfg->dev, ai->ai_family, &ss);
758 if (ret < 0)
759 panic("Cannot get own device address!\n");
761 ret = bind(fd, (struct sockaddr *) &ss, sizeof(ss));
762 if (ret < 0)
763 panic("Cannot bind socket!\n");
765 memset(&sd, 0, sizeof(sd));
766 memcpy(&sd, ai->ai_addr, ai->ai_addrlen);
767 if (ai->ai_family == PF_INET6) {
768 struct sockaddr_in6 *sd6 = (struct sockaddr_in6 *) &sd;
769 sd6->sin6_port = htons(0);
771 sd_len = ai->ai_addrlen;
773 break;
776 freeaddrinfo(ahead);
778 if (fd < 0) {
779 whine("Cannot create socket! Does remote support IPv%d?!\n",
780 cfg->ip);
781 return -EIO;
784 len = cfg->totlen;
785 if (cfg->ip == 4) {
786 if (len < sizeof(struct iphdr) + sizeof(struct tcphdr)) {
787 len = sizeof(struct iphdr) + sizeof(struct tcphdr);
788 if (cfg->payload)
789 len += strlen(cfg->payload);
791 } else {
792 if (len < sizeof(struct ip6_hdr) + sizeof(struct tcphdr)) {
793 len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
794 if (cfg->payload)
795 len += strlen(cfg->payload);
799 if (len >= device_mtu(cfg->dev))
800 panic("Packet len exceeds device MTU!\n");
802 packet = xmalloc(len);
803 len_rcv = device_mtu(cfg->dev);
804 packet_rcv = xmalloc(len_rcv);
806 hbuff1 = xzmalloc(256);
807 getnameinfo((struct sockaddr *) &sd, sizeof(sd), hbuff1, 256,
808 NULL, 0, NI_NUMERICHOST);
810 hbuff2 = xzmalloc(256);
811 getnameinfo((struct sockaddr *) &ss, sizeof(ss), hbuff2, 256,
812 NULL, 0, NI_NUMERICHOST);
814 ret = setsockopt(fd, cfg->ip == 4 ? IPPROTO_IP : IPPROTO_IPV6,
815 IP_HDRINCL, &one, sizeof(one));
816 if (ret < 0)
817 panic("Kernel does not support IP_HDRINCL!\n");
819 printf("AS path IPv%d TCP trace from %s to %s:%s (%s) with len %zu "
820 "Bytes, %u max hops\n", cfg->ip, hbuff2, hbuff1, cfg->port,
821 cfg->host, len, cfg->max_ttl);
823 printf("Using flags SYN:%d,ACK:%d,ECN:%d,FIN:%d,PSH:%d,RST:%d,URG:%d\n",
824 cfg->syn, cfg->ack, cfg->ecn, cfg->fin, cfg->psh, cfg->rst,
825 cfg->urg);
827 if (cfg->payload)
828 printf("With payload: \'%s\'\n", cfg->payload);
830 fflush(stdout);
832 xfree(hbuff1);
833 xfree(hbuff2);
835 hbuff1 = hbuff2 = NULL;
837 enable_kernel_bpf_jit_compiler();
839 memset(&bpf_ops, 0, sizeof(bpf_ops));
840 if (cfg->ip == 4) {
841 bpf_ops.filter = ipv4_icmp_type_11;
842 bpf_ops.len = (sizeof(ipv4_icmp_type_11) /
843 sizeof(ipv4_icmp_type_11[0]));
844 } else {
845 bpf_ops.filter = ipv6_icmp6_type_3;
846 bpf_ops.len = (sizeof(ipv6_icmp6_type_3) /
847 sizeof(ipv6_icmp6_type_3[0]));
850 bpf_attach_to_sock(fd_cap, &bpf_ops);
851 ifindex = device_ifindex(cfg->dev);
852 bind_rx_ring(fd_cap, &dummy_ring, ifindex);
853 prepare_polling(fd_cap, &pfd);
855 timeout_poll = (cfg->timeout > 0 ? cfg->timeout : 3) * 1000;
857 for (ttl = cfg->init_ttl; ttl <= cfg->max_ttl; ++ttl) {
858 int icmp = 0;
859 is_okay = 0;
861 if ((ttl == cfg->init_ttl && !show_pkt) ||
862 (ttl > cfg->init_ttl)) {
863 printf("%2d: ", ttl);
864 fflush(stdout);
866 retry:
867 for (query = 0; query < cfg->queries && !is_okay; ++query) {
868 id = assemble_packet_or_die(packet, len, ttl, icmp, cfg,
869 (struct sockaddr *) &sd,
870 (struct sockaddr *) &ss);
871 if (ttl == cfg->init_ttl && query == 0 && show_pkt) {
872 struct pkt_buff *pkt;
874 printf("Original packet:\n");
876 pkt = pkt_alloc(packet, len);
877 hex_ascii(pkt);
878 tprintf_flush();
879 pkt_free(pkt);
881 printf("\n%2d: ", ttl);
882 fflush(stdout);
885 err = sendto(fd, packet, len, 0, (struct sockaddr *) &sd,
886 sd_len);
887 if (err < 0)
888 panic("sendto failed: %s\n", strerror(errno));
890 err = poll(&pfd, 1, timeout_poll);
891 if (err > 0 && pfd.revents & POLLIN) {
892 real_len = recvfrom(fd_cap, packet_rcv, len_rcv,
893 0, NULL, NULL);
894 if (real_len < sizeof(struct ethhdr) +
895 (cfg->ip ? sizeof(struct iphdr) +
896 sizeof(struct icmphdr) :
897 sizeof(struct ip6_hdr) +
898 sizeof(struct icmp6hdr)))
899 continue;
901 is_okay = handle_packet(packet_rcv + sizeof(struct ethhdr),
902 real_len - sizeof(struct ethhdr),
903 cfg->ip, ttl, id,
904 (struct sockaddr *) &ss,
905 cfg->dns_resolv);
906 if (is_okay && show_pkt) {
907 struct pkt_buff *pkt;
909 printf("\n Received packet:\n");
911 pkt = pkt_alloc(packet_rcv, real_len);
912 hex_ascii(pkt);
913 tprintf_flush();
914 pkt_free(pkt);
916 } else {
917 printf("* ");
918 fflush(stdout);
919 is_okay = 0;
923 if (is_okay == 0 && icmp == 0) {
924 icmp = 1;
925 goto retry;
928 printf("\n");
929 fflush(stdout);
932 close(fd_cap);
933 close(fd);
935 xfree(packet);
936 xfree(packet_rcv);
938 return 0;
941 static void parse_whois_or_die(struct ash_cfg *cfg)
943 int fd;
944 ssize_t ret;
945 char tmp[512], *ptr, *ptr2;
947 fd = open_or_die(WHOIS_SERVER_SOURCE, O_RDONLY);
949 while ((ret = read(fd, tmp, sizeof(tmp))) > 0) {
950 tmp[sizeof(tmp) - 1] = 0;
951 ptr = skips(tmp);
952 ptr2 = ptr;
953 while (*ptr2 != ' ' && ptr2 < &tmp[sizeof(tmp) - 1])
954 ptr2++;
955 if (*ptr2 != ' ')
956 panic("Parser error!\n");
957 *ptr2 = 0;
958 cfg->whois = xstrdup(ptr);
959 ptr = ptr2 + 1;
960 if (ptr >= &tmp[sizeof(tmp) - 1])
961 panic("Parser error!\n");
962 ptr = skips(ptr);
963 ptr[strlen(ptr) - 1] = 0;
964 cfg->whois_port = xstrdup(ptr);
965 break;
968 close(fd);
971 int main(int argc, char **argv)
973 int c, opt_index, ret;
974 struct ash_cfg cfg;
975 char *path_city_db = NULL, *path_country_db = NULL;
977 memset(&cfg, 0, sizeof(cfg));
978 cfg.init_ttl = 1;
979 cfg.max_ttl = 30;
980 cfg.queries = 3;
981 cfg.timeout = 3;
982 cfg.ip = 4;
983 cfg.payload = NULL;
984 cfg.dev = xstrdup("eth0");
985 cfg.port = xstrdup("80");
987 while ((c = getopt_long(argc, argv, short_options, long_options,
988 &opt_index)) != EOF) {
989 switch (c) {
990 case 'h':
991 help();
992 break;
993 case 'v':
994 version();
995 break;
996 case 'H':
997 cfg.host = xstrdup(optarg);
998 break;
999 case 'p':
1000 if (cfg.port)
1001 xfree(cfg.port);
1002 cfg.port = xstrdup(optarg);
1003 break;
1004 case 'n':
1005 cfg.dns_resolv = 0;
1006 break;
1007 case '4':
1008 cfg.ip = 4;
1009 break;
1010 case '6':
1011 cfg.ip = 6;
1012 break;
1013 case 'Z':
1014 show_pkt = 1;
1015 break;
1016 case 'N':
1017 cfg.dns_resolv = 1;
1018 break;
1019 case 'f':
1020 cfg.init_ttl = atoi(optarg);
1021 if (cfg.init_ttl <= 0)
1022 help();
1023 break;
1024 case 'm':
1025 cfg.max_ttl = atoi(optarg);
1026 if (cfg.max_ttl <= 0)
1027 help();
1028 break;
1029 case 'i':
1030 case 'd':
1031 if (cfg.dev)
1032 xfree(cfg.dev);
1033 cfg.dev = xstrdup(optarg);
1034 break;
1035 case 'q':
1036 cfg.queries = atoi(optarg);
1037 if (cfg.queries <= 0)
1038 help();
1039 break;
1040 case 'x':
1041 cfg.timeout = atoi(optarg);
1042 if (cfg.timeout <= 0)
1043 help();
1044 break;
1045 case 'S':
1046 cfg.syn = 1;
1047 break;
1048 case 'A':
1049 cfg.ack = 1;
1050 break;
1051 case 'F':
1052 cfg.fin = 1;
1053 break;
1054 case 'U':
1055 cfg.urg = 1;
1056 break;
1057 case 'P':
1058 cfg.psh = 1;
1059 break;
1060 case 'R':
1061 cfg.rst = 1;
1062 break;
1063 case 'E':
1064 cfg.syn = 1;
1065 cfg.ecn = 1;
1066 break;
1067 case 't':
1068 cfg.tos = atoi(optarg);
1069 if (cfg.tos < 0)
1070 help();
1071 break;
1072 case 'G':
1073 cfg.nofrag = 1;
1074 break;
1075 case 'X':
1076 cfg.payload = xstrdup(optarg);
1077 break;
1078 case 'l':
1079 cfg.totlen = atoi(optarg);
1080 if (cfg.totlen <= 0)
1081 help();
1082 break;
1083 case 'w':
1084 cfg.whois = xstrdup(optarg);
1085 break;
1086 case 'W':
1087 cfg.whois_port = xstrdup(optarg);
1088 break;
1089 case 'L':
1090 path_city_db = xstrdup(optarg);
1091 break;
1092 case 'K':
1093 path_country_db = xstrdup(optarg);
1094 break;
1095 case '?':
1096 switch (optopt) {
1097 case 'H':
1098 case 'p':
1099 case 'L':
1100 case 'K':
1101 case 'f':
1102 case 'm':
1103 case 'i':
1104 case 'd':
1105 case 'q':
1106 case 'x':
1107 case 'X':
1108 case 't':
1109 case 'l':
1110 case 'w':
1111 case 'W':
1112 panic("Option -%c requires an argument!\n",
1113 optopt);
1114 default:
1115 if (isprint(optopt))
1116 whine("Unknown option character "
1117 "`0x%X\'!\n", optopt);
1118 die();
1120 default:
1121 break;
1125 if (argc < 3 ||
1126 !cfg.host || !cfg.port ||
1127 cfg.init_ttl > cfg.max_ttl ||
1128 cfg.init_ttl > MAXTTL ||
1129 cfg.max_ttl > MAXTTL)
1130 help();
1132 if (!device_up_and_running(cfg.dev))
1133 panic("Networking device not up and running!\n");
1134 if (!cfg.whois || !cfg.whois_port)
1135 parse_whois_or_die(&cfg);
1136 if (device_mtu(cfg.dev) <= cfg.totlen)
1137 panic("Packet larger than device MTU!\n");
1139 register_signal(SIGHUP, signal_handler);
1141 header();
1143 tprintf_init();
1145 ret = aslookup_prepare(cfg.whois, cfg.whois_port);
1146 if (ret < 0)
1147 panic("Cannot resolve whois server!\n");
1149 if (path_country_db)
1150 gi_country = GeoIP_open(path_country_db, GEOIP_MMAP_CACHE);
1151 else
1152 gi_country = GeoIP_open_type(cfg.ip == 4 ?
1153 GEOIP_COUNTRY_EDITION :
1154 GEOIP_COUNTRY_EDITION_V6,
1155 GEOIP_MMAP_CACHE);
1157 if (path_city_db)
1158 gi_city = GeoIP_open(path_city_db, GEOIP_MMAP_CACHE);
1159 else
1160 gi_city = GeoIP_open_type(cfg.ip == 4 ?
1161 GEOIP_CITY_EDITION_REV1 :
1162 GEOIP_CITY_EDITION_REV1_V6,
1163 GEOIP_MMAP_CACHE);
1165 if (!gi_country || !gi_city)
1166 panic("Cannot open GeoIP database! Wrong path?!\n");
1168 GeoIP_set_charset(gi_country, GEOIP_CHARSET_UTF8);
1169 GeoIP_set_charset(gi_city, GEOIP_CHARSET_UTF8);
1171 ret = do_trace(&cfg);
1173 GeoIP_delete(gi_city);
1174 GeoIP_delete(gi_country);
1176 tprintf_cleanup();
1178 free(cfg.whois_port);
1179 free(cfg.whois);
1180 free(cfg.dev);
1181 free(cfg.host);
1182 free(cfg.port);
1183 free(cfg.payload);
1184 free(path_city_db);
1185 free(path_country_db);
1187 return ret;