xref: /plan9/sys/src/cmd/ip/traceroute.c (revision 94aa1c4c0955b2b4e990c9f4679be8e9f67a469b)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <ctype.h>
47dd7cddfSDavid du Colombier #include <bio.h>
57dd7cddfSDavid du Colombier #include <ndb.h>
65e1edbcaSDavid du Colombier #include <ip.h>
75a354e27SDavid du Colombier #include "icmp.h"
87dd7cddfSDavid du Colombier 
97dd7cddfSDavid du Colombier enum{
107dd7cddfSDavid du Colombier 	Maxstring=	128,
117dd7cddfSDavid du Colombier 	Maxpath=	256,
127dd7cddfSDavid du Colombier };
137dd7cddfSDavid du Colombier 
147dd7cddfSDavid du Colombier typedef struct DS DS;
157dd7cddfSDavid du Colombier struct DS {
165a354e27SDavid du Colombier 	/* dial string */
177dd7cddfSDavid du Colombier 	char	buf[Maxstring];
187dd7cddfSDavid du Colombier 	char	*netdir;
197dd7cddfSDavid du Colombier 	char	*proto;
207dd7cddfSDavid du Colombier 	char	*rem;
217dd7cddfSDavid du Colombier };
227dd7cddfSDavid du Colombier 
237dd7cddfSDavid du Colombier char *argv0;
247dd7cddfSDavid du Colombier int debug;
257dd7cddfSDavid du Colombier 
263ff48bf5SDavid du Colombier void	histogram(long *t, int n, int buckets, long lo, long hi);
273ff48bf5SDavid du Colombier 
287dd7cddfSDavid du Colombier void
usage(void)297dd7cddfSDavid du Colombier usage(void)
307dd7cddfSDavid du Colombier {
315a354e27SDavid du Colombier 	fprint(2,
325a354e27SDavid du Colombier "usage: %s [-n][-a tries][-h buckets][-t ttl][-x net] [protocol!]destination\n",
335a354e27SDavid du Colombier 		argv0);
347dd7cddfSDavid du Colombier 	exits("usage");
357dd7cddfSDavid du Colombier }
367dd7cddfSDavid du Colombier 
377dd7cddfSDavid du Colombier static int
csquery(DS * ds,char * clone,char * dest)387dd7cddfSDavid du Colombier csquery(DS *ds, char *clone, char *dest)
397dd7cddfSDavid du Colombier {
407dd7cddfSDavid du Colombier 	int n, fd;
417dd7cddfSDavid du Colombier 	char *p, buf[Maxstring];
427dd7cddfSDavid du Colombier 
437dd7cddfSDavid du Colombier 	/*
447dd7cddfSDavid du Colombier 	 *  open connection server
457dd7cddfSDavid du Colombier 	 */
467dd7cddfSDavid du Colombier 	snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
477dd7cddfSDavid du Colombier 	fd = open(buf, ORDWR);
487dd7cddfSDavid du Colombier 	if(fd < 0){
497dd7cddfSDavid du Colombier 		if(!isdigit(*dest)){
507dd7cddfSDavid du Colombier 			werrstr("can't translate");
517dd7cddfSDavid du Colombier 			return -1;
527dd7cddfSDavid du Colombier 		}
537dd7cddfSDavid du Colombier 
547dd7cddfSDavid du Colombier 		/* no connection server, don't translate */
557dd7cddfSDavid du Colombier 		snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
567dd7cddfSDavid du Colombier 		strcpy(dest, ds->rem);
577dd7cddfSDavid du Colombier 		return 0;
587dd7cddfSDavid du Colombier 	}
597dd7cddfSDavid du Colombier 
607dd7cddfSDavid du Colombier 	/*
617dd7cddfSDavid du Colombier 	 *  ask connection server to translate
627dd7cddfSDavid du Colombier 	 */
637dd7cddfSDavid du Colombier 	sprint(buf, "%s!%s", ds->proto, ds->rem);
647dd7cddfSDavid du Colombier 	if(write(fd, buf, strlen(buf)) < 0){
657dd7cddfSDavid du Colombier 		close(fd);
667dd7cddfSDavid du Colombier 		return -1;
677dd7cddfSDavid du Colombier 	}
687dd7cddfSDavid du Colombier 
697dd7cddfSDavid du Colombier 	/*
707dd7cddfSDavid du Colombier 	 *  get an address.
717dd7cddfSDavid du Colombier 	 */
727dd7cddfSDavid du Colombier 	seek(fd, 0, 0);
737dd7cddfSDavid du Colombier 	n = read(fd, buf, sizeof(buf) - 1);
747dd7cddfSDavid du Colombier 	close(fd);
757dd7cddfSDavid du Colombier 	if(n <= 0){
767dd7cddfSDavid du Colombier 		werrstr("problem with cs");
777dd7cddfSDavid du Colombier 		return -1;
787dd7cddfSDavid du Colombier 	}
797dd7cddfSDavid du Colombier 
807dd7cddfSDavid du Colombier 	buf[n] = 0;
817dd7cddfSDavid du Colombier 	p = strchr(buf, ' ');
827dd7cddfSDavid du Colombier 	if(p == 0){
837dd7cddfSDavid du Colombier 		werrstr("problem with cs");
847dd7cddfSDavid du Colombier 		return -1;
857dd7cddfSDavid du Colombier 	}
867dd7cddfSDavid du Colombier 
877dd7cddfSDavid du Colombier 	*p++ = 0;
887dd7cddfSDavid du Colombier 	strcpy(clone, buf);
897dd7cddfSDavid du Colombier 	strcpy(dest, p);
907dd7cddfSDavid du Colombier 	return 0;
917dd7cddfSDavid du Colombier }
927dd7cddfSDavid du Colombier 
937dd7cddfSDavid du Colombier /*
947dd7cddfSDavid du Colombier  *  call the dns process and have it try to resolve the mx request
957dd7cddfSDavid du Colombier  */
967dd7cddfSDavid du Colombier static int
dodnsquery(DS * ds,char * ip,char * dom)977dd7cddfSDavid du Colombier dodnsquery(DS *ds, char *ip, char *dom)
987dd7cddfSDavid du Colombier {
997dd7cddfSDavid du Colombier 	char *p;
1007dd7cddfSDavid du Colombier 	Ndbtuple *t, *nt;
1017dd7cddfSDavid du Colombier 
1027dd7cddfSDavid du Colombier 	p = strchr(ip, '!');
1037dd7cddfSDavid du Colombier 	if(p)
1047dd7cddfSDavid du Colombier 		*p = 0;
1057dd7cddfSDavid du Colombier 
1067dd7cddfSDavid du Colombier 	t = dnsquery(ds->netdir, ip, "ptr");
1077dd7cddfSDavid du Colombier 	for(nt = t; nt != nil; nt = nt->entry)
1087dd7cddfSDavid du Colombier 		if(strcmp(nt->attr, "dom") == 0){
1097dd7cddfSDavid du Colombier 			strcpy(dom, nt->val);
1107dd7cddfSDavid du Colombier 			ndbfree(t);
1117dd7cddfSDavid du Colombier 			return 0;
1127dd7cddfSDavid du Colombier 		}
1137dd7cddfSDavid du Colombier 	ndbfree(t);
1147dd7cddfSDavid du Colombier 	return -1;
1157dd7cddfSDavid du Colombier }
1167dd7cddfSDavid du Colombier 
1177dd7cddfSDavid du Colombier /*  for connection oriented protocols (il, tcp) we just need
1187dd7cddfSDavid du Colombier  *  to try dialing.  resending is up to it.
1197dd7cddfSDavid du Colombier  */
1207dd7cddfSDavid du Colombier static int
tcpilprobe(int cfd,int dfd,char * dest,int interval)1217dd7cddfSDavid du Colombier tcpilprobe(int cfd, int dfd, char *dest, int interval)
1227dd7cddfSDavid du Colombier {
1237dd7cddfSDavid du Colombier 	int n;
1247dd7cddfSDavid du Colombier 	char msg[Maxstring];
1257dd7cddfSDavid du Colombier 
1267dd7cddfSDavid du Colombier 	USED(dfd);
1277dd7cddfSDavid du Colombier 
1287dd7cddfSDavid du Colombier 	n = snprint(msg, sizeof msg, "connect %s", dest);
1297dd7cddfSDavid du Colombier 	alarm(interval);
1307dd7cddfSDavid du Colombier 	n = write(cfd, msg, n);
1317dd7cddfSDavid du Colombier 	alarm(0);
1327dd7cddfSDavid du Colombier 	return n;
1337dd7cddfSDavid du Colombier }
1347dd7cddfSDavid du Colombier 
1357dd7cddfSDavid du Colombier /*
1367dd7cddfSDavid du Colombier  *  for udp, we keep sending to an improbable port
1377dd7cddfSDavid du Colombier  *  till we timeout or someone complains
1387dd7cddfSDavid du Colombier  */
1397dd7cddfSDavid du Colombier static int
udpprobe(int cfd,int dfd,char * dest,int interval)1407dd7cddfSDavid du Colombier udpprobe(int cfd, int dfd, char *dest, int interval)
1417dd7cddfSDavid du Colombier {
1427dd7cddfSDavid du Colombier 	int n, i, rv;
1437dd7cddfSDavid du Colombier 	char msg[Maxstring];
1447dd7cddfSDavid du Colombier 	char err[Maxstring];
1457dd7cddfSDavid du Colombier 
1467dd7cddfSDavid du Colombier 	seek(cfd, 0, 0);
1477dd7cddfSDavid du Colombier 	n = snprint(msg, sizeof msg, "connect %s", dest);
1487dd7cddfSDavid du Colombier 	if(write(cfd, msg, n)< 0)
1497dd7cddfSDavid du Colombier 		return -1;
1507dd7cddfSDavid du Colombier 
1517dd7cddfSDavid du Colombier 	rv = -1;
1527dd7cddfSDavid du Colombier 	for(i = 0; i < 3; i++){
1537dd7cddfSDavid du Colombier 		alarm(interval/3);
1547dd7cddfSDavid du Colombier 		if(write(dfd, "boo hoo ", 8) < 0)
1557dd7cddfSDavid du Colombier 			break;
1567dd7cddfSDavid du Colombier 		/*
1577dd7cddfSDavid du Colombier 		 *  a hangup due to an error looks like 3 eofs followed
1587dd7cddfSDavid du Colombier 		 *  by a real error.  this is a qio.c qbread() strangeness
1597dd7cddfSDavid du Colombier 		 *  done for pipes.
1607dd7cddfSDavid du Colombier 		 */
1617dd7cddfSDavid du Colombier 		do {
1627dd7cddfSDavid du Colombier 			n = read(dfd, msg, sizeof(msg)-1);
1637dd7cddfSDavid du Colombier 		} while(n == 0);
1647dd7cddfSDavid du Colombier 		alarm(0);
1657dd7cddfSDavid du Colombier 		if(n > 0){
1667dd7cddfSDavid du Colombier 			rv = 0;
1677dd7cddfSDavid du Colombier 			break;
1687dd7cddfSDavid du Colombier 		}
1699a747e4fSDavid du Colombier 		errstr(err, sizeof err);
1707dd7cddfSDavid du Colombier 		if(strstr(err, "alarm") == 0){
1717dd7cddfSDavid du Colombier 			werrstr(err);
1727dd7cddfSDavid du Colombier 			break;
1737dd7cddfSDavid du Colombier 		}
1747dd7cddfSDavid du Colombier 		werrstr(err);
1757dd7cddfSDavid du Colombier 	}
1767dd7cddfSDavid du Colombier 	alarm(0);
1777dd7cddfSDavid du Colombier 	return rv;
1787dd7cddfSDavid du Colombier }
1797dd7cddfSDavid du Colombier 
1807dd7cddfSDavid du Colombier #define MSG "traceroute probe"
1817dd7cddfSDavid du Colombier #define MAGIC 0xdead
1827dd7cddfSDavid du Colombier 
1835e1edbcaSDavid du Colombier /* ICMPv4 only */
1847dd7cddfSDavid du Colombier static int
icmpprobe(int cfd,int dfd,char * dest,int interval)1857dd7cddfSDavid du Colombier icmpprobe(int cfd, int dfd, char *dest, int interval)
1867dd7cddfSDavid du Colombier {
1877dd7cddfSDavid du Colombier 	int x, i, n, len, rv;
188*94aa1c4cSDavid du Colombier 	char buf[512], err[Maxstring], msg[Maxstring];
189*94aa1c4cSDavid du Colombier 	Icmphdr *ip;
1907dd7cddfSDavid du Colombier 
1917dd7cddfSDavid du Colombier 	seek(cfd, 0, 0);
1927dd7cddfSDavid du Colombier 	n = snprint(msg, sizeof msg, "connect %s", dest);
1937dd7cddfSDavid du Colombier 	if(write(cfd, msg, n)< 0)
1947dd7cddfSDavid du Colombier 		return -1;
1957dd7cddfSDavid du Colombier 
1967dd7cddfSDavid du Colombier 	rv = -1;
197*94aa1c4cSDavid du Colombier 	ip = (Icmphdr *)(buf + IPV4HDR_LEN);
1987dd7cddfSDavid du Colombier 	for(i = 0; i < 3; i++){
1997dd7cddfSDavid du Colombier 		alarm(interval/3);
2007dd7cddfSDavid du Colombier 		ip->type = EchoRequest;
2017dd7cddfSDavid du Colombier 		ip->code = 0;
2027dd7cddfSDavid du Colombier 		strcpy((char*)ip->data, MSG);
2037dd7cddfSDavid du Colombier 		ip->seq[0] = MAGIC;
2047dd7cddfSDavid du Colombier 		ip->seq[1] = MAGIC>>8;
2055e1edbcaSDavid du Colombier 		len = IPV4HDR_LEN + ICMP_HDRSIZE + sizeof(MSG);
2067dd7cddfSDavid du Colombier 
2077dd7cddfSDavid du Colombier 		/* send a request */
2087dd7cddfSDavid du Colombier 		if(write(dfd, buf, len) < len)
2097dd7cddfSDavid du Colombier 			break;
2107dd7cddfSDavid du Colombier 
2117dd7cddfSDavid du Colombier 		/* wait for reply */
2127dd7cddfSDavid du Colombier 		n = read(dfd, buf, sizeof(buf));
2137dd7cddfSDavid du Colombier 		alarm(0);
2147dd7cddfSDavid du Colombier 		if(n < 0){
2159a747e4fSDavid du Colombier 			errstr(err, sizeof err);
2167dd7cddfSDavid du Colombier 			if(strstr(err, "alarm") == 0){
2177dd7cddfSDavid du Colombier 				werrstr(err);
2187dd7cddfSDavid du Colombier 				break;
2197dd7cddfSDavid du Colombier 			}
2207dd7cddfSDavid du Colombier 			werrstr(err);
2217dd7cddfSDavid du Colombier 			continue;
2227dd7cddfSDavid du Colombier 		}
2237dd7cddfSDavid du Colombier 		x = (ip->seq[1]<<8) | ip->seq[0];
224*94aa1c4cSDavid du Colombier 		if(n >= len && ip->type == EchoReply && x == MAGIC &&
225*94aa1c4cSDavid du Colombier 		    strcmp((char*)ip->data, MSG) == 0){
2267dd7cddfSDavid du Colombier 			rv = 0;
2277dd7cddfSDavid du Colombier 			break;
2287dd7cddfSDavid du Colombier 		}
2297dd7cddfSDavid du Colombier 	}
2307dd7cddfSDavid du Colombier 	alarm(0);
2317dd7cddfSDavid du Colombier 	return rv;
2327dd7cddfSDavid du Colombier }
2337dd7cddfSDavid du Colombier 
2347dd7cddfSDavid du Colombier static void
catch(void * a,char * msg)2357dd7cddfSDavid du Colombier catch(void *a, char *msg)
2367dd7cddfSDavid du Colombier {
2377dd7cddfSDavid du Colombier 	USED(a);
2387dd7cddfSDavid du Colombier 	if(strstr(msg, "alarm"))
2397dd7cddfSDavid du Colombier 		noted(NCONT);
2407dd7cddfSDavid du Colombier 	else
2417dd7cddfSDavid du Colombier 		noted(NDFLT);
2427dd7cddfSDavid du Colombier }
2437dd7cddfSDavid du Colombier 
2447dd7cddfSDavid du Colombier static int
call(DS * ds,char * clone,char * dest,int ttl,long * interval)2457dd7cddfSDavid du Colombier call(DS *ds, char *clone, char *dest, int ttl, long *interval)
2467dd7cddfSDavid du Colombier {
2477dd7cddfSDavid du Colombier 	int cfd, dfd, rv, n;
2487dd7cddfSDavid du Colombier 	char msg[Maxstring];
2497dd7cddfSDavid du Colombier 	char file[Maxstring];
2507dd7cddfSDavid du Colombier 	vlong start;
2517dd7cddfSDavid du Colombier 
2527dd7cddfSDavid du Colombier 	notify(catch);
2537dd7cddfSDavid du Colombier 
2547dd7cddfSDavid du Colombier 	/* start timing */
2557dd7cddfSDavid du Colombier 	start = nsec()/1000;
2567dd7cddfSDavid du Colombier 	rv = -1;
2577dd7cddfSDavid du Colombier 
2587dd7cddfSDavid du Colombier 	cfd = open(clone, ORDWR);
2597dd7cddfSDavid du Colombier 	if(cfd < 0){
2607dd7cddfSDavid du Colombier 		werrstr("%s: %r", clone);
2617dd7cddfSDavid du Colombier 		return -1;
2627dd7cddfSDavid du Colombier 	}
2637dd7cddfSDavid du Colombier 	dfd = -1;
2647dd7cddfSDavid du Colombier 
2657dd7cddfSDavid du Colombier 	/* get conversation number */
2667dd7cddfSDavid du Colombier 	n = read(cfd, msg, sizeof(msg)-1);
2677dd7cddfSDavid du Colombier 	if(n <= 0)
2687dd7cddfSDavid du Colombier 		goto out;
2697dd7cddfSDavid du Colombier 	msg[n] = 0;
2707dd7cddfSDavid du Colombier 
2717dd7cddfSDavid du Colombier 	/* open data file */
2727dd7cddfSDavid du Colombier 	sprint(file, "%s/%s/%s/data", ds->netdir, ds->proto, msg);
2737dd7cddfSDavid du Colombier 	dfd = open(file, ORDWR);
2747dd7cddfSDavid du Colombier 	if(dfd < 0)
2757dd7cddfSDavid du Colombier 		goto out;
2767dd7cddfSDavid du Colombier 
2777dd7cddfSDavid du Colombier 	/* set ttl */
2787dd7cddfSDavid du Colombier 	if(ttl)
2797dd7cddfSDavid du Colombier 		fprint(cfd, "ttl %d", ttl);
2807dd7cddfSDavid du Colombier 
2817dd7cddfSDavid du Colombier 	/* probe */
2827dd7cddfSDavid du Colombier 	if(strcmp(ds->proto, "udp") == 0)
2837dd7cddfSDavid du Colombier 		rv = udpprobe(cfd, dfd, dest, 3000);
2847dd7cddfSDavid du Colombier 	else if(strcmp(ds->proto, "icmp") == 0)
2857dd7cddfSDavid du Colombier 		rv = icmpprobe(cfd, dfd, dest, 3000);
2867dd7cddfSDavid du Colombier 	else	/* il and tcp */
2877dd7cddfSDavid du Colombier 		rv = tcpilprobe(cfd, dfd, dest, 3000);
2887dd7cddfSDavid du Colombier out:
2897dd7cddfSDavid du Colombier 	/* turn off alarms */
2907dd7cddfSDavid du Colombier 	alarm(0);
2917dd7cddfSDavid du Colombier 	*interval = nsec()/1000 - start;
2927dd7cddfSDavid du Colombier 	close(cfd);
2937dd7cddfSDavid du Colombier 	close(dfd);
2947dd7cddfSDavid du Colombier 	return rv;
2957dd7cddfSDavid du Colombier }
2967dd7cddfSDavid du Colombier 
2977dd7cddfSDavid du Colombier /*
2987dd7cddfSDavid du Colombier  *  parse a dial string.  default netdir is /net.
2997dd7cddfSDavid du Colombier  *  default proto is tcp.
3007dd7cddfSDavid du Colombier  */
3017dd7cddfSDavid du Colombier static void
dial_string_parse(char * str,DS * ds)3027dd7cddfSDavid du Colombier dial_string_parse(char *str, DS *ds)
3037dd7cddfSDavid du Colombier {
3047dd7cddfSDavid du Colombier 	char *p, *p2;
3057dd7cddfSDavid du Colombier 
3067dd7cddfSDavid du Colombier 	strncpy(ds->buf, str, Maxstring);
3077dd7cddfSDavid du Colombier 	ds->buf[Maxstring-3] = 0;
3087dd7cddfSDavid du Colombier 
3097dd7cddfSDavid du Colombier 	p = strchr(ds->buf, '!');
3107dd7cddfSDavid du Colombier 	if(p == 0) {
3117dd7cddfSDavid du Colombier 		ds->netdir = 0;
3127dd7cddfSDavid du Colombier 		ds->proto = "tcp";
3137dd7cddfSDavid du Colombier 		ds->rem = ds->buf;
3147dd7cddfSDavid du Colombier 	} else {
3157dd7cddfSDavid du Colombier 		if(*ds->buf != '/'){
3167dd7cddfSDavid du Colombier 			ds->netdir = 0;
3177dd7cddfSDavid du Colombier 			ds->proto = ds->buf;
3187dd7cddfSDavid du Colombier 		} else {
3197dd7cddfSDavid du Colombier 			for(p2 = p; *p2 != '/'; p2--)
3207dd7cddfSDavid du Colombier 				;
3217dd7cddfSDavid du Colombier 			*p2++ = 0;
3227dd7cddfSDavid du Colombier 			ds->netdir = ds->buf;
3237dd7cddfSDavid du Colombier 			ds->proto = p2;
3247dd7cddfSDavid du Colombier 		}
3257dd7cddfSDavid du Colombier 		*p = 0;
3267dd7cddfSDavid du Colombier 		ds->rem = p + 1;
3277dd7cddfSDavid du Colombier 	}
3287dd7cddfSDavid du Colombier 	if(strchr(ds->rem, '!') == 0)
3297dd7cddfSDavid du Colombier 		strcat(ds->rem, "!32767");
3307dd7cddfSDavid du Colombier }
3317dd7cddfSDavid du Colombier 
3327dd7cddfSDavid du Colombier void
main(int argc,char ** argv)3337dd7cddfSDavid du Colombier main(int argc, char **argv)
3347dd7cddfSDavid du Colombier {
3355a354e27SDavid du Colombier 	int buckets, ttl, j, done, tries, notranslate;
3365a354e27SDavid du Colombier 	long lo, hi, sum, x;
3375a354e27SDavid du Colombier 	long *t;
3385a354e27SDavid du Colombier 	char *net, *p;
3397dd7cddfSDavid du Colombier 	char clone[Maxpath], dest[Maxstring], hop[Maxstring], dom[Maxstring];
3407dd7cddfSDavid du Colombier 	char err[Maxstring];
3415a354e27SDavid du Colombier 	DS ds;
3427dd7cddfSDavid du Colombier 
3433ff48bf5SDavid du Colombier 	buckets = 0;
3447dd7cddfSDavid du Colombier 	tries = 3;
3457dd7cddfSDavid du Colombier 	notranslate = 0;
3467dd7cddfSDavid du Colombier 	net = "/net";
3473ff48bf5SDavid du Colombier 	ttl = 1;
3487dd7cddfSDavid du Colombier 	ARGBEGIN{
3495a354e27SDavid du Colombier 	case 'a':
3505a354e27SDavid du Colombier 		tries = atoi(EARGF(usage()));
3513ff48bf5SDavid du Colombier 		break;
3527dd7cddfSDavid du Colombier 	case 'd':
3537dd7cddfSDavid du Colombier 		debug++;
3547dd7cddfSDavid du Colombier 		break;
3555a354e27SDavid du Colombier 	case 'h':
3565a354e27SDavid du Colombier 		buckets = atoi(EARGF(usage()));
3575a354e27SDavid du Colombier 		break;
3587dd7cddfSDavid du Colombier 	case 'n':
3597dd7cddfSDavid du Colombier 		notranslate++;
3607dd7cddfSDavid du Colombier 		break;
3615a354e27SDavid du Colombier 	case 't':
3625a354e27SDavid du Colombier 		ttl = atoi(EARGF(usage()));
3637dd7cddfSDavid du Colombier 		break;
3647dd7cddfSDavid du Colombier 	case 'x':
3656c54378cSDavid du Colombier 		net = EARGF(usage());
3667dd7cddfSDavid du Colombier 		break;
3677dd7cddfSDavid du Colombier 	default:
3687dd7cddfSDavid du Colombier 		usage();
3697dd7cddfSDavid du Colombier 	}ARGEND;
3707dd7cddfSDavid du Colombier 
3717dd7cddfSDavid du Colombier 	if(argc < 1)
3727dd7cddfSDavid du Colombier 		usage();
3737dd7cddfSDavid du Colombier 
3743ff48bf5SDavid du Colombier 	t = malloc(tries*sizeof(ulong));
3753ff48bf5SDavid du Colombier 
3767dd7cddfSDavid du Colombier 	dial_string_parse(argv[0], &ds);
3777dd7cddfSDavid du Colombier 
3787dd7cddfSDavid du Colombier 	if(ds.netdir == 0)
3797dd7cddfSDavid du Colombier 		ds.netdir = net;
3807dd7cddfSDavid du Colombier 	if(csquery(&ds, clone, dest) < 0){
3817dd7cddfSDavid du Colombier 		fprint(2, "%s: %s: %r\n", argv0, argv[0]);
3827dd7cddfSDavid du Colombier 		exits(0);
3837dd7cddfSDavid du Colombier 	}
3847dd7cddfSDavid du Colombier 	print("trying %s/%s!%s\n\n", ds.netdir, ds.proto, dest);
3857dd7cddfSDavid du Colombier 	print("                       round trip times in µs\n");
3867dd7cddfSDavid du Colombier 	print("                        low      avg     high\n");
3877dd7cddfSDavid du Colombier 	print("                     --------------------------\n");
3887dd7cddfSDavid du Colombier 
3897dd7cddfSDavid du Colombier 	done = 0;
3903ff48bf5SDavid du Colombier 	for(; ttl < 32; ttl++){
3913ff48bf5SDavid du Colombier 		for(j = 0; j < tries; j++){
3923ff48bf5SDavid du Colombier 			if(call(&ds, clone, dest, ttl, &t[j]) >= 0){
3933ff48bf5SDavid du Colombier 				if(debug)
3943ff48bf5SDavid du Colombier 					print("%ld %s\n", t[j], dest);
3957dd7cddfSDavid du Colombier 				strcpy(hop, dest);
3967dd7cddfSDavid du Colombier 				done = 1;
3977dd7cddfSDavid du Colombier 				continue;
3987dd7cddfSDavid du Colombier 			}
3999a747e4fSDavid du Colombier 			errstr(err, sizeof err);
4007dd7cddfSDavid du Colombier 			if(strstr(err, "refused")){
4017dd7cddfSDavid du Colombier 				strcpy(hop, dest);
4027dd7cddfSDavid du Colombier 				p = strchr(hop, '!');
4037dd7cddfSDavid du Colombier 				if(p)
4047dd7cddfSDavid du Colombier 					*p = 0;
4057dd7cddfSDavid du Colombier 				done = 1;
4067dd7cddfSDavid du Colombier 			} else if(strstr(err, "unreachable")){
4073ff48bf5SDavid du Colombier 				snprint(hop, sizeof(hop), "%s", err);
4087dd7cddfSDavid du Colombier 				p = strchr(hop, '!');
4097dd7cddfSDavid du Colombier 				if(p)
4107dd7cddfSDavid du Colombier 					*p = 0;
4117dd7cddfSDavid du Colombier 				done = 1;
4125a354e27SDavid du Colombier 			} else if(strncmp(err, "ttl exceeded at ", 16) == 0)
4137dd7cddfSDavid du Colombier 				strcpy(hop, err+16);
4145a354e27SDavid du Colombier 			else {
4157dd7cddfSDavid du Colombier 				strcpy(hop, "*");
4167dd7cddfSDavid du Colombier 				break;
4177dd7cddfSDavid du Colombier 			}
4183ff48bf5SDavid du Colombier 			if(debug)
4193ff48bf5SDavid du Colombier 				print("%ld %s\n", t[j], hop);
4207dd7cddfSDavid du Colombier 		}
4217dd7cddfSDavid du Colombier 		if(strcmp(hop, "*") == 0){
4227dd7cddfSDavid du Colombier 			print("*\n");
4237dd7cddfSDavid du Colombier 			continue;
4247dd7cddfSDavid du Colombier 		}
4257dd7cddfSDavid du Colombier 		lo = 10000000;
4267dd7cddfSDavid du Colombier 		hi = 0;
4277dd7cddfSDavid du Colombier 		sum = 0;
4287dd7cddfSDavid du Colombier 		for(j = 0; j < tries; j++){
4297dd7cddfSDavid du Colombier 			x = t[j];
4307dd7cddfSDavid du Colombier 			sum += x;
4317dd7cddfSDavid du Colombier 			if(x < lo)
4327dd7cddfSDavid du Colombier 				lo = x;
4337dd7cddfSDavid du Colombier 			if(x > hi)
4347dd7cddfSDavid du Colombier 				hi = x;
4357dd7cddfSDavid du Colombier 		}
4367dd7cddfSDavid du Colombier 		if(notranslate == 1 || dodnsquery(&ds, hop, dom) < 0)
4377dd7cddfSDavid du Colombier 			dom[0] = 0;
438059f8267SDavid du Colombier 		/* don't truncate: ipv6 addresses can be quite long */
439059f8267SDavid du Colombier 		print("%-18s %8ld %8ld %8ld %s\n", hop, lo, sum/tries, hi, dom);
4403ff48bf5SDavid du Colombier 		if(buckets)
4413ff48bf5SDavid du Colombier 			histogram(t, tries, buckets, lo, hi);
4427dd7cddfSDavid du Colombier 		if(done)
4437dd7cddfSDavid du Colombier 			break;
4447dd7cddfSDavid du Colombier 	}
4457dd7cddfSDavid du Colombier 	exits(0);
4467dd7cddfSDavid du Colombier }
4473ff48bf5SDavid du Colombier 
4483ff48bf5SDavid du Colombier char *order = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
4493ff48bf5SDavid du Colombier 
4503ff48bf5SDavid du Colombier void
histogram(long * t,int n,int buckets,long lo,long hi)4513ff48bf5SDavid du Colombier histogram(long *t, int n, int buckets, long lo, long hi)
4523ff48bf5SDavid du Colombier {
4533ff48bf5SDavid du Colombier 	int i, j, empty;
4543ff48bf5SDavid du Colombier 	long span;
4553ff48bf5SDavid du Colombier 	static char *bar;
4563ff48bf5SDavid du Colombier 	char *p;
4573ff48bf5SDavid du Colombier 	char x[64];
4583ff48bf5SDavid du Colombier 
4593ff48bf5SDavid du Colombier 	if(bar == nil)
4603ff48bf5SDavid du Colombier 		bar = malloc(n+1);
4613ff48bf5SDavid du Colombier 
4623ff48bf5SDavid du Colombier 	print("+++++++++++++++++++++++\n");
4633ff48bf5SDavid du Colombier 	span = (hi-lo)/buckets;
4643ff48bf5SDavid du Colombier 	span++;
4653ff48bf5SDavid du Colombier 	empty = 0;
4663ff48bf5SDavid du Colombier 	for(i = 0; i < buckets; i++){
4673ff48bf5SDavid du Colombier 		p = bar;
4683ff48bf5SDavid du Colombier 		for(j = 0; j < n; j++)
4693ff48bf5SDavid du Colombier 			if(t[j] >= lo+i*span && t[j] <= lo+(i+1)*span)
4703ff48bf5SDavid du Colombier 				*p++ = order[j];
4713ff48bf5SDavid du Colombier 		*p = 0;
4723ff48bf5SDavid du Colombier 		if(p != bar){
4733ff48bf5SDavid du Colombier 			snprint(x, sizeof x, "[%ld-%ld]", lo+i*span, lo+(i+1)*span);
4743ff48bf5SDavid du Colombier 			print("%-16s %s\n", x, bar);
4753ff48bf5SDavid du Colombier 			empty = 0;
4763ff48bf5SDavid du Colombier 		} else if(!empty){
4773ff48bf5SDavid du Colombier 			print("...\n");
4783ff48bf5SDavid du Colombier 			empty = 1;
4793ff48bf5SDavid du Colombier 		}
4803ff48bf5SDavid du Colombier 	}
4813ff48bf5SDavid du Colombier 	print("+++++++++++++++++++++++\n");
4823ff48bf5SDavid du Colombier }
483