1 #include <u.h> 2 #include <libc.h> 3 #include <ctype.h> 4 #include <bio.h> 5 #include <ndb.h> 6 7 enum{ 8 Maxstring= 128, 9 Maxpath= 256, 10 }; 11 12 typedef struct DS DS; 13 struct DS { 14 /* dist string */ 15 char buf[Maxstring]; 16 char *netdir; 17 char *proto; 18 char *rem; 19 }; 20 21 typedef struct Icmp Icmp; 22 struct Icmp 23 { 24 uchar vihl; /* Version and header length */ 25 uchar tos; /* Type of service */ 26 uchar length[2]; /* packet length */ 27 uchar id[2]; /* Identification */ 28 uchar frag[2]; /* Fragment information */ 29 uchar ttl; /* Time to live */ 30 uchar proto; /* Protocol */ 31 uchar ipcksum[2]; /* Header checksum */ 32 uchar src[4]; /* Ip source */ 33 uchar dst[4]; /* Ip destination */ 34 uchar type; 35 uchar code; 36 uchar cksum[2]; 37 uchar icmpid[2]; 38 uchar seq[2]; 39 uchar data[1]; 40 }; 41 42 enum 43 { /* Packet Types */ 44 EchoReply = 0, 45 Unreachable = 3, 46 SrcQuench = 4, 47 EchoRequest = 8, 48 TimeExceed = 11, 49 Timestamp = 13, 50 TimestampReply = 14, 51 InfoRequest = 15, 52 InfoReply = 16, 53 54 ICMP_IPSIZE = 20, 55 ICMP_HDRSIZE = 8, 56 }; 57 58 char *argv0; 59 int debug; 60 61 void histogram(long *t, int n, int buckets, long lo, long hi); 62 63 void 64 usage(void) 65 { 66 fprint(2, "usage: %s [-n] [protocol!]destination\n", argv0); 67 exits("usage"); 68 } 69 70 static int 71 csquery(DS *ds, char *clone, char *dest) 72 { 73 int n, fd; 74 char *p, buf[Maxstring]; 75 76 /* 77 * open connection server 78 */ 79 snprint(buf, sizeof(buf), "%s/cs", ds->netdir); 80 fd = open(buf, ORDWR); 81 if(fd < 0){ 82 if(!isdigit(*dest)){ 83 werrstr("can't translate"); 84 return -1; 85 } 86 87 /* no connection server, don't translate */ 88 snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto); 89 strcpy(dest, ds->rem); 90 return 0; 91 } 92 93 /* 94 * ask connection server to translate 95 */ 96 sprint(buf, "%s!%s", ds->proto, ds->rem); 97 if(write(fd, buf, strlen(buf)) < 0){ 98 close(fd); 99 return -1; 100 } 101 102 /* 103 * get an address. 104 */ 105 seek(fd, 0, 0); 106 n = read(fd, buf, sizeof(buf) - 1); 107 close(fd); 108 if(n <= 0){ 109 werrstr("problem with cs"); 110 return -1; 111 } 112 113 buf[n] = 0; 114 p = strchr(buf, ' '); 115 if(p == 0){ 116 werrstr("problem with cs"); 117 return -1; 118 } 119 120 *p++ = 0; 121 strcpy(clone, buf); 122 strcpy(dest, p); 123 return 0; 124 } 125 126 /* 127 * call the dns process and have it try to resolve the mx request 128 */ 129 static int 130 dodnsquery(DS *ds, char *ip, char *dom) 131 { 132 char *p; 133 Ndbtuple *t, *nt; 134 135 p = strchr(ip, '!'); 136 if(p) 137 *p = 0; 138 139 t = dnsquery(ds->netdir, ip, "ptr"); 140 for(nt = t; nt != nil; nt = nt->entry) 141 if(strcmp(nt->attr, "dom") == 0){ 142 strcpy(dom, nt->val); 143 ndbfree(t); 144 return 0; 145 } 146 ndbfree(t); 147 return -1; 148 } 149 150 /* for connection oriented protocols (il, tcp) we just need 151 * to try dialing. resending is up to it. 152 */ 153 static int 154 tcpilprobe(int cfd, int dfd, char *dest, int interval) 155 { 156 int n; 157 char msg[Maxstring]; 158 159 USED(dfd); 160 161 n = snprint(msg, sizeof msg, "connect %s", dest); 162 alarm(interval); 163 n = write(cfd, msg, n); 164 alarm(0); 165 return n; 166 } 167 168 /* 169 * for udp, we keep sending to an improbable port 170 * till we timeout or someone complains 171 */ 172 static int 173 udpprobe(int cfd, int dfd, char *dest, int interval) 174 { 175 int n, i, rv; 176 char msg[Maxstring]; 177 char err[Maxstring]; 178 179 seek(cfd, 0, 0); 180 n = snprint(msg, sizeof msg, "connect %s", dest); 181 if(write(cfd, msg, n)< 0) 182 return -1; 183 184 rv = -1; 185 for(i = 0; i < 3; i++){ 186 alarm(interval/3); 187 if(write(dfd, "boo hoo ", 8) < 0) 188 break; 189 /* 190 * a hangup due to an error looks like 3 eofs followed 191 * by a real error. this is a qio.c qbread() strangeness 192 * done for pipes. 193 */ 194 do { 195 n = read(dfd, msg, sizeof(msg)-1); 196 } while(n == 0); 197 alarm(0); 198 if(n > 0){ 199 rv = 0; 200 break; 201 } 202 errstr(err, sizeof err); 203 if(strstr(err, "alarm") == 0){ 204 werrstr(err); 205 break; 206 } 207 werrstr(err); 208 } 209 alarm(0); 210 return rv; 211 } 212 213 #define MSG "traceroute probe" 214 #define MAGIC 0xdead 215 216 static int 217 icmpprobe(int cfd, int dfd, char *dest, int interval) 218 { 219 int x, i, n, len, rv; 220 char buf[512]; 221 Icmp *ip; 222 char msg[Maxstring]; 223 char err[Maxstring]; 224 225 seek(cfd, 0, 0); 226 n = snprint(msg, sizeof msg, "connect %s", dest); 227 if(write(cfd, msg, n)< 0) 228 return -1; 229 230 rv = -1; 231 ip = (Icmp*)buf; 232 for(i = 0; i < 3; i++){ 233 alarm(interval/3); 234 ip->type = EchoRequest; 235 ip->code = 0; 236 strcpy((char*)ip->data, MSG); 237 ip->seq[0] = MAGIC; 238 ip->seq[1] = MAGIC>>8; 239 len = ICMP_IPSIZE+ICMP_HDRSIZE+sizeof(MSG); 240 241 /* send a request */ 242 if(write(dfd, buf, len) < len) 243 break; 244 245 /* wait for reply */ 246 n = read(dfd, buf, sizeof(buf)); 247 alarm(0); 248 if(n < 0){ 249 errstr(err, sizeof err); 250 if(strstr(err, "alarm") == 0){ 251 werrstr(err); 252 break; 253 } 254 werrstr(err); 255 continue; 256 } 257 x = (ip->seq[1]<<8)|ip->seq[0]; 258 if(n >= len) 259 if(ip->type == EchoReply) 260 if(x == MAGIC) 261 if(strcmp((char*)ip->data, MSG) == 0){ 262 rv = 0; 263 break; 264 } 265 } 266 alarm(0); 267 return rv; 268 } 269 270 static void 271 catch(void *a, char *msg) 272 { 273 USED(a); 274 if(strstr(msg, "alarm")) 275 noted(NCONT); 276 else 277 noted(NDFLT); 278 } 279 280 static int 281 call(DS *ds, char *clone, char *dest, int ttl, long *interval) 282 { 283 int cfd, dfd, rv, n; 284 char msg[Maxstring]; 285 char file[Maxstring]; 286 vlong start; 287 288 notify(catch); 289 290 /* start timing */ 291 start = nsec()/1000; 292 rv = -1; 293 294 cfd = open(clone, ORDWR); 295 if(cfd < 0){ 296 werrstr("%s: %r", clone); 297 return -1; 298 } 299 dfd = -1; 300 301 /* get conversation number */ 302 n = read(cfd, msg, sizeof(msg)-1); 303 if(n <= 0) 304 goto out; 305 msg[n] = 0; 306 307 /* open data file */ 308 sprint(file, "%s/%s/%s/data", ds->netdir, ds->proto, msg); 309 dfd = open(file, ORDWR); 310 if(dfd < 0) 311 goto out; 312 313 /* set ttl */ 314 if(ttl) 315 fprint(cfd, "ttl %d", ttl); 316 317 /* probe */ 318 if(strcmp(ds->proto, "udp") == 0) 319 rv = udpprobe(cfd, dfd, dest, 3000); 320 else if(strcmp(ds->proto, "icmp") == 0) 321 rv = icmpprobe(cfd, dfd, dest, 3000); 322 else /* il and tcp */ 323 rv = tcpilprobe(cfd, dfd, dest, 3000); 324 out: 325 /* turn off alarms */ 326 alarm(0); 327 *interval = nsec()/1000 - start; 328 close(cfd); 329 close(dfd); 330 return rv; 331 } 332 333 /* 334 * parse a dial string. default netdir is /net. 335 * default proto is tcp. 336 */ 337 static void 338 dial_string_parse(char *str, DS *ds) 339 { 340 char *p, *p2; 341 342 strncpy(ds->buf, str, Maxstring); 343 ds->buf[Maxstring-3] = 0; 344 345 p = strchr(ds->buf, '!'); 346 if(p == 0) { 347 ds->netdir = 0; 348 ds->proto = "tcp"; 349 ds->rem = ds->buf; 350 } else { 351 if(*ds->buf != '/'){ 352 ds->netdir = 0; 353 ds->proto = ds->buf; 354 } else { 355 for(p2 = p; *p2 != '/'; p2--) 356 ; 357 *p2++ = 0; 358 ds->netdir = ds->buf; 359 ds->proto = p2; 360 } 361 *p = 0; 362 ds->rem = p + 1; 363 } 364 if(strchr(ds->rem, '!') == 0) 365 strcat(ds->rem, "!32767"); 366 } 367 368 void 369 main(int argc, char **argv) 370 { 371 int j, done; 372 DS ds; 373 char clone[Maxpath], dest[Maxstring], hop[Maxstring], dom[Maxstring]; 374 char err[Maxstring]; 375 long lo, hi, sum, x; 376 char *p; 377 int tries, notranslate; 378 char *net; 379 int buckets, ttl; 380 long *t; 381 382 buckets = 0; 383 tries = 3; 384 notranslate = 0; 385 net = "/net"; 386 ttl = 1; 387 ARGBEGIN{ 388 case 't': 389 p = ARGF(); 390 if(p) 391 ttl = atoi(p); 392 break; 393 case 'h': 394 p = ARGF(); 395 if(p) 396 buckets = atoi(p); 397 break; 398 case 'd': 399 debug++; 400 break; 401 case 'n': 402 notranslate++; 403 break; 404 case 'a': 405 p = ARGF(); 406 if(p) 407 tries = atoi(p); 408 break; 409 case 'x': 410 net = ARGF(); 411 break; 412 default: 413 usage(); 414 }ARGEND; 415 416 if(argc < 1) 417 usage(); 418 419 t = malloc(tries*sizeof(ulong)); 420 421 dial_string_parse(argv[0], &ds); 422 423 if(ds.netdir == 0) 424 ds.netdir = net; 425 if(csquery(&ds, clone, dest) < 0){ 426 fprint(2, "%s: %s: %r\n", argv0, argv[0]); 427 exits(0); 428 } 429 print("trying %s/%s!%s\n\n", ds.netdir, ds.proto, dest); 430 print(" round trip times in µs\n"); 431 print(" low avg high\n"); 432 print(" --------------------------\n"); 433 434 done = 0; 435 for(; ttl < 32; ttl++){ 436 for(j = 0; j < tries; j++){ 437 if(call(&ds, clone, dest, ttl, &t[j]) >= 0){ 438 if(debug) 439 print("%ld %s\n", t[j], dest); 440 strcpy(hop, dest); 441 done = 1; 442 continue; 443 } 444 errstr(err, sizeof err); 445 if(strstr(err, "refused")){ 446 strcpy(hop, dest); 447 p = strchr(hop, '!'); 448 if(p) 449 *p = 0; 450 done = 1; 451 } else if(strstr(err, "unreachable")){ 452 snprint(hop, sizeof(hop), "%s", err); 453 p = strchr(hop, '!'); 454 if(p) 455 *p = 0; 456 done = 1; 457 } else if(strncmp(err, "ttl exceeded at ", 16) == 0){ 458 strcpy(hop, err+16); 459 } else { 460 strcpy(hop, "*"); 461 break; 462 } 463 if(debug) 464 print("%ld %s\n", t[j], hop); 465 } 466 if(strcmp(hop, "*") == 0){ 467 print("*\n"); 468 continue; 469 } 470 lo = 10000000; 471 hi = 0; 472 sum = 0; 473 for(j = 0; j < tries; j++){ 474 x = t[j]; 475 sum += x; 476 if(x < lo) 477 lo = x; 478 if(x > hi) 479 hi = x; 480 } 481 if(notranslate == 1 || dodnsquery(&ds, hop, dom) < 0) 482 dom[0] = 0; 483 print("%-18.18s %8ld %8ld %8ld %s\n", hop, lo, sum/tries, hi, dom); 484 if(buckets) 485 histogram(t, tries, buckets, lo, hi); 486 if(done) 487 break; 488 } 489 490 exits(0); 491 } 492 493 char *order = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 494 495 void 496 histogram(long *t, int n, int buckets, long lo, long hi) 497 { 498 int i, j, empty; 499 long span; 500 static char *bar; 501 char *p; 502 char x[64]; 503 504 if(bar == nil) 505 bar = malloc(n+1); 506 507 print("+++++++++++++++++++++++\n"); 508 span = (hi-lo)/buckets; 509 span++; 510 empty = 0; 511 for(i = 0; i < buckets; i++){ 512 p = bar; 513 for(j = 0; j < n; j++) 514 if(t[j] >= lo+i*span && t[j] <= lo+(i+1)*span) 515 *p++ = order[j]; 516 *p = 0; 517 if(p != bar){ 518 snprint(x, sizeof x, "[%ld-%ld]", lo+i*span, lo+(i+1)*span); 519 print("%-16s %s\n", x, bar); 520 empty = 0; 521 } else if(!empty){ 522 print("...\n"); 523 empty = 1; 524 } 525 } 526 print("+++++++++++++++++++++++\n"); 527 } 528