diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/ip/traceroute.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/ip/traceroute.c')
-rwxr-xr-x | sys/src/cmd/ip/traceroute.c | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/sys/src/cmd/ip/traceroute.c b/sys/src/cmd/ip/traceroute.c new file mode 100755 index 000000000..33b6678b2 --- /dev/null +++ b/sys/src/cmd/ip/traceroute.c @@ -0,0 +1,482 @@ +#include <u.h> +#include <libc.h> +#include <ctype.h> +#include <bio.h> +#include <ndb.h> +#include <ip.h> +#include "icmp.h" + +enum{ + Maxstring= 128, + Maxpath= 256, +}; + +typedef struct DS DS; +struct DS { + /* dial string */ + char buf[Maxstring]; + char *netdir; + char *proto; + char *rem; +}; + +char *argv0; +int debug; + +void histogram(long *t, int n, int buckets, long lo, long hi); + +void +usage(void) +{ + fprint(2, +"usage: %s [-n][-a tries][-h buckets][-t ttl][-x net] [protocol!]destination\n", + argv0); + exits("usage"); +} + +static int +csquery(DS *ds, char *clone, char *dest) +{ + int n, fd; + char *p, buf[Maxstring]; + + /* + * open connection server + */ + snprint(buf, sizeof(buf), "%s/cs", ds->netdir); + fd = open(buf, ORDWR); + if(fd < 0){ + if(!isdigit(*dest)){ + werrstr("can't translate"); + return -1; + } + + /* no connection server, don't translate */ + snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto); + strcpy(dest, ds->rem); + return 0; + } + + /* + * ask connection server to translate + */ + sprint(buf, "%s!%s", ds->proto, ds->rem); + if(write(fd, buf, strlen(buf)) < 0){ + close(fd); + return -1; + } + + /* + * get an address. + */ + seek(fd, 0, 0); + n = read(fd, buf, sizeof(buf) - 1); + close(fd); + if(n <= 0){ + werrstr("problem with cs"); + return -1; + } + + buf[n] = 0; + p = strchr(buf, ' '); + if(p == 0){ + werrstr("problem with cs"); + return -1; + } + + *p++ = 0; + strcpy(clone, buf); + strcpy(dest, p); + return 0; +} + +/* + * call the dns process and have it try to resolve the mx request + */ +static int +dodnsquery(DS *ds, char *ip, char *dom) +{ + char *p; + Ndbtuple *t, *nt; + + p = strchr(ip, '!'); + if(p) + *p = 0; + + t = dnsquery(ds->netdir, ip, "ptr"); + for(nt = t; nt != nil; nt = nt->entry) + if(strcmp(nt->attr, "dom") == 0){ + strcpy(dom, nt->val); + ndbfree(t); + return 0; + } + ndbfree(t); + return -1; +} + +/* for connection oriented protocols (il, tcp) we just need + * to try dialing. resending is up to it. + */ +static int +tcpilprobe(int cfd, int dfd, char *dest, int interval) +{ + int n; + char msg[Maxstring]; + + USED(dfd); + + n = snprint(msg, sizeof msg, "connect %s", dest); + alarm(interval); + n = write(cfd, msg, n); + alarm(0); + return n; +} + +/* + * for udp, we keep sending to an improbable port + * till we timeout or someone complains + */ +static int +udpprobe(int cfd, int dfd, char *dest, int interval) +{ + int n, i, rv; + char msg[Maxstring]; + char err[Maxstring]; + + seek(cfd, 0, 0); + n = snprint(msg, sizeof msg, "connect %s", dest); + if(write(cfd, msg, n)< 0) + return -1; + + rv = -1; + for(i = 0; i < 3; i++){ + alarm(interval/3); + if(write(dfd, "boo hoo ", 8) < 0) + break; + /* + * a hangup due to an error looks like 3 eofs followed + * by a real error. this is a qio.c qbread() strangeness + * done for pipes. + */ + do { + n = read(dfd, msg, sizeof(msg)-1); + } while(n == 0); + alarm(0); + if(n > 0){ + rv = 0; + break; + } + errstr(err, sizeof err); + if(strstr(err, "alarm") == 0){ + werrstr(err); + break; + } + werrstr(err); + } + alarm(0); + return rv; +} + +#define MSG "traceroute probe" +#define MAGIC 0xdead + +/* ICMPv4 only */ +static int +icmpprobe(int cfd, int dfd, char *dest, int interval) +{ + int x, i, n, len, rv; + char buf[512], err[Maxstring], msg[Maxstring]; + Icmphdr *ip; + + seek(cfd, 0, 0); + n = snprint(msg, sizeof msg, "connect %s", dest); + if(write(cfd, msg, n)< 0) + return -1; + + rv = -1; + ip = (Icmphdr *)(buf + IPV4HDR_LEN); + for(i = 0; i < 3; i++){ + alarm(interval/3); + ip->type = EchoRequest; + ip->code = 0; + strcpy((char*)ip->data, MSG); + ip->seq[0] = MAGIC; + ip->seq[1] = MAGIC>>8; + len = IPV4HDR_LEN + ICMP_HDRSIZE + sizeof(MSG); + + /* send a request */ + if(write(dfd, buf, len) < len) + break; + + /* wait for reply */ + n = read(dfd, buf, sizeof(buf)); + alarm(0); + if(n < 0){ + errstr(err, sizeof err); + if(strstr(err, "alarm") == 0){ + werrstr(err); + break; + } + werrstr(err); + continue; + } + x = (ip->seq[1]<<8) | ip->seq[0]; + if(n >= len && ip->type == EchoReply && x == MAGIC && + strcmp((char*)ip->data, MSG) == 0){ + rv = 0; + break; + } + } + alarm(0); + return rv; +} + +static void +catch(void *a, char *msg) +{ + USED(a); + if(strstr(msg, "alarm")) + noted(NCONT); + else + noted(NDFLT); +} + +static int +call(DS *ds, char *clone, char *dest, int ttl, long *interval) +{ + int cfd, dfd, rv, n; + char msg[Maxstring]; + char file[Maxstring]; + vlong start; + + notify(catch); + + /* start timing */ + start = nsec()/1000; + rv = -1; + + cfd = open(clone, ORDWR); + if(cfd < 0){ + werrstr("%s: %r", clone); + return -1; + } + dfd = -1; + + /* get conversation number */ + n = read(cfd, msg, sizeof(msg)-1); + if(n <= 0) + goto out; + msg[n] = 0; + + /* open data file */ + sprint(file, "%s/%s/%s/data", ds->netdir, ds->proto, msg); + dfd = open(file, ORDWR); + if(dfd < 0) + goto out; + + /* set ttl */ + if(ttl) + fprint(cfd, "ttl %d", ttl); + + /* probe */ + if(strcmp(ds->proto, "udp") == 0) + rv = udpprobe(cfd, dfd, dest, 3000); + else if(strcmp(ds->proto, "icmp") == 0) + rv = icmpprobe(cfd, dfd, dest, 3000); + else /* il and tcp */ + rv = tcpilprobe(cfd, dfd, dest, 3000); +out: + /* turn off alarms */ + alarm(0); + *interval = nsec()/1000 - start; + close(cfd); + close(dfd); + return rv; +} + +/* + * parse a dial string. default netdir is /net. + * default proto is tcp. + */ +static void +dial_string_parse(char *str, DS *ds) +{ + char *p, *p2; + + strncpy(ds->buf, str, Maxstring); + ds->buf[Maxstring-3] = 0; + + p = strchr(ds->buf, '!'); + if(p == 0) { + ds->netdir = 0; + ds->proto = "tcp"; + ds->rem = ds->buf; + } else { + if(*ds->buf != '/'){ + ds->netdir = 0; + ds->proto = ds->buf; + } else { + for(p2 = p; *p2 != '/'; p2--) + ; + *p2++ = 0; + ds->netdir = ds->buf; + ds->proto = p2; + } + *p = 0; + ds->rem = p + 1; + } + if(strchr(ds->rem, '!') == 0) + strcat(ds->rem, "!32767"); +} + +void +main(int argc, char **argv) +{ + int buckets, ttl, j, done, tries, notranslate; + long lo, hi, sum, x; + long *t; + char *net, *p; + char clone[Maxpath], dest[Maxstring], hop[Maxstring], dom[Maxstring]; + char err[Maxstring]; + DS ds; + + buckets = 0; + tries = 3; + notranslate = 0; + net = "/net"; + ttl = 1; + ARGBEGIN{ + case 'a': + tries = atoi(EARGF(usage())); + break; + case 'd': + debug++; + break; + case 'h': + buckets = atoi(EARGF(usage())); + break; + case 'n': + notranslate++; + break; + case 't': + ttl = atoi(EARGF(usage())); + break; + case 'x': + net = EARGF(usage()); + break; + default: + usage(); + }ARGEND; + + if(argc < 1) + usage(); + + t = malloc(tries*sizeof(ulong)); + + dial_string_parse(argv[0], &ds); + + if(ds.netdir == 0) + ds.netdir = net; + if(csquery(&ds, clone, dest) < 0){ + fprint(2, "%s: %s: %r\n", argv0, argv[0]); + exits(0); + } + print("trying %s/%s!%s\n\n", ds.netdir, ds.proto, dest); + print(" round trip times in µs\n"); + print(" low avg high\n"); + print(" --------------------------\n"); + + done = 0; + for(; ttl < 32; ttl++){ + for(j = 0; j < tries; j++){ + if(call(&ds, clone, dest, ttl, &t[j]) >= 0){ + if(debug) + print("%ld %s\n", t[j], dest); + strcpy(hop, dest); + done = 1; + continue; + } + errstr(err, sizeof err); + if(strstr(err, "refused")){ + strcpy(hop, dest); + p = strchr(hop, '!'); + if(p) + *p = 0; + done = 1; + } else if(strstr(err, "unreachable")){ + snprint(hop, sizeof(hop), "%s", err); + p = strchr(hop, '!'); + if(p) + *p = 0; + done = 1; + } else if(strncmp(err, "ttl exceeded at ", 16) == 0) + strcpy(hop, err+16); + else { + strcpy(hop, "*"); + break; + } + if(debug) + print("%ld %s\n", t[j], hop); + } + if(strcmp(hop, "*") == 0){ + print("*\n"); + continue; + } + lo = 10000000; + hi = 0; + sum = 0; + for(j = 0; j < tries; j++){ + x = t[j]; + sum += x; + if(x < lo) + lo = x; + if(x > hi) + hi = x; + } + if(notranslate == 1 || dodnsquery(&ds, hop, dom) < 0) + dom[0] = 0; + /* don't truncate: ipv6 addresses can be quite long */ + print("%-18s %8ld %8ld %8ld %s\n", hop, lo, sum/tries, hi, dom); + if(buckets) + histogram(t, tries, buckets, lo, hi); + if(done) + break; + } + exits(0); +} + +char *order = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +void +histogram(long *t, int n, int buckets, long lo, long hi) +{ + int i, j, empty; + long span; + static char *bar; + char *p; + char x[64]; + + if(bar == nil) + bar = malloc(n+1); + + print("+++++++++++++++++++++++\n"); + span = (hi-lo)/buckets; + span++; + empty = 0; + for(i = 0; i < buckets; i++){ + p = bar; + for(j = 0; j < n; j++) + if(t[j] >= lo+i*span && t[j] <= lo+(i+1)*span) + *p++ = order[j]; + *p = 0; + if(p != bar){ + snprint(x, sizeof x, "[%ld-%ld]", lo+i*span, lo+(i+1)*span); + print("%-16s %s\n", x, bar); + empty = 0; + } else if(!empty){ + print("...\n"); + empty = 1; + } + } + print("+++++++++++++++++++++++\n"); +} |