traceroute.c 8.5 KB

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