traceroute.c 9.2 KB

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