stddef: add more useful CPP constants
[netsniff-ng.git] / astraceroute.c
blob6bf94f311a4df54665e572c4e3878d2782c0128b
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 <sys/fsuid.h>
34 #include <fcntl.h>
35 #include <time.h>
36 #include <string.h>
37 #include <asm/byteorder.h>
38 #include <linux/tcp.h>
39 #include <netinet/ip.h>
40 #include <netinet/ip6.h>
41 #include <netinet/in.h>
42 #include <errno.h>
43 #include <netdb.h>
44 #include <arpa/inet.h>
45 #include <linux/if_ether.h>
46 #include <linux/icmp.h>
47 #include <linux/icmpv6.h>
48 #include <GeoIP.h>
49 #include <GeoIPCity.h>
51 #include "bpf.h"
52 #include "die.h"
53 #include "tprintf.h"
54 #include "pkt_buff.h"
55 #include "proto.h"
56 #include "xmalloc.h"
57 #include "xio.h"
58 #include "aslookup.h"
59 #include "xutils.h"
60 #include "ring_rx.h"
61 #include "built_in.h"
63 #define WHOIS_SERVER_SOURCE "/etc/netsniff-ng/whois.conf"
65 struct ash_cfg {
66 char *host;
67 char *port;
68 int init_ttl;
69 int max_ttl;
70 int dns_resolv;
71 char *dev;
72 int queries;
73 int timeout;
74 int syn, ack, ecn, fin, psh, rst, urg;
75 int tos, nofrag;
76 int totlen;
77 char *whois;
78 char *whois_port;
79 int ip;
80 char *payload;
83 volatile sig_atomic_t sigint = 0;
85 static int show_pkt = 0;
87 static GeoIP *gi_country = NULL;
88 static GeoIP *gi_city = NULL;
90 static const char *short_options = "H:p:nNf:m:i:d:q:x:SAEFPURt:Gl:w:W:hv46X:ZLK";
91 static const struct option long_options[] = {
92 {"host", required_argument, NULL, 'H'},
93 {"port", required_argument, NULL, 'p'},
94 {"init-ttl", required_argument, NULL, 'f'},
95 {"max-ttl", required_argument, NULL, 'm'},
96 {"dev", required_argument, NULL, 'd'},
97 {"num-probes", required_argument, NULL, 'q'},
98 {"timeout", required_argument, NULL, 'x'},
99 {"tos", required_argument, NULL, 't'},
100 {"payload", required_argument, NULL, 'X'},
101 {"totlen", required_argument, NULL, 'l'},
102 {"whois", required_argument, NULL, 'w'},
103 {"wport", required_argument, NULL, 'W'},
104 {"city-db", required_argument, NULL, 'L'},
105 {"country-db", required_argument, NULL, 'K'},
106 {"numeric", no_argument, NULL, 'n'},
107 {"dns", no_argument, NULL, 'N'},
108 {"ipv4", no_argument, NULL, '4'},
109 {"ipv6", no_argument, NULL, '6'},
110 {"syn", no_argument, NULL, 'S'},
111 {"ack", no_argument, NULL, 'A'},
112 {"urg", no_argument, NULL, 'U'},
113 {"fin", no_argument, NULL, 'F'},
114 {"psh", no_argument, NULL, 'P'},
115 {"rst", no_argument, NULL, 'R'},
116 {"ecn-syn", no_argument, NULL, 'E'},
117 {"show-packet", no_argument, NULL, 'Z'},
118 {"nofrag", no_argument, NULL, 'G'},
119 {"version", no_argument, NULL, 'v'},
120 {"help", no_argument, NULL, 'h'},
121 {NULL, 0, NULL, 0}
124 static struct sock_filter ipv4_icmp_type_11[] = {
125 /* (000) ldh [12] */
126 { 0x28, 0, 0, 0x0000000c },
127 /* (001) jeq #0x800 jt 2 jf 10 */
128 { 0x15, 0, 8, 0x00000800 },
129 /* (002) ldb [23] */
130 { 0x30, 0, 0, 0x00000017 },
131 /* (003) jeq #0x1 jt 4 jf 10 */
132 { 0x15, 0, 6, 0x00000001 },
133 /* (004) ldh [20] */
134 { 0x28, 0, 0, 0x00000014 },
135 /* (005) jset #0x1fff jt 10 jf 6 */
136 { 0x45, 4, 0, 0x00001fff },
137 /* (006) ldxb 4*([14]&0xf) */
138 { 0xb1, 0, 0, 0x0000000e },
139 /* (007) ldb [x + 14] */
140 { 0x50, 0, 0, 0x0000000e },
141 /* (008) jeq #0xb jt 9 jf 10 */
142 { 0x15, 0, 1, 0x0000000b },
143 /* (009) ret #65535 */
144 { 0x06, 0, 0, 0xffffffff },
145 /* (010) ret #0 */
146 { 0x06, 0, 0, 0x00000000 },
149 static struct sock_filter ipv6_icmp6_type_3[] = {
150 /* (000) ldh [12] */
151 { 0x28, 0, 0, 0x0000000c },
152 /* (001) jeq #0x86dd jt 2 jf 7 */
153 { 0x15, 0, 5, 0x000086dd },
154 /* (002) ldb [20] */
155 { 0x30, 0, 0, 0x00000014 },
156 /* (003) jeq #0x3a jt 4 jf 7 */
157 { 0x15, 0, 3, 0x0000003a },
158 /* (004) ldb [54] */
159 { 0x30, 0, 0, 0x00000036 },
160 /* (005) jeq #0x3 jt 6 jf 7 */
161 { 0x15, 0, 1, 0x00000003 },
162 /* (006) ret #65535 */
163 { 0x06, 0, 0, 0xffffffff },
164 /* (007) ret #0 */
165 { 0x06, 0, 0, 0x00000000 },
168 #define PKT_NOT_FOR_US 0
169 #define PKT_GOOD 1
171 static inline const char *make_n_a(const char *p)
173 return p ? : "N/A";
176 static void signal_handler(int number)
178 switch (number) {
179 case SIGINT:
180 sigint = 1;
181 break;
182 default:
183 break;
187 static void help(void)
190 printf("\nastraceroute %s, autonomous system trace route utility\n",
191 VERSION_STRING);
192 puts("http://www.netsniff-ng.org\n\n"
193 "Usage: astraceroute [options]\n"
194 "Options:\n"
195 " -H|--host <host> Host/IPv4/IPv6 to lookup AS route to\n"
196 " -p|--port <port> Hosts port to lookup AS route to\n"
197 " -i|-d|--dev <device> Networking device, i.e. eth0\n"
198 " -4|--ipv4 Use IPv4 requests (default)\n"
199 " -6|--ipv6 Use IPv6 requests\n"
200 " -n|--numeric Do not do reverse DNS lookup for hops\n"
201 " -N|--dns Do a reverse DNS lookup for hops\n"
202 " -f|--init-ttl <ttl> Set initial TTL\n"
203 " -m|--max-ttl <ttl> Set maximum TTL (default: 30)\n"
204 " -q|--num-probes <num> Number of max probes for each hop (default: 3)\n"
205 " -x|--timeout <sec> Probe response timeout in sec (default: 3)\n"
206 " -S|--syn Set TCP SYN flag in packets\n"
207 " -A|--ack Set TCP ACK flag in packets\n"
208 " -F|--fin Set TCP FIN flag in packets\n"
209 " -P|--psh Set TCP PSH flag in packets\n"
210 " -U|--urg Set TCP URG flag in packets\n"
211 " -R|--rst Set TCP RST flag in packets\n"
212 " -E|--ecn-syn Send ECN SYN packets (RFC3168)\n"
213 " -t|--tos <tos> Set the IP TOS field\n"
214 " -G|--nofrag Set do not fragment bit\n"
215 " -X|--payload <string> Specify a payload string to test DPIs\n"
216 " -Z|--show-packet Show returned packet on each hop\n"
217 " -l|--totlen <len> Specify total packet len\n"
218 " -w|--whois <server> Use a different AS whois DB server\n"
219 " (default: /etc/netsniff-ng/whois.conf)\n"
220 " -W|--wport <port> Use a different port to AS whois server\n"
221 " (default: /etc/netsniff-ng/whois.conf)\n"
222 " --city-db <path> Specifiy path for geoip city database\n"
223 " --country-db <path> Specifiy path for geoip country database\n"
224 " -v|--version Print version\n"
225 " -h|--help Print this help\n\n"
226 "Examples:\n"
227 " IPv4 trace of AS with TCP SYN probe (this will most-likely pass):\n"
228 " astraceroute -i eth0 -N -S -H netsniff-ng.org\n"
229 " IPv4 trace of AS with TCP ECN SYN probe:\n"
230 " astraceroute -i eth0 -N -E -H netsniff-ng.org\n"
231 " IPv4 trace of AS with TCP FIN probe:\n"
232 " astraceroute -i eth0 -N -F -H netsniff-ng.org\n"
233 " IPv4 trace of AS with Xmas probe:\n"
234 " astraceroute -i eth0 -N -FPU -H netsniff-ng.org\n"
235 " IPv4 trace of AS with Null probe with ASCII payload:\n"
236 " astraceroute -i eth0 -N -H netsniff-ng.org -X \"censor-me\" -Z\n"
237 " IPv6 trace of AS up to www.6bone.net:\n"
238 " astraceroute -6 -i eth0 -S -E -N -H www.6bone.net\n\n"
239 "Note:\n"
240 " If the TCP probe did not give any results, then astraceroute will\n"
241 " automatically probe for classic ICMP packets! To gather more\n"
242 " information about astraceroute's fetched AS numbers, see i.e.\n"
243 " http://bgp.he.net/AS<number>!\n\n"
244 "Please report bugs to <bugs@netsniff-ng.org>\n"
245 "Copyright (C) 2011-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
246 "Swiss federal institute of technology (ETH Zurich)\n"
247 "License: GNU GPL version 2.0\n"
248 "This is free software: you are free to change and redistribute it.\n"
249 "There is NO WARRANTY, to the extent permitted by law.\n");
250 die();
253 static void version(void)
255 printf("\nastraceroute %s, autonomous system trace route utility\n",
256 VERSION_STRING);
257 puts("http://www.netsniff-ng.org\n\n"
258 "Please report bugs to <bugs@netsniff-ng.org>\n"
259 "Copyright (C) 2011-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
260 "Swiss federal institute of technology (ETH Zurich)\n"
261 "License: GNU GPL version 2.0\n"
262 "This is free software: you are free to change and redistribute it.\n"
263 "There is NO WARRANTY, to the extent permitted by law.\n");
264 die();
267 static inline unsigned short csum(unsigned short *buf, int nwords)
269 unsigned long sum;
271 for (sum = 0; nwords > 0; nwords--)
272 sum += *buf++;
273 sum = (sum >> 16) + (sum & 0xffff);
274 sum += (sum >> 16);
276 return ~sum;
279 static void assemble_data(uint8_t *packet, size_t len, const char *payload)
281 int i;
283 if (payload == NULL) {
284 for (i = 0; i < len; ++i)
285 packet[i] = (uint8_t) rand();
286 } else {
287 int lmin = min(len, strlen(payload));
288 for (i = 0; i < lmin; ++i)
289 packet[i] = (uint8_t) payload[i];
290 for (i = lmin; i < len; ++i)
291 packet[i] = (uint8_t) rand();
295 static void assemble_icmp4(uint8_t *packet, size_t len)
297 struct icmphdr *icmph = (struct icmphdr *) packet;
299 bug_on(len < sizeof(struct icmphdr));
301 icmph->type = ICMP_ECHO;
302 icmph->code = 0;
303 icmph->checksum = 0;
306 static void assemble_icmp6(uint8_t *packet, size_t len)
308 struct icmp6hdr *icmp6h = (struct icmp6hdr *) packet;
310 bug_on(len < sizeof(struct icmp6hdr));
312 icmp6h->icmp6_type = ICMPV6_ECHO_REQUEST;
313 icmp6h->icmp6_code = 0;
314 icmp6h->icmp6_cksum = 0;
317 static void assemble_tcp(uint8_t *packet, size_t len, int syn, int ack,
318 int urg, int fin, int rst, int psh, int ecn, int dport)
320 struct tcphdr *tcph = (struct tcphdr *) packet;
322 bug_on(len < sizeof(struct tcphdr));
324 tcph->source = htons((uint16_t) rand());
325 tcph->dest = htons((uint16_t) dport);
326 tcph->seq = htonl(rand());
327 tcph->ack_seq = (!!ack ? htonl(rand()) : 0);
328 tcph->doff = 5;
329 tcph->syn = !!syn;
330 tcph->ack = !!ack;
331 tcph->urg = !!urg;
332 tcph->fin = !!fin;
333 tcph->rst = !!rst;
334 tcph->psh = !!psh;
335 tcph->ece = !!ecn;
336 tcph->cwr = !!ecn;
337 tcph->window = htons((uint16_t) (100 + (rand() % 65435)));
338 tcph->check = 0;
339 tcph->urg_ptr = (!!urg ? htons((uint16_t) rand()) : 0);
342 static int assemble_ipv4_tcp(uint8_t *packet, size_t len, int ttl,
343 int tos, const struct sockaddr *dst,
344 const struct sockaddr *src, int syn, int ack,
345 int urg, int fin, int rst, int psh, int ecn,
346 int nofrag, int dport, const char *payload)
348 struct iphdr *iph = (struct iphdr *) packet;
349 uint8_t *data;
350 size_t data_len;
352 bug_on(!src || !dst);
353 bug_on(src->sa_family != PF_INET || dst->sa_family != PF_INET);
354 bug_on(len < sizeof(*iph) + sizeof(struct tcphdr));
356 iph->ihl = 5;
357 iph->version = 4;
358 iph->tos = (uint8_t) tos;
359 iph->tot_len = htons((uint16_t) len);
360 iph->id = htons((uint16_t) rand());
361 iph->frag_off = nofrag ? IP_DF : 0;
362 iph->ttl = (uint8_t) ttl;
363 iph->protocol = 6; /* TCP */
364 iph->saddr = ((const struct sockaddr_in *) src)->sin_addr.s_addr;
365 iph->daddr = ((const struct sockaddr_in *) dst)->sin_addr.s_addr;
367 data = packet + sizeof(*iph);
368 data_len = len - sizeof(*iph);
369 assemble_tcp(data, data_len, syn, ack, urg, fin, rst, psh, ecn, dport);
371 data = packet + sizeof(*iph) + sizeof(struct tcphdr);
372 data_len = len - sizeof(*iph) - sizeof(struct tcphdr);
373 assemble_data(data, data_len, payload);
375 iph->check = csum((unsigned short *) packet, ntohs(iph->tot_len) >> 1);
377 return ntohs(iph->id);
380 static int assemble_ipv6_tcp(uint8_t *packet, size_t len, int ttl,
381 const struct sockaddr *dst,
382 const struct sockaddr *src, int syn, int ack,
383 int urg, int fin, int rst, int psh, int ecn,
384 int dport, const char *payload)
386 struct ip6_hdr *ip6h = (struct ip6_hdr *) packet;
387 uint8_t *data;
388 size_t data_len;
390 bug_on(!src || !dst);
391 bug_on(src->sa_family != PF_INET6 || dst->sa_family != PF_INET6);
392 bug_on(len < sizeof(*ip6h) + sizeof(struct tcphdr));
394 ip6h->ip6_flow = htonl(rand() & 0x000fffff);
395 ip6h->ip6_vfc = 0x60;
396 ip6h->ip6_plen = htons((uint16_t) len - sizeof(*ip6h));
397 ip6h->ip6_nxt = 6; /* TCP */
398 ip6h->ip6_hlim = (uint8_t) ttl;
399 memcpy(&ip6h->ip6_src, &(((const struct sockaddr_in6 *)
400 src)->sin6_addr), sizeof(ip6h->ip6_src));
401 memcpy(&ip6h->ip6_dst, &(((const struct sockaddr_in6 *)
402 dst)->sin6_addr), sizeof(ip6h->ip6_dst));
404 data = packet + sizeof(*ip6h);
405 data_len = len - sizeof(*ip6h);
406 assemble_tcp(data, data_len, syn, ack, urg, fin, rst, psh, ecn, dport);
408 data = packet + sizeof(*ip6h) + sizeof(struct tcphdr);
409 data_len = len - sizeof(*ip6h) - sizeof(struct tcphdr);
410 assemble_data(data, data_len, payload);
412 return ntohl(ip6h->ip6_flow) & 0x000fffff;
415 static int assemble_ipv6_icmp6(uint8_t *packet, size_t len, int ttl,
416 const struct sockaddr *dst,
417 const struct sockaddr *src,
418 const char *payload)
420 struct ip6_hdr *ip6h = (struct ip6_hdr *) packet;
421 uint8_t *data;
422 size_t data_len;
424 bug_on(!src || !dst);
425 bug_on(src->sa_family != PF_INET6 || dst->sa_family != PF_INET6);
426 bug_on(len < sizeof(*ip6h) + sizeof(struct icmp6hdr));
428 ip6h->ip6_flow = htonl(rand() & 0x000fffff);
429 ip6h->ip6_vfc = 0x60;
430 ip6h->ip6_plen = htons((uint16_t) len - sizeof(*ip6h));
431 ip6h->ip6_nxt = 0x3a; /* ICMP6 */
432 ip6h->ip6_hlim = (uint8_t) ttl;
433 memcpy(&ip6h->ip6_src, &(((const struct sockaddr_in6 *)
434 src)->sin6_addr), sizeof(ip6h->ip6_src));
435 memcpy(&ip6h->ip6_dst, &(((const struct sockaddr_in6 *)
436 dst)->sin6_addr), sizeof(ip6h->ip6_dst));
438 data = packet + sizeof(*ip6h);
439 data_len = len - sizeof(*ip6h);
440 assemble_icmp6(data, data_len);
442 data = packet + sizeof(*ip6h) + sizeof(struct icmp6hdr);
443 data_len = len - sizeof(*ip6h) - sizeof(struct icmp6hdr);
444 assemble_data(data, data_len, payload);
446 return ntohl(ip6h->ip6_flow) & 0x000fffff;
449 static int assemble_ipv4_icmp4(uint8_t *packet, size_t len, int ttl,
450 int tos, const struct sockaddr *dst,
451 const struct sockaddr *src, int nofrag,
452 const char *payload)
454 struct iphdr *iph = (struct iphdr *) packet;
455 uint8_t *data;
456 size_t data_len;
458 bug_on(!src || !dst);
459 bug_on(src->sa_family != PF_INET || dst->sa_family != PF_INET);
460 bug_on(len < sizeof(struct iphdr) + sizeof(struct icmphdr));
462 iph->ihl = 5;
463 iph->version = 4;
464 iph->tos = 0;
465 iph->tot_len = htons((uint16_t) len);
466 iph->id = htons((uint16_t) rand());
467 iph->frag_off = nofrag ? IP_DF : 0;
468 iph->ttl = (uint8_t) ttl;
469 iph->protocol = 1; /* ICMP4 */
470 iph->saddr = ((const struct sockaddr_in *) src)->sin_addr.s_addr;
471 iph->daddr = ((const struct sockaddr_in *) dst)->sin_addr.s_addr;
473 data = packet + sizeof(*iph);
474 data_len = len - sizeof(*iph);
475 assemble_icmp4(data, data_len);
477 data = packet + sizeof(*iph) + sizeof(struct icmphdr);
478 data_len = len - sizeof(*iph) - sizeof(struct icmphdr);
479 assemble_data(data, data_len, payload);
481 iph->check = csum((unsigned short *) packet, ntohs(iph->tot_len) >> 1);
483 return ntohs(iph->id);
486 static int assemble_packet_or_die(uint8_t *packet, size_t len,
487 int ttl, int icmp,
488 const struct ash_cfg *cfg,
489 const struct sockaddr *dst,
490 const struct sockaddr *src)
492 if (icmp) {
493 if (cfg->ip == 4) {
494 return assemble_ipv4_icmp4(packet, len, ttl, cfg->tos,
495 dst, src, cfg->nofrag,
496 cfg->payload);
497 } else {
498 return assemble_ipv6_icmp6(packet, len, ttl, dst, src,
499 cfg->payload);
501 } else {
502 if (cfg->ip == 4) {
503 return assemble_ipv4_tcp(packet, len, ttl, cfg->tos,
504 dst, src, cfg->syn, cfg->ack,
505 cfg->urg, cfg->fin, cfg->rst,
506 cfg->psh, cfg->ecn,
507 cfg->nofrag, atoi(cfg->port),
508 cfg->payload);
509 } else {
510 return assemble_ipv6_tcp(packet, len, ttl, dst, src,
511 cfg->syn, cfg->ack, cfg->urg,
512 cfg->fin, cfg->rst, cfg->psh,
513 cfg->ecn, atoi(cfg->port),
514 cfg->payload);
518 return -EIO;
521 static int handle_ipv4_icmp(uint8_t *packet, size_t len, int ttl, int id,
522 const struct sockaddr *own, int dns_resolv)
524 int ret;
525 struct iphdr *iph = (struct iphdr *) packet;
526 struct iphdr *iph_inner;
527 struct icmphdr *icmph;
528 char *hbuff;
529 struct sockaddr_in sa;
530 struct asrecord rec;
531 GeoIPRecord *gir;
533 if (iph->protocol != 1)
534 return PKT_NOT_FOR_US;
535 if (iph->daddr != ((const struct sockaddr_in *) own)->sin_addr.s_addr)
536 return PKT_NOT_FOR_US;
538 icmph = (struct icmphdr *) (packet + sizeof(struct iphdr));
539 if (icmph->type != ICMP_TIME_EXCEEDED)
540 return PKT_NOT_FOR_US;
541 if (icmph->code != ICMP_EXC_TTL)
542 return PKT_NOT_FOR_US;
544 iph_inner = (struct iphdr *) (packet + sizeof(struct iphdr) +
545 sizeof(struct icmphdr));
546 if (ntohs(iph_inner->id) != id)
547 return PKT_NOT_FOR_US;
549 hbuff = xzmalloc(NI_MAXHOST);
551 memset(&sa, 0, sizeof(sa));
552 sa.sin_family = PF_INET;
553 sa.sin_addr.s_addr = iph->saddr;
555 getnameinfo((struct sockaddr *) &sa, sizeof(sa), hbuff, NI_MAXHOST,
556 NULL, 0, NI_NUMERICHOST);
558 memset(&rec, 0, sizeof(rec));
559 ret = aslookup(hbuff, &rec);
560 if (ret < 0)
561 panic("AS lookup error %d!\n", ret);
563 gir = GeoIP_record_by_ipnum(gi_city, ntohl(iph->saddr));
564 if (!dns_resolv) {
565 if (strlen(rec.country) > 0 && gir) {
566 const char *city = make_n_a(gir->city);
568 printf("%s in AS%s (%s, %s, %s, %f, %f), %s %s (%s), %s", hbuff,
569 rec.number, rec.country,
570 GeoIP_country_name_by_ipnum(gi_country, ntohl(iph->saddr)),
571 city, gir->latitude, gir->longitude,
572 rec.prefix, rec.registry, rec.since, rec.name);
573 } else if (strlen(rec.country) > 0 && !gir) {
574 printf("%s in AS%s (%s, %s), %s %s (%s), %s", hbuff,
575 rec.number, rec.country,
576 GeoIP_country_name_by_ipnum(gi_country, ntohl(iph->saddr)),
577 rec.prefix, rec.registry, rec.since, rec.name);
578 } else {
579 printf("%s in unknown AS", hbuff);
581 } else {
582 struct hostent *hent = gethostbyaddr(&sa.sin_addr,
583 sizeof(sa.sin_addr),
584 PF_INET);
586 if (strlen(rec.country) > 0 && gir) {
587 const char *city = make_n_a(gir->city);
588 printf("%s (%s) in AS%s (%s, %s, %s, %f, %f), %s %s (%s), %s",
589 (hent ? hent->h_name : hbuff), hbuff,
590 rec.number, rec.country,
591 GeoIP_country_name_by_ipnum(gi_country, ntohl(iph->saddr)),
592 city, gir->latitude, gir->longitude,
593 rec.prefix, rec.registry,
594 rec.since, rec.name);
595 } else if (strlen(rec.country) > 0 && !gir) {
596 printf("%s (%s) in AS%s (%s, %s), %s %s (%s), %s",
597 (hent ? hent->h_name : hbuff), hbuff,
598 rec.number, rec.country,
599 GeoIP_country_name_by_ipnum(gi_country, ntohl(iph->saddr)),
600 rec.prefix, rec.registry,
601 rec.since, rec.name);
602 } else {
603 printf("%s (%s) in unknown AS",
604 (hent ? hent->h_name : hbuff), hbuff);
608 xfree(hbuff);
610 return PKT_GOOD;
613 static int handle_ipv6_icmp(uint8_t *packet, size_t len, int ttl, int id,
614 const struct sockaddr *own, int dns_resolv)
616 int ret;
617 struct ip6_hdr *ip6h = (struct ip6_hdr *) packet;
618 struct ip6_hdr *ip6h_inner;
619 struct icmp6hdr *icmp6h;
620 char *hbuff;
621 struct sockaddr_in6 sa;
622 struct asrecord rec;
623 GeoIPRecord *gir;
625 if (ip6h->ip6_nxt != 0x3a)
626 return PKT_NOT_FOR_US;
627 if (memcmp(&ip6h->ip6_dst, &(((const struct sockaddr_in6 *)
628 own)->sin6_addr), sizeof(ip6h->ip6_dst)))
629 return PKT_NOT_FOR_US;
631 icmp6h = (struct icmp6hdr *) (packet + sizeof(*ip6h));
632 if (icmp6h->icmp6_type != ICMPV6_TIME_EXCEED)
633 return PKT_NOT_FOR_US;
634 if (icmp6h->icmp6_code != ICMPV6_EXC_HOPLIMIT)
635 return PKT_NOT_FOR_US;
637 ip6h_inner = (struct ip6_hdr *) (packet + sizeof(*ip6h) + sizeof(*icmp6h));
638 if ((ntohl(ip6h_inner->ip6_flow) & 0x000fffff) != id)
639 return PKT_NOT_FOR_US;
641 hbuff = xzmalloc(NI_MAXHOST);
643 memset(&sa, 0, sizeof(sa));
644 sa.sin6_family = PF_INET6;
645 memcpy(&sa.sin6_addr, &ip6h->ip6_src, sizeof(ip6h->ip6_src));
647 getnameinfo((struct sockaddr *) &sa, sizeof(sa), hbuff, NI_MAXHOST,
648 NULL, 0, NI_NUMERICHOST);
650 memset(&rec, 0, sizeof(rec));
651 ret = aslookup(hbuff, &rec);
652 if (ret < 0)
653 panic("AS lookup error %d!\n", ret);
655 gir = GeoIP_record_by_ipnum_v6(gi_city, sa.sin6_addr);
656 if (!dns_resolv) {
657 if (strlen(rec.country) > 0 && gir) {
658 const char *city = make_n_a(gir->city);
660 printf("%s in AS%s (%s, %s, %s, %f, %f), %s %s (%s), %s", hbuff,
661 rec.number, rec.country,
662 GeoIP_country_name_by_ipnum_v6(gi_country, sa.sin6_addr),
663 city, gir->latitude, gir->longitude,
664 rec.prefix, rec.registry, rec.since, rec.name);
665 } else if (strlen(rec.country) > 0 && !gir) {
666 printf("%s in AS%s (%s, %s), %s %s (%s), %s", hbuff,
667 rec.number, rec.country,
668 GeoIP_country_name_by_ipnum_v6(gi_country, sa.sin6_addr),
669 rec.prefix, rec.registry, rec.since, rec.name);
670 } else {
671 printf("%s in unknown AS", hbuff);
673 } else {
674 struct hostent *hent = gethostbyaddr(&sa.sin6_addr,
675 sizeof(sa.sin6_addr),
676 PF_INET6);
678 if (strlen(rec.country) > 0 && gir) {
679 const char *city = make_n_a(gir->city);
680 printf("%s (%s) in AS%s (%s, %s, %s, %f, %f), %s %s (%s), %s",
681 (hent ? hent->h_name : hbuff), hbuff,
682 rec.number, rec.country,
683 GeoIP_country_name_by_ipnum_v6(gi_country, sa.sin6_addr),
684 city, gir->latitude, gir->longitude,
685 rec.prefix, rec.registry,
686 rec.since, rec.name);
687 } else if (strlen(rec.country) > 0 && !gir) {
688 printf("%s (%s) in AS%s (%s, %s), %s %s (%s), %s",
689 (hent ? hent->h_name : hbuff), hbuff,
690 rec.number, rec.country,
691 GeoIP_country_name_by_ipnum_v6(gi_country, sa.sin6_addr),
692 rec.prefix, rec.registry,
693 rec.since, rec.name);
694 } else {
695 printf("%s (%s) in unknown AS",
696 (hent ? hent->h_name : hbuff), hbuff);
700 xfree(hbuff);
702 return PKT_GOOD;
705 static int handle_packet(uint8_t *packet, size_t len, int ip, int ttl, int id,
706 struct sockaddr *own, int dns_resolv)
708 if (ip == 4)
709 return handle_ipv4_icmp(packet, len, ttl, id, own, dns_resolv);
710 else
711 return handle_ipv6_icmp(packet, len, ttl, id, own, dns_resolv);
714 static int do_trace(const struct ash_cfg *cfg)
716 int ttl, query, fd = -1, one = 1, ret, fd_cap, ifindex;
717 int is_okay = 0, id, timeout_poll;
718 uint8_t *packet, *packet_rcv;
719 ssize_t err, real_len, sd_len;
720 size_t len, len_rcv;
721 struct addrinfo hints, *ahead, *ai;
722 char *hbuff1, *hbuff2;
723 struct sockaddr_storage ss, sd;
724 struct sock_fprog bpf_ops;
725 struct ring dummy_ring;
726 struct pollfd pfd;
728 srand(time(NULL));
730 memset(&hints, 0, sizeof(hints));
731 hints.ai_family = PF_UNSPEC;
732 hints.ai_socktype = SOCK_STREAM;
733 hints.ai_protocol = IPPROTO_TCP;
734 hints.ai_flags = AI_NUMERICSERV;
736 ret = getaddrinfo(cfg->host, cfg->port, &hints, &ahead);
737 if (ret < 0) {
738 printf("Cannot get address info!\n");
739 return -EIO;
742 for (ai = ahead; ai != NULL && fd < 0; ai = ai->ai_next) {
743 if (!((ai->ai_family == PF_INET6 && cfg->ip == 6) ||
744 (ai->ai_family == PF_INET && cfg->ip == 4)))
745 continue;
746 fd = socket(ai->ai_family, SOCK_RAW, IPPROTO_RAW);
747 if (fd < 0)
748 continue;
749 fd_cap = pf_socket();
751 memset(&ss, 0, sizeof(ss));
752 ret = device_address(cfg->dev, ai->ai_family, &ss);
753 if (ret < 0)
754 panic("Cannot get own device address!\n");
756 ret = bind(fd, (struct sockaddr *) &ss, sizeof(ss));
757 if (ret < 0)
758 panic("Cannot bind socket!\n");
760 memset(&sd, 0, sizeof(sd));
761 memcpy(&sd, ai->ai_addr, ai->ai_addrlen);
762 if (ai->ai_family == PF_INET6) {
763 struct sockaddr_in6 *sd6 = (struct sockaddr_in6 *) &sd;
764 sd6->sin6_port = htons(0);
766 sd_len = ai->ai_addrlen;
768 break;
771 freeaddrinfo(ahead);
773 if (fd < 0) {
774 printf("Cannot create socket! Does remote support IPv%d?!\n",
775 cfg->ip);
776 return -EIO;
779 len = cfg->totlen;
780 if (cfg->ip == 4) {
781 if (len < sizeof(struct iphdr) + sizeof(struct tcphdr)) {
782 len = sizeof(struct iphdr) + sizeof(struct tcphdr);
783 if (cfg->payload)
784 len += strlen(cfg->payload);
786 } else {
787 if (len < sizeof(struct ip6_hdr) + sizeof(struct tcphdr)) {
788 len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
789 if (cfg->payload)
790 len += strlen(cfg->payload);
794 len_rcv = device_mtu(cfg->dev);
795 if (len >= len_rcv)
796 panic("Packet len exceeds device MTU!\n");
798 packet = xmalloc(len);
799 packet_rcv = xmalloc(len_rcv);
801 hbuff1 = xzmalloc(256);
802 getnameinfo((struct sockaddr *) &sd, sizeof(sd), hbuff1, 256,
803 NULL, 0, NI_NUMERICHOST);
805 hbuff2 = xzmalloc(256);
806 getnameinfo((struct sockaddr *) &ss, sizeof(ss), hbuff2, 256,
807 NULL, 0, NI_NUMERICHOST);
809 ret = setsockopt(fd, cfg->ip == 4 ? IPPROTO_IP : IPPROTO_IPV6,
810 IP_HDRINCL, &one, sizeof(one));
811 if (ret < 0)
812 panic("Kernel does not support IP_HDRINCL!\n");
814 printf("AS path IPv%d TCP trace from %s to %s:%s (%s) with len %zu "
815 "Bytes, %u max hops\n", cfg->ip, hbuff2, hbuff1, cfg->port,
816 cfg->host, len, cfg->max_ttl);
818 printf("Using flags SYN:%d,ACK:%d,ECN:%d,FIN:%d,PSH:%d,RST:%d,URG:%d\n",
819 cfg->syn, cfg->ack, cfg->ecn, cfg->fin, cfg->psh, cfg->rst,
820 cfg->urg);
822 if (cfg->payload)
823 printf("With payload: \'%s\'\n", cfg->payload);
825 fflush(stdout);
827 xfree(hbuff1);
828 xfree(hbuff2);
830 hbuff1 = hbuff2 = NULL;
832 enable_kernel_bpf_jit_compiler();
834 memset(&bpf_ops, 0, sizeof(bpf_ops));
835 if (cfg->ip == 4) {
836 bpf_ops.filter = ipv4_icmp_type_11;
837 bpf_ops.len = (sizeof(ipv4_icmp_type_11) /
838 sizeof(ipv4_icmp_type_11[0]));
839 } else {
840 bpf_ops.filter = ipv6_icmp6_type_3;
841 bpf_ops.len = (sizeof(ipv6_icmp6_type_3) /
842 sizeof(ipv6_icmp6_type_3[0]));
845 bpf_attach_to_sock(fd_cap, &bpf_ops);
846 ifindex = device_ifindex(cfg->dev);
847 bind_rx_ring(fd_cap, &dummy_ring, ifindex);
848 prepare_polling(fd_cap, &pfd);
850 timeout_poll = (cfg->timeout > 0 ? cfg->timeout : 3) * 1000;
852 for (ttl = cfg->init_ttl; ttl <= cfg->max_ttl; ++ttl) {
853 int icmp = 0;
854 is_okay = 0;
856 if ((ttl == cfg->init_ttl && !show_pkt) ||
857 (ttl > cfg->init_ttl)) {
858 printf("%2d: ", ttl);
859 fflush(stdout);
861 retry:
862 for (query = 0; query < cfg->queries && !is_okay; ++query) {
863 id = assemble_packet_or_die(packet, len, ttl, icmp, cfg,
864 (struct sockaddr *) &sd,
865 (struct sockaddr *) &ss);
866 if (ttl == cfg->init_ttl && query == 0 && show_pkt) {
867 struct pkt_buff *pkt;
869 printf("Original packet:\n");
871 pkt = pkt_alloc(packet, len);
872 hex_ascii(pkt);
873 tprintf_flush();
874 pkt_free(pkt);
876 printf("\n%2d: ", ttl);
877 fflush(stdout);
880 err = sendto(fd, packet, len, 0, (struct sockaddr *) &sd,
881 sd_len);
882 if (err < 0)
883 panic("sendto failed: %s\n", strerror(errno));
885 err = poll(&pfd, 1, timeout_poll);
886 if (err > 0 && pfd.revents & POLLIN) {
887 real_len = recvfrom(fd_cap, packet_rcv, len_rcv,
888 0, NULL, NULL);
889 if (real_len < sizeof(struct ethhdr) +
890 (cfg->ip ? sizeof(struct iphdr) +
891 sizeof(struct icmphdr) :
892 sizeof(struct ip6_hdr) +
893 sizeof(struct icmp6hdr)))
894 continue;
896 is_okay = handle_packet(packet_rcv + sizeof(struct ethhdr),
897 real_len - sizeof(struct ethhdr),
898 cfg->ip, ttl, id,
899 (struct sockaddr *) &ss,
900 cfg->dns_resolv);
901 if (is_okay && show_pkt) {
902 struct pkt_buff *pkt;
904 printf("\n Received packet:\n");
906 pkt = pkt_alloc(packet_rcv, real_len);
907 hex_ascii(pkt);
908 tprintf_flush();
909 pkt_free(pkt);
911 } else {
912 printf("* ");
913 fflush(stdout);
914 is_okay = 0;
918 if (is_okay == 0 && icmp == 0) {
919 icmp = 1;
920 goto retry;
923 printf("\n");
924 fflush(stdout);
927 close(fd_cap);
928 close(fd);
930 xfree(packet);
931 xfree(packet_rcv);
933 return 0;
936 static void parse_whois_or_die(struct ash_cfg *cfg)
938 int fd;
939 ssize_t ret;
940 char tmp[512], *ptr, *ptr2;
942 fd = open_or_die(WHOIS_SERVER_SOURCE, O_RDONLY);
944 memset(tmp, 0, sizeof(tmp));
945 while ((ret = read(fd, tmp, sizeof(tmp))) > 0) {
946 tmp[sizeof(tmp) - 1] = 0;
947 ptr = skips(tmp);
948 ptr2 = ptr;
949 while (*ptr2 != ' ' && ptr2 < &tmp[sizeof(tmp) - 1])
950 ptr2++;
951 if (*ptr2 != ' ')
952 panic("Parser error!\n");
953 *ptr2 = 0;
954 cfg->whois = xstrdup(ptr);
955 ptr = ptr2 + 1;
956 if (ptr >= &tmp[sizeof(tmp) - 1])
957 panic("Parser error!\n");
958 ptr = skips(ptr);
959 ptr[strlen(ptr) - 1] = 0;
960 cfg->whois_port = xstrdup(ptr);
961 break;
964 close(fd);
967 int main(int argc, char **argv)
969 int c, opt_index, ret;
970 struct ash_cfg cfg;
971 char *path_city_db = NULL, *path_country_db = NULL;
973 setfsuid(getuid());
974 setfsgid(getgid());
976 memset(&cfg, 0, sizeof(cfg));
977 cfg.init_ttl = 1;
978 cfg.max_ttl = 30;
979 cfg.queries = 3;
980 cfg.timeout = 3;
981 cfg.ip = 4;
982 cfg.payload = NULL;
983 cfg.dev = xstrdup("eth0");
984 cfg.port = xstrdup("80");
986 while ((c = getopt_long(argc, argv, short_options, long_options,
987 &opt_index)) != EOF) {
988 switch (c) {
989 case 'h':
990 help();
991 break;
992 case 'v':
993 version();
994 break;
995 case 'H':
996 cfg.host = xstrdup(optarg);
997 break;
998 case 'p':
999 if (cfg.port)
1000 xfree(cfg.port);
1001 cfg.port = xstrdup(optarg);
1002 break;
1003 case 'n':
1004 cfg.dns_resolv = 0;
1005 break;
1006 case '4':
1007 cfg.ip = 4;
1008 break;
1009 case '6':
1010 cfg.ip = 6;
1011 break;
1012 case 'Z':
1013 show_pkt = 1;
1014 break;
1015 case 'N':
1016 cfg.dns_resolv = 1;
1017 break;
1018 case 'f':
1019 cfg.init_ttl = atoi(optarg);
1020 if (cfg.init_ttl <= 0)
1021 help();
1022 break;
1023 case 'm':
1024 cfg.max_ttl = atoi(optarg);
1025 if (cfg.max_ttl <= 0)
1026 help();
1027 break;
1028 case 'i':
1029 case 'd':
1030 if (cfg.dev)
1031 xfree(cfg.dev);
1032 cfg.dev = xstrdup(optarg);
1033 break;
1034 case 'q':
1035 cfg.queries = atoi(optarg);
1036 if (cfg.queries <= 0)
1037 help();
1038 break;
1039 case 'x':
1040 cfg.timeout = atoi(optarg);
1041 if (cfg.timeout <= 0)
1042 help();
1043 break;
1044 case 'S':
1045 cfg.syn = 1;
1046 break;
1047 case 'A':
1048 cfg.ack = 1;
1049 break;
1050 case 'F':
1051 cfg.fin = 1;
1052 break;
1053 case 'U':
1054 cfg.urg = 1;
1055 break;
1056 case 'P':
1057 cfg.psh = 1;
1058 break;
1059 case 'R':
1060 cfg.rst = 1;
1061 break;
1062 case 'E':
1063 cfg.syn = 1;
1064 cfg.ecn = 1;
1065 break;
1066 case 't':
1067 cfg.tos = atoi(optarg);
1068 if (cfg.tos < 0)
1069 help();
1070 break;
1071 case 'G':
1072 cfg.nofrag = 1;
1073 break;
1074 case 'X':
1075 cfg.payload = xstrdup(optarg);
1076 break;
1077 case 'l':
1078 cfg.totlen = atoi(optarg);
1079 if (cfg.totlen <= 0)
1080 help();
1081 break;
1082 case 'w':
1083 cfg.whois = xstrdup(optarg);
1084 break;
1085 case 'W':
1086 cfg.whois_port = xstrdup(optarg);
1087 break;
1088 case 'L':
1089 path_city_db = xstrdup(optarg);
1090 break;
1091 case 'K':
1092 path_country_db = xstrdup(optarg);
1093 break;
1094 case '?':
1095 switch (optopt) {
1096 case 'H':
1097 case 'p':
1098 case 'L':
1099 case 'K':
1100 case 'f':
1101 case 'm':
1102 case 'i':
1103 case 'd':
1104 case 'q':
1105 case 'x':
1106 case 'X':
1107 case 't':
1108 case 'l':
1109 case 'w':
1110 case 'W':
1111 panic("Option -%c requires an argument!\n",
1112 optopt);
1113 default:
1114 if (isprint(optopt))
1115 printf("Unknown option character `0x%X\'!\n", optopt);
1116 die();
1118 default:
1119 break;
1123 if (argc < 3 ||
1124 !cfg.host || !cfg.port ||
1125 cfg.init_ttl > cfg.max_ttl ||
1126 cfg.init_ttl > MAXTTL ||
1127 cfg.max_ttl > MAXTTL)
1128 help();
1130 if (!device_up_and_running(cfg.dev))
1131 panic("Networking device not up and running!\n");
1132 if (!cfg.whois || !cfg.whois_port)
1133 parse_whois_or_die(&cfg);
1134 if (device_mtu(cfg.dev) <= cfg.totlen)
1135 panic("Packet larger than device MTU!\n");
1137 register_signal(SIGHUP, signal_handler);
1139 tprintf_init();
1141 ret = aslookup_prepare(cfg.whois, cfg.whois_port);
1142 if (ret < 0)
1143 panic("Cannot resolve whois server!\n");
1145 if (path_country_db)
1146 gi_country = GeoIP_open(path_country_db, GEOIP_MMAP_CACHE);
1147 else
1148 gi_country = GeoIP_open_type(cfg.ip == 4 ?
1149 GEOIP_COUNTRY_EDITION :
1150 GEOIP_COUNTRY_EDITION_V6,
1151 GEOIP_MMAP_CACHE);
1153 if (path_city_db)
1154 gi_city = GeoIP_open(path_city_db, GEOIP_MMAP_CACHE);
1155 else
1156 gi_city = GeoIP_open_type(cfg.ip == 4 ?
1157 GEOIP_CITY_EDITION_REV1 :
1158 GEOIP_CITY_EDITION_REV1_V6,
1159 GEOIP_MMAP_CACHE);
1161 if (!gi_country || !gi_city)
1162 panic("Cannot open GeoIP database! Wrong path?!\n");
1164 GeoIP_set_charset(gi_country, GEOIP_CHARSET_UTF8);
1165 GeoIP_set_charset(gi_city, GEOIP_CHARSET_UTF8);
1167 ret = do_trace(&cfg);
1169 GeoIP_delete(gi_city);
1170 GeoIP_delete(gi_country);
1172 tprintf_cleanup();
1174 free(cfg.whois_port);
1175 free(cfg.whois);
1176 free(cfg.dev);
1177 free(cfg.host);
1178 free(cfg.port);
1179 free(cfg.payload);
1180 free(path_city_db);
1181 free(path_country_db);
1183 return ret;