traceroute.c 8.4 KB

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