traceroute.c 9.0 KB

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