123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- #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");
- }
|