]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/traceroute.c
ip/torrent: remove unneeded assignment
[plan9front.git] / sys / src / cmd / ip / traceroute.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <bio.h>
5 #include <ndb.h>
6 #include <ip.h>
7 #include "icmp.h"
8
9 enum{
10         Maxstring=      128,
11         Maxpath=        256,
12 };
13
14 typedef struct DS DS;
15 struct DS {
16         /* dial string */
17         char    buf[Maxstring];
18         char    *netdir;
19         char    *proto;
20         char    *rem;
21 };
22
23 char *argv0;
24 int debug;
25
26 void    histogram(long *t, int n, int buckets, long lo, long hi);
27
28 void
29 usage(void)
30 {
31         fprint(2,
32 "usage: %s [-n][-a tries][-h buckets][-t ttl][-x net] [protocol!]destination\n",
33                 argv0);
34         exits("usage");
35 }
36
37 static int
38 csquery(DS *ds, char *clone, char *dest)
39 {
40         int n, fd;
41         char *p, buf[Maxstring];
42
43         /*
44          *  open connection server
45          */
46         snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
47         fd = open(buf, ORDWR);
48         if(fd < 0){
49                 if(!isdigit(*dest)){
50                         werrstr("can't translate");
51                         return -1;
52                 }
53
54                 /* no connection server, don't translate */
55                 snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
56                 strcpy(dest, ds->rem);
57                 return 0;
58         }
59
60         /*
61          *  ask connection server to translate
62          */
63         sprint(buf, "%s!%s", ds->proto, ds->rem);
64         if(write(fd, buf, strlen(buf)) < 0){
65                 close(fd);
66                 return -1;
67         }
68
69         /*
70          *  get an address.
71          */
72         seek(fd, 0, 0);
73         n = read(fd, buf, sizeof(buf) - 1);
74         close(fd);
75         if(n <= 0){
76                 werrstr("problem with cs");
77                 return -1;
78         }
79
80         buf[n] = 0;
81         p = strchr(buf, ' ');
82         if(p == 0){
83                 werrstr("problem with cs");
84                 return -1;
85         }
86
87         *p++ = 0;
88         strcpy(clone, buf);
89         strcpy(dest, p);
90         return 0;
91 }
92
93 /*
94  *  call the dns process and have it try to resolve the mx request
95  */
96 static int
97 dodnsquery(DS *ds, char *ip, char *dom)
98 {
99         char *p;
100         Ndbtuple *t, *nt;
101
102         p = strchr(ip, '!');
103         if(p)
104                 *p = 0;
105
106         t = dnsquery(ds->netdir, ip, "ptr");
107         for(nt = t; nt != nil; nt = nt->entry)
108                 if(strcmp(nt->attr, "dom") == 0){
109                         strcpy(dom, nt->val);
110                         ndbfree(t);
111                         return 0;
112                 }
113         ndbfree(t);
114         return -1;
115 }
116
117 /*  for connection oriented protocols (il, tcp) we just need
118  *  to try dialing.  resending is up to it.
119  */
120 static int
121 tcpilprobe(int cfd, int dfd, char *dest, int interval)
122 {
123         int n;
124         char msg[Maxstring];
125
126         USED(dfd);
127
128         n = snprint(msg, sizeof msg, "connect %s", dest);
129         alarm(interval);
130         n = write(cfd, msg, n);
131         alarm(0);
132         return n;
133 }
134
135 /*
136  *  for udp, we keep sending to an improbable port
137  *  till we timeout or someone complains
138  */
139 static int
140 udpprobe(int cfd, int dfd, char *dest, int interval)
141 {
142         int n, i, rv;
143         char msg[Maxstring];
144         char err[Maxstring];
145
146         seek(cfd, 0, 0);
147         n = snprint(msg, sizeof msg, "connect %s", dest);
148         if(write(cfd, msg, n)< 0)
149                 return -1;
150
151         rv = -1;
152         for(i = 0; i < 3; i++){
153                 alarm(interval/3);
154                 if(write(dfd, "boo hoo ", 8) < 0)
155                         break;
156                 /*
157                  *  a hangup due to an error looks like 3 eofs followed
158                  *  by a real error.  this is a qio.c qbread() strangeness
159                  *  done for pipes.
160                  */
161                 do {
162                         n = read(dfd, msg, sizeof(msg)-1);
163                 } while(n == 0);
164                 alarm(0);
165                 if(n > 0){
166                         rv = 0;
167                         break;
168                 }
169                 errstr(err, sizeof err);
170                 if(strstr(err, "alarm") == 0){
171                         werrstr(err);
172                         break;
173                 }
174                 werrstr(err);
175         }
176         alarm(0);
177         return rv;
178 }
179
180 #define MSG "traceroute probe"
181 #define MAGIC 0xdead
182
183 /* ICMPv4 only */
184 static int
185 icmpprobe(int cfd, int dfd, char *dest, int interval)
186 {
187         int x, i, n, len, rv;
188         char buf[512], err[Maxstring], msg[Maxstring];
189         Icmphdr *ip;
190
191         seek(cfd, 0, 0);
192         n = snprint(msg, sizeof msg, "connect %s", dest);
193         if(write(cfd, msg, n)< 0)
194                 return -1;
195
196         rv = -1;
197         ip = (Icmphdr *)(buf + IPV4HDR_LEN);
198         for(i = 0; i < 3; i++){
199                 alarm(interval/3);
200                 ip->type = EchoRequest;
201                 ip->code = 0;
202                 strcpy((char*)ip->data, MSG);
203                 ip->seq[0] = MAGIC;
204                 ip->seq[1] = MAGIC>>8;
205                 len = IPV4HDR_LEN + ICMP_HDRSIZE + sizeof(MSG);
206
207                 /* send a request */
208                 if(write(dfd, buf, len) < len)
209                         break;
210
211                 /* wait for reply */
212                 n = read(dfd, buf, sizeof(buf));
213                 alarm(0);
214                 if(n < 0){
215                         errstr(err, sizeof err);
216                         if(strstr(err, "alarm") == 0){
217                                 werrstr(err);
218                                 break;
219                         }
220                         werrstr(err);
221                         continue;
222                 }
223                 x = (ip->seq[1]<<8) | ip->seq[0];
224                 if(n >= len && ip->type == EchoReply && x == MAGIC &&
225                     strcmp((char*)ip->data, MSG) == 0){
226                         rv = 0;
227                         break;
228                 }
229         }
230         alarm(0);
231         return rv;
232 }
233
234 static void
235 catch(void *a, char *msg)
236 {
237         USED(a);
238         if(strstr(msg, "alarm"))
239                 noted(NCONT);
240         else
241                 noted(NDFLT);
242 }
243
244 static int
245 call(DS *ds, char *clone, char *dest, int ttl, long *interval)
246 {
247         int cfd, dfd, rv, n;
248         char msg[Maxstring];
249         char file[Maxstring];
250         vlong start;
251
252         notify(catch);
253
254         /* start timing */
255         start = nsec()/1000;
256         rv = -1;
257
258         cfd = open(clone, ORDWR);
259         if(cfd < 0){
260                 werrstr("%s: %r", clone);
261                 return -1;
262         }
263         dfd = -1;
264
265         /* get conversation number */
266         n = read(cfd, msg, sizeof(msg)-1);
267         if(n <= 0)
268                 goto out;
269         msg[n] = 0;
270
271         /* open data file */
272         sprint(file, "%s/%s/%s/data", ds->netdir, ds->proto, msg);
273         dfd = open(file, ORDWR);
274         if(dfd < 0)
275                 goto out;
276
277         /* set ttl */
278         if(ttl)
279                 fprint(cfd, "ttl %d", ttl);
280
281         /* probe */
282         if(strcmp(ds->proto, "udp") == 0)
283                 rv = udpprobe(cfd, dfd, dest, 3000);
284         else if(strcmp(ds->proto, "icmp") == 0)
285                 rv = icmpprobe(cfd, dfd, dest, 3000);
286         else    /* il and tcp */
287                 rv = tcpilprobe(cfd, dfd, dest, 3000);
288 out:
289         /* turn off alarms */
290         alarm(0);
291         *interval = nsec()/1000 - start;
292         close(cfd);
293         close(dfd);
294         return rv;
295 }
296
297 /*
298  *  parse a dial string.  default netdir is /net.
299  *  default proto is tcp.
300  */
301 static void
302 dial_string_parse(char *str, DS *ds)
303 {
304         char *p, *p2;
305
306         strncpy(ds->buf, str, Maxstring);
307         ds->buf[Maxstring-3] = 0;
308
309         p = strchr(ds->buf, '!');
310         if(p == 0) {
311                 ds->netdir = 0;
312                 ds->proto = "tcp";
313                 ds->rem = ds->buf;
314         } else {
315                 if(*ds->buf != '/'){
316                         ds->netdir = 0;
317                         ds->proto = ds->buf;
318                 } else {
319                         for(p2 = p; *p2 != '/'; p2--)
320                                 ;
321                         *p2++ = 0;
322                         ds->netdir = ds->buf;
323                         ds->proto = p2;
324                 }
325                 *p = 0;
326                 ds->rem = p + 1;
327         }
328         if(strchr(ds->rem, '!') == 0)
329                 strcat(ds->rem, "!32767");
330 }
331
332 void
333 main(int argc, char **argv)
334 {
335         int buckets, ttl, j, done, tries, notranslate;
336         long lo, hi, sum, x;
337         long *t;
338         char *net, *p;
339         char clone[Maxpath], dest[Maxstring], hop[Maxstring], dom[Maxstring];
340         char err[Maxstring];
341         DS ds;
342
343         buckets = 0;
344         tries = 3;
345         notranslate = 0;
346         net = "/net";
347         ttl = 1;
348         ARGBEGIN{
349         case 'a':
350                 tries = atoi(EARGF(usage()));
351                 break;
352         case 'd':
353                 debug++;
354                 break;
355         case 'h':
356                 buckets = atoi(EARGF(usage()));
357                 break;
358         case 'n':
359                 notranslate++;
360                 break;
361         case 't':
362                 ttl = atoi(EARGF(usage()));
363                 break;
364         case 'x':
365                 net = EARGF(usage());
366                 break;
367         default:
368                 usage();
369         }ARGEND;
370
371         if(argc < 1)
372                 usage();
373
374         t = malloc(tries*sizeof(ulong));
375
376         dial_string_parse(argv[0], &ds);
377
378         if(ds.netdir == 0)
379                 ds.netdir = net;
380         if(csquery(&ds, clone, dest) < 0){
381                 fprint(2, "%s: %s: %r\n", argv0, argv[0]);
382                 exits(0);
383         }
384         print("trying %s/%s!%s\n\n", ds.netdir, ds.proto, dest);
385         print("                       round trip times in µs\n");
386         print("                        low      avg     high\n");
387         print("                     --------------------------\n");
388
389         done = 0;
390         for(; ttl < 32; ttl++){
391                 for(j = 0; j < tries; j++){
392                         if(call(&ds, clone, dest, ttl, &t[j]) >= 0){
393                                 if(debug)
394                                         print("%ld %s\n", t[j], dest);
395                                 strcpy(hop, dest);
396                                 done = 1;
397                                 continue;
398                         }
399                         errstr(err, sizeof err);
400                         if(strstr(err, "refused")){
401                                 strcpy(hop, dest);
402                                 p = strchr(hop, '!');
403                                 if(p)
404                                         *p = 0;
405                                 done = 1;
406                         } else if(strstr(err, "unreachable")){
407                                 snprint(hop, sizeof(hop), "%s", err);
408                                 p = strchr(hop, '!');
409                                 if(p)
410                                         *p = 0;
411                                 done = 1;
412                         } else if(strncmp(err, "ttl exceeded at ", 16) == 0)
413                                 strcpy(hop, err+16);
414                         else {
415                                 strcpy(hop, "*");
416                                 break;
417                         }
418                         if(debug)
419                                 print("%ld %s\n", t[j], hop);
420                 }
421                 if(strcmp(hop, "*") == 0){
422                         print("*\n");
423                         continue;
424                 }
425                 lo = 10000000;
426                 hi = 0;
427                 sum = 0;
428                 for(j = 0; j < tries; j++){
429                         x = t[j];
430                         sum += x;
431                         if(x < lo)
432                                 lo = x;
433                         if(x > hi)
434                                 hi = x;
435                 }
436                 if(notranslate == 1 || dodnsquery(&ds, hop, dom) < 0)
437                         dom[0] = 0;
438                 /* don't truncate: ipv6 addresses can be quite long */
439                 print("%-18s %8ld %8ld %8ld %s\n", hop, lo, sum/tries, hi, dom);
440                 if(buckets)
441                         histogram(t, tries, buckets, lo, hi);
442                 if(done)
443                         break;
444         }
445         exits(0);
446 }
447
448 char *order = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
449
450 void
451 histogram(long *t, int n, int buckets, long lo, long hi)
452 {
453         int i, j, empty;
454         long span;
455         static char *bar;
456         char *p;
457         char x[64];
458
459         if(bar == nil)
460                 bar = malloc(n+1);
461
462         print("+++++++++++++++++++++++\n");
463         span = (hi-lo)/buckets;
464         span++;
465         empty = 0;
466         for(i = 0; i < buckets; i++){
467                 p = bar;
468                 for(j = 0; j < n; j++)
469                         if(t[j] >= lo+i*span && t[j] <= lo+(i+1)*span)
470                                 *p++ = order[j];
471                 *p = 0;
472                 if(p != bar){
473                         snprint(x, sizeof x, "[%ld-%ld]", lo+i*span, lo+(i+1)*span);
474                         print("%-16s %s\n", x, bar);
475                         empty = 0;
476                 } else if(!empty){
477                         print("...\n");
478                         empty = 1;
479                 }
480         }
481         print("+++++++++++++++++++++++\n");
482 }