26 void histogram(long *t, int n, int buckets, long lo, long hi);
32 "usage: %s [-n][-a tries][-h buckets][-t ttl][-x net] [protocol!]destination\n",
38 csquery(DS *ds, char *clone, char *dest)
41 char *p, buf[Maxstring];
44 * open connection server
46 snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
47 fd = open(buf, ORDWR);
50 werrstr("can't translate");
54 /* no connection server, don't translate */
55 snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
56 strcpy(dest, ds->rem);
61 * ask connection server to translate
63 sprint(buf, "%s!%s", ds->proto, ds->rem);
64 if(write(fd, buf, strlen(buf)) < 0){
73 n = read(fd, buf, sizeof(buf) - 1);
76 werrstr("problem with cs");
83 werrstr("problem with cs");
94 * call the dns process and have it try to resolve the mx request
97 dodnsquery(DS *ds, char *ip, char *dom)
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);
117 /* for connection oriented protocols (il, tcp) we just need
118 * to try dialing. resending is up to it.
121 tcpilprobe(int cfd, int dfd, char *dest, int interval)
128 n = snprint(msg, sizeof msg, "connect %s", dest);
130 n = write(cfd, msg, n);
136 * for udp, we keep sending to an improbable port
137 * till we timeout or someone complains
140 udpprobe(int cfd, int dfd, char *dest, int interval)
143 char msg[Maxstring], err[ERRMAX];
146 n = snprint(msg, sizeof msg, "connect %s", dest);
147 if(write(cfd, msg, n)< 0)
151 for(i = 0; i < 3; i++){
153 if(write(dfd, "boo hoo ", 8) < 0)
156 * a hangup due to an error looks like 3 eofs followed
157 * by a real error. this is a qio.c qbread() strangeness
161 n = read(dfd, msg, sizeof(msg)-1);
169 errstr(err, sizeof err);
170 if(strcmp(err, "interrupted") != 0){
171 errstr(err, sizeof err);
174 errstr(err, sizeof err);
180 #define MSG "traceroute probe"
184 icmpprobe(int cfd, int dfd, int version, char *dest, int interval)
186 int x, i, n, len, rv;
187 char buf[512], err[ERRMAX], msg[Maxstring];
191 n = snprint(msg, sizeof msg, "connect %s", dest);
192 if(write(cfd, msg, n)< 0)
196 len = (version == 4)? IPV4HDR_LEN: IPV6HDR_LEN;
197 ip = (Icmphdr *)(buf + len);
198 len += ICMP_HDRSIZE + sizeof(MSG);
199 for(i = 0; i < 3; i++){
201 ip->type = (version == 4)? EchoRequest: EchoRequestV6;
203 strcpy((char*)ip->data, MSG);
205 ip->seq[1] = MAGIC>>8;
208 if(write(dfd, buf, len) < len)
212 n = read(dfd, buf, sizeof(buf));
216 errstr(err, sizeof err);
217 if(strcmp(err, "interrupted") != 0){
218 errstr(err, sizeof err);
221 errstr(err, sizeof err);
224 x = (ip->seq[1]<<8) | ip->seq[0];
225 if(n >= len && ip->type == ((version == 4)? EchoReply: EchoReplyV6) && x == MAGIC &&
226 strcmp((char*)ip->data, MSG) == 0){
236 catch(void *a, char *msg)
239 if(strstr(msg, "alarm"))
246 call(DS *ds, char *clone, char *dest, int ttl, long *interval)
250 char file[Maxstring];
259 cfd = open(clone, ORDWR);
261 werrstr("%s: %r", clone);
266 /* get conversation number */
267 n = read(cfd, msg, sizeof(msg)-1);
273 sprint(file, "%s/%s/%s/data", ds->netdir, ds->proto, msg);
274 dfd = open(file, ORDWR);
280 fprint(cfd, "ttl %d", ttl);
283 if(strcmp(ds->proto, "udp") == 0)
284 rv = udpprobe(cfd, dfd, dest, 3000);
285 else if(strcmp(ds->proto, "icmp") == 0)
286 rv = icmpprobe(cfd, dfd, 4, dest, 3000);
287 else if(strcmp(ds->proto, "icmpv6") == 0)
288 rv = icmpprobe(cfd, dfd, 6, dest, 3000);
289 else /* il and tcp */
290 rv = tcpilprobe(cfd, dfd, dest, 3000);
292 /* turn off alarms */
294 *interval = nsec()/1000 - start;
301 * parse a dial string. default netdir is /net.
302 * default proto is tcp.
305 dial_string_parse(char *str, DS *ds)
309 strncpy(ds->buf, str, Maxstring);
310 ds->buf[Maxstring-3] = 0;
312 p = strchr(ds->buf, '!');
322 for(p2 = p; *p2 != '/'; p2--)
325 ds->netdir = ds->buf;
331 if(strchr(ds->rem, '!') == 0)
332 strcat(ds->rem, "!32767");
336 main(int argc, char **argv)
338 int buckets, ttl, j, done, tries, notranslate;
342 char clone[Maxpath], dest[Maxstring], hop[Maxstring], dom[Maxstring];
353 tries = atoi(EARGF(usage()));
359 buckets = atoi(EARGF(usage()));
365 ttl = atoi(EARGF(usage()));
368 net = EARGF(usage());
377 t = malloc(tries*sizeof(ulong));
379 dial_string_parse(argv[0], &ds);
383 if(csquery(&ds, clone, dest) < 0){
384 fprint(2, "%s: %s: %r\n", argv0, argv[0]);
387 print("trying %s/%s!%s\n\n", ds.netdir, ds.proto, dest);
388 print(" round trip times in µs\n");
389 print(" low avg high\n");
390 print(" --------------------------\n");
393 for(; ttl < 32; ttl++){
394 for(j = 0; j < tries; j++){
395 if(call(&ds, clone, dest, ttl, &t[j]) >= 0){
397 print("%ld %s\n", t[j], dest);
403 errstr(err, sizeof err);
404 if(strstr(err, "refused")){
406 p = strchr(hop, '!');
410 } else if(strstr(err, "unreachable")){
411 snprint(hop, sizeof(hop), "%s", err);
412 p = strchr(hop, '!');
416 } else if(strncmp(err, "ttl exceeded at ", 16) == 0)
423 print("%ld %s\n", t[j], hop);
425 if(strcmp(hop, "*") == 0){
432 for(j = 0; j < tries; j++){
440 if(notranslate == 1 || dodnsquery(&ds, hop, dom) < 0)
442 /* don't truncate: ipv6 addresses can be quite long */
443 print("%-18s %8ld %8ld %8ld %s\n", hop, lo, sum/tries, hi, dom);
445 histogram(t, tries, buckets, lo, hi);
452 char *order = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
455 histogram(long *t, int n, int buckets, long lo, long hi)
466 print("+++++++++++++++++++++++\n");
467 span = (hi-lo)/buckets;
470 for(i = 0; i < buckets; i++){
472 for(j = 0; j < n; j++)
473 if(t[j] >= lo+i*span && t[j] <= lo+(i+1)*span)
477 snprint(x, sizeof x, "[%ld-%ld]", lo+i*span, lo+(i+1)*span);
478 print("%-16s %s\n", x, bar);
485 print("+++++++++++++++++++++++\n");