ping.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <ip.h>
  4. typedef struct Icmp Icmp;
  5. struct Icmp
  6. {
  7. uchar vihl; /* Version and header length */
  8. uchar tos; /* Type of service */
  9. uchar length[2]; /* packet length */
  10. uchar id[2]; /* Identification */
  11. uchar frag[2]; /* Fragment information */
  12. uchar ttl; /* Time to live */
  13. uchar proto; /* Protocol */
  14. uchar ipcksum[2]; /* Header checksum */
  15. uchar src[4]; /* Ip source */
  16. uchar dst[4]; /* Ip destination */
  17. uchar type;
  18. uchar code;
  19. uchar cksum[2];
  20. uchar icmpid[2];
  21. uchar seq[2];
  22. uchar data[1];
  23. };
  24. enum
  25. { /* Packet Types */
  26. EchoReply = 0,
  27. Unreachable = 3,
  28. SrcQuench = 4,
  29. EchoRequest = 8,
  30. TimeExceed = 11,
  31. Timestamp = 13,
  32. TimestampReply = 14,
  33. InfoRequest = 15,
  34. InfoReply = 16,
  35. ICMP_IPSIZE = 20,
  36. ICMP_HDRSIZE = 8,
  37. MAXMSG = 32,
  38. SLEEPMS = 1000,
  39. };
  40. typedef struct Req Req;
  41. struct Req
  42. {
  43. ushort seq; // sequence number
  44. vlong time; // time sent
  45. vlong rtt;
  46. int ttl;
  47. int replied;
  48. Req *next;
  49. };
  50. Req *first; // request list
  51. Req *last; // ...
  52. Lock listlock;
  53. char *argv0;
  54. int debug;
  55. int quiet;
  56. int lostonly;
  57. int lostmsgs;
  58. int rcvdmsgs;
  59. int done;
  60. int rint;
  61. vlong sum;
  62. ushort firstseq;
  63. int addresses;
  64. int flood;
  65. void usage(void);
  66. void lost(Req*, Icmp*);
  67. void reply(Req*, Icmp*);
  68. #define SECOND 1000000000LL
  69. #define MINUTE (60LL*SECOND)
  70. static void
  71. catch(void *a, char *msg)
  72. {
  73. USED(a);
  74. if(strstr(msg, "alarm"))
  75. noted(NCONT);
  76. else if(strstr(msg, "die"))
  77. exits("errors");
  78. else
  79. noted(NDFLT);
  80. }
  81. void
  82. clean(ushort seq, vlong now, Icmp *ip)
  83. {
  84. Req **l, *r;
  85. lock(&listlock);
  86. last = nil;
  87. for(l = &first; *l; ){
  88. r = *l;
  89. if(ip && r->seq == seq){
  90. r->rtt = now-r->time;
  91. r->ttl = ip->ttl;
  92. reply(r, ip);
  93. }
  94. if(now-r->time > MINUTE){
  95. *l = r->next;
  96. r->rtt = now-r->time;
  97. if(ip)
  98. r->ttl = ip->ttl;
  99. if(r->replied == 0)
  100. lost(r, ip);
  101. free(r);
  102. }else{
  103. last = r;
  104. l = &(r->next);
  105. }
  106. }
  107. unlock(&listlock);
  108. }
  109. void
  110. sender(int fd, int msglen, int interval, int n)
  111. {
  112. char buf[64*1024+512];
  113. Icmp *ip;
  114. int i, extra;
  115. Req *r;
  116. ushort seq;
  117. ip = (Icmp*)buf;
  118. srand(time(0));
  119. firstseq = seq = rand();
  120. for(i = 32; i < msglen; i++)
  121. buf[i] = i;
  122. ip->type = EchoRequest;
  123. ip->code = 0;
  124. for(i = 0; i < n; i++){
  125. if(i != 0){
  126. extra = rint? nrand(interval): 0;
  127. sleep(interval + extra);
  128. }
  129. r = malloc(sizeof *r);
  130. if(r != nil){
  131. hnputs(ip->seq, seq);
  132. r->seq = seq;
  133. r->next = nil;
  134. r->replied = 0;
  135. r->time = nsec(); /* avoid early free in reply! */
  136. lock(&listlock);
  137. if(first == nil)
  138. first = r;
  139. else
  140. last->next = r;
  141. last = r;
  142. unlock(&listlock);
  143. r->time = nsec();
  144. if(write(fd, ip, msglen) < msglen){
  145. fprint(2, "%s: write failed: %r\n", argv0);
  146. return;
  147. }
  148. seq++;
  149. }
  150. }
  151. done = 1;
  152. }
  153. void
  154. rcvr(int fd, int msglen, int interval, int nmsg)
  155. {
  156. uchar buf[64*1024+512];
  157. Icmp *ip;
  158. ushort x;
  159. int i, n, munged;
  160. vlong now;
  161. Req *r;
  162. ip = (Icmp*)buf;
  163. sum = 0;
  164. while(lostmsgs+rcvdmsgs < nmsg){
  165. alarm((nmsg-lostmsgs-rcvdmsgs)*interval+5000);
  166. n = read(fd, buf, sizeof(buf));
  167. alarm(0);
  168. now = nsec();
  169. if(n <= 0){ /* read interrupted - time to go */
  170. clean(0, now+MINUTE, nil);
  171. continue;
  172. }
  173. if(n < msglen){
  174. print("bad len %d/%d\n", n, msglen);
  175. continue;
  176. }
  177. munged = 0;
  178. for(i = 32; i < msglen; i++)
  179. if(buf[i] != (i&0xff))
  180. munged++;
  181. if(munged)
  182. print("currupted reply\n");
  183. x = nhgets(ip->seq);
  184. if(ip->type != EchoReply || ip->code != 0) {
  185. print("bad sequence/code/type %d/%d/%d\n",
  186. ip->type, ip->code, x);
  187. continue;
  188. }
  189. clean(x, now, ip);
  190. }
  191. lock(&listlock);
  192. for(r = first; r; r = r->next)
  193. if(r->replied == 0)
  194. lostmsgs++;
  195. unlock(&listlock);
  196. if(lostmsgs)
  197. print("%d out of %d messages lost\n", lostmsgs, lostmsgs+rcvdmsgs);
  198. }
  199. void
  200. usage(void)
  201. {
  202. fprint(2, "usage: %s [-alq] [-s msgsize] [-i millisecs] [-n #pings] destination\n", argv0);
  203. exits("usage");
  204. }
  205. void
  206. main(int argc, char **argv)
  207. {
  208. int fd;
  209. int msglen, interval, nmsg;
  210. nsec(); /* make sure time file is already open */
  211. fmtinstall('V', eipfmt);
  212. msglen = interval = 0;
  213. nmsg = MAXMSG;
  214. ARGBEGIN {
  215. case 'l':
  216. lostonly++;
  217. break;
  218. case 'd':
  219. debug++;
  220. break;
  221. case 's':
  222. msglen = atoi(ARGF());
  223. break;
  224. case 'i':
  225. interval = atoi(ARGF());
  226. break;
  227. case 'n':
  228. nmsg = atoi(ARGF());
  229. break;
  230. case 'a':
  231. addresses = 1;
  232. break;
  233. case 'q':
  234. quiet = 1;
  235. break;
  236. case 'r':
  237. rint = 1;
  238. break;
  239. case 'f':
  240. flood = 1;
  241. break;
  242. } ARGEND;
  243. if(msglen < 32)
  244. msglen = 64;
  245. if(msglen >= 65*1024)
  246. msglen = 65*1024-1;
  247. if(interval <= 0 && !flood)
  248. interval = SLEEPMS;
  249. if(argc < 1)
  250. usage();
  251. notify(catch);
  252. fd = dial(netmkaddr(argv[0], "icmp", "1"), 0, 0, 0);
  253. if(fd < 0){
  254. fprint(2, "%s: couldn't dial: %r\n", argv0);
  255. exits("dialing");
  256. }
  257. print("sending %d %d byte messages %d ms apart\n", nmsg, msglen, interval);
  258. switch(rfork(RFPROC|RFMEM|RFFDG)){
  259. case -1:
  260. fprint(2, "%s: can't fork: %r\n", argv0);
  261. case 0:
  262. rcvr(fd, msglen, interval, nmsg);
  263. exits(0);
  264. default:
  265. sender(fd, msglen, interval, nmsg);
  266. wait();
  267. exits(lostmsgs ? "lost messages" : "");
  268. }
  269. }
  270. void
  271. reply(Req *r, Icmp *ip)
  272. {
  273. r->rtt /= 1000LL;
  274. sum += r->rtt;
  275. if(!r->replied)
  276. rcvdmsgs++;
  277. if(!quiet && !lostonly){
  278. if(addresses)
  279. print("%ud: %V->%V rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
  280. r->seq-firstseq,
  281. ip->src, ip->dst,
  282. r->rtt, sum/rcvdmsgs, r->ttl);
  283. else
  284. print("%ud: rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
  285. r->seq-firstseq,
  286. r->rtt, sum/rcvdmsgs, r->ttl);
  287. }
  288. r->replied = 1;
  289. }
  290. void
  291. lost(Req *r, Icmp *ip)
  292. {
  293. if(!quiet){
  294. if(addresses)
  295. print("lost %ud: %V->%V\n", r->seq-firstseq,
  296. ip->src, ip->dst);
  297. else
  298. print("lost %ud\n", r->seq-firstseq);
  299. }
  300. lostmsgs++;
  301. }