summaryrefslogtreecommitdiff
path: root/sys/src/cmd/ip/traceroute.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /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-xsys/src/cmd/ip/traceroute.c482
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");
+}