17dd7cddfSDavid du Colombier #include <u.h> 27dd7cddfSDavid du Colombier #include <libc.h> 33ff48bf5SDavid du Colombier #include <ip.h> 47dd7cddfSDavid du Colombier 57dd7cddfSDavid du Colombier typedef struct Icmp Icmp; 67dd7cddfSDavid du Colombier struct Icmp 77dd7cddfSDavid du Colombier { 87dd7cddfSDavid du Colombier uchar vihl; /* Version and header length */ 97dd7cddfSDavid du Colombier uchar tos; /* Type of service */ 107dd7cddfSDavid du Colombier uchar length[2]; /* packet length */ 117dd7cddfSDavid du Colombier uchar id[2]; /* Identification */ 127dd7cddfSDavid du Colombier uchar frag[2]; /* Fragment information */ 137dd7cddfSDavid du Colombier uchar ttl; /* Time to live */ 147dd7cddfSDavid du Colombier uchar proto; /* Protocol */ 157dd7cddfSDavid du Colombier uchar ipcksum[2]; /* Header checksum */ 167dd7cddfSDavid du Colombier uchar src[4]; /* Ip source */ 177dd7cddfSDavid du Colombier uchar dst[4]; /* Ip destination */ 187dd7cddfSDavid du Colombier uchar type; 197dd7cddfSDavid du Colombier uchar code; 207dd7cddfSDavid du Colombier uchar cksum[2]; 217dd7cddfSDavid du Colombier uchar icmpid[2]; 227dd7cddfSDavid du Colombier uchar seq[2]; 237dd7cddfSDavid du Colombier uchar data[1]; 247dd7cddfSDavid du Colombier }; 257dd7cddfSDavid du Colombier 267dd7cddfSDavid du Colombier enum 277dd7cddfSDavid du Colombier { /* Packet Types */ 287dd7cddfSDavid du Colombier EchoReply = 0, 297dd7cddfSDavid du Colombier Unreachable = 3, 307dd7cddfSDavid du Colombier SrcQuench = 4, 317dd7cddfSDavid du Colombier EchoRequest = 8, 327dd7cddfSDavid du Colombier TimeExceed = 11, 337dd7cddfSDavid du Colombier Timestamp = 13, 347dd7cddfSDavid du Colombier TimestampReply = 14, 357dd7cddfSDavid du Colombier InfoRequest = 15, 367dd7cddfSDavid du Colombier InfoReply = 16, 377dd7cddfSDavid du Colombier 387dd7cddfSDavid du Colombier ICMP_IPSIZE = 20, 397dd7cddfSDavid du Colombier ICMP_HDRSIZE = 8, 407dd7cddfSDavid du Colombier 417dd7cddfSDavid du Colombier MAXMSG = 32, 427dd7cddfSDavid du Colombier SLEEPMS = 1000, 437dd7cddfSDavid du Colombier }; 447dd7cddfSDavid du Colombier 457dd7cddfSDavid du Colombier typedef struct Req Req; 467dd7cddfSDavid du Colombier struct Req 477dd7cddfSDavid du Colombier { 4800c1ae4bSDavid du Colombier ushort seq; // sequence number 497dd7cddfSDavid du Colombier vlong time; // time sent 503ff48bf5SDavid du Colombier vlong rtt; 517dd7cddfSDavid du Colombier int ttl; 523ff48bf5SDavid du Colombier int replied; 537dd7cddfSDavid du Colombier Req *next; 547dd7cddfSDavid du Colombier }; 557dd7cddfSDavid du Colombier Req *first; // request list 567dd7cddfSDavid du Colombier Req *last; // ... 577dd7cddfSDavid du Colombier Lock listlock; 587dd7cddfSDavid du Colombier 597dd7cddfSDavid du Colombier char *argv0; 607dd7cddfSDavid du Colombier int debug; 617dd7cddfSDavid du Colombier int quiet; 6200c1ae4bSDavid du Colombier int lostonly; 637dd7cddfSDavid du Colombier int lostmsgs; 647dd7cddfSDavid du Colombier int rcvdmsgs; 657dd7cddfSDavid du Colombier int done; 66da51d93aSDavid du Colombier int rint; 677dd7cddfSDavid du Colombier vlong sum; 683ff48bf5SDavid du Colombier ushort firstseq; 693ff48bf5SDavid du Colombier int addresses; 707dd7cddfSDavid du Colombier 717dd7cddfSDavid du Colombier void usage(void); 7200c1ae4bSDavid du Colombier void lost(Req*, Icmp*); 733ff48bf5SDavid du Colombier void reply(Req*, Icmp*); 747dd7cddfSDavid du Colombier 757dd7cddfSDavid du Colombier #define SECOND 1000000000LL 767dd7cddfSDavid du Colombier #define MINUTE (60LL*SECOND) 777dd7cddfSDavid du Colombier 787dd7cddfSDavid du Colombier static void 797dd7cddfSDavid du Colombier catch(void *a, char *msg) 807dd7cddfSDavid du Colombier { 817dd7cddfSDavid du Colombier USED(a); 827dd7cddfSDavid du Colombier if(strstr(msg, "alarm")) 837dd7cddfSDavid du Colombier noted(NCONT); 848aafde0cSDavid du Colombier else if(strstr(msg, "die")) 858aafde0cSDavid du Colombier exits("errors"); 867dd7cddfSDavid du Colombier else 877dd7cddfSDavid du Colombier noted(NDFLT); 887dd7cddfSDavid du Colombier } 897dd7cddfSDavid du Colombier 907dd7cddfSDavid du Colombier void 913ff48bf5SDavid du Colombier clean(ushort seq, vlong now, Icmp *ip) 927dd7cddfSDavid du Colombier { 937dd7cddfSDavid du Colombier Req **l, *r; 947dd7cddfSDavid du Colombier 957dd7cddfSDavid du Colombier lock(&listlock); 967dd7cddfSDavid du Colombier for(l = &first; *l; ){ 977dd7cddfSDavid du Colombier r = *l; 983ff48bf5SDavid du Colombier 998aafde0cSDavid du Colombier if(ip && r->seq == seq){ 1003ff48bf5SDavid du Colombier r->rtt = now-r->time; 1013ff48bf5SDavid du Colombier r->ttl = ip->ttl; 1023ff48bf5SDavid du Colombier reply(r, ip); 1033ff48bf5SDavid du Colombier } 1043ff48bf5SDavid du Colombier 1053ff48bf5SDavid du Colombier if(now-r->time > MINUTE){ 1067dd7cddfSDavid du Colombier *l = r->next; 1073ff48bf5SDavid du Colombier r->rtt = now-r->time; 1088aafde0cSDavid du Colombier if(ip) 1093ff48bf5SDavid du Colombier r->ttl = ip->ttl; 1103ff48bf5SDavid du Colombier if(r->replied == 0) 11100c1ae4bSDavid du Colombier lost(r, ip); 1127dd7cddfSDavid du Colombier free(r); 11359cc4ca5SDavid du Colombier } else { 11459cc4ca5SDavid du Colombier last = r; 1157dd7cddfSDavid du Colombier l = &(r->next); 1167dd7cddfSDavid du Colombier } 11759cc4ca5SDavid du Colombier } 1187dd7cddfSDavid du Colombier unlock(&listlock); 1197dd7cddfSDavid du Colombier } 1207dd7cddfSDavid du Colombier 1217dd7cddfSDavid du Colombier void 1227dd7cddfSDavid du Colombier sender(int fd, int msglen, int interval, int n) 1237dd7cddfSDavid du Colombier { 1247dd7cddfSDavid du Colombier char buf[64*1024+512]; 1257dd7cddfSDavid du Colombier Icmp *ip; 126da51d93aSDavid du Colombier int i, extra; 1277dd7cddfSDavid du Colombier Req *r; 1287dd7cddfSDavid du Colombier ushort seq; 1297dd7cddfSDavid du Colombier 1307dd7cddfSDavid du Colombier ip = (Icmp*)buf; 1317dd7cddfSDavid du Colombier 1327dd7cddfSDavid du Colombier srand(time(0)); 1333ff48bf5SDavid du Colombier firstseq = seq = rand(); 1347dd7cddfSDavid du Colombier 1359a747e4fSDavid du Colombier for(i = 32; i < msglen; i++) 1369a747e4fSDavid du Colombier buf[i] = i; 1377dd7cddfSDavid du Colombier ip->type = EchoRequest; 1387dd7cddfSDavid du Colombier ip->code = 0; 1397dd7cddfSDavid du Colombier 1407dd7cddfSDavid du Colombier for(i = 0; i < n; i++){ 141da51d93aSDavid du Colombier if(i != 0){ 142da51d93aSDavid du Colombier extra = rint? nrand(interval): 0; 143da51d93aSDavid du Colombier sleep(interval + extra); 144da51d93aSDavid du Colombier } 1457dd7cddfSDavid du Colombier r = malloc(sizeof *r); 1467dd7cddfSDavid du Colombier if(r != nil){ 1477dd7cddfSDavid du Colombier ip->seq[0] = seq; 1487dd7cddfSDavid du Colombier ip->seq[1] = seq>>8; 1497dd7cddfSDavid du Colombier r->seq = seq; 1507dd7cddfSDavid du Colombier r->next = nil; 1517dd7cddfSDavid du Colombier lock(&listlock); 1527dd7cddfSDavid du Colombier if(first == nil) 1537dd7cddfSDavid du Colombier first = r; 1547dd7cddfSDavid du Colombier else 1557dd7cddfSDavid du Colombier last->next = r; 1567dd7cddfSDavid du Colombier last = r; 1577dd7cddfSDavid du Colombier unlock(&listlock); 1583ff48bf5SDavid du Colombier r->replied = 0; 1597dd7cddfSDavid du Colombier r->time = nsec(); 1607dd7cddfSDavid du Colombier if(write(fd, ip, msglen) < msglen){ 1617dd7cddfSDavid du Colombier fprint(2, "%s: write failed: %r\n", argv0); 1627dd7cddfSDavid du Colombier return; 1637dd7cddfSDavid du Colombier } 1647dd7cddfSDavid du Colombier seq++; 1657dd7cddfSDavid du Colombier } 1667dd7cddfSDavid du Colombier } 1677dd7cddfSDavid du Colombier done = 1; 1687dd7cddfSDavid du Colombier } 1697dd7cddfSDavid du Colombier 1707dd7cddfSDavid du Colombier void 171*fbfc9db9SDavid du Colombier rcvr(int fd, int msglen, int interval, int nmsg) 1727dd7cddfSDavid du Colombier { 1737dd7cddfSDavid du Colombier uchar buf[64*1024+512]; 1747dd7cddfSDavid du Colombier Icmp *ip; 1757dd7cddfSDavid du Colombier ushort x; 1767dd7cddfSDavid du Colombier int i, n, munged; 1777dd7cddfSDavid du Colombier vlong now; 17859cc4ca5SDavid du Colombier Req *r; 1797dd7cddfSDavid du Colombier 1807dd7cddfSDavid du Colombier ip = (Icmp*)buf; 1817dd7cddfSDavid du Colombier sum = 0; 1827dd7cddfSDavid du Colombier 1838aafde0cSDavid du Colombier while(lostmsgs+rcvdmsgs < nmsg){ 18459cc4ca5SDavid du Colombier alarm((nmsg-lostmsgs-rcvdmsgs)*interval+5000); 1857dd7cddfSDavid du Colombier n = read(fd, buf, sizeof(buf)); 1867dd7cddfSDavid du Colombier alarm(0); 1877dd7cddfSDavid du Colombier now = nsec(); 1888aafde0cSDavid du Colombier if(n <= 0){ /* read interrupted - time to go */ 1898aafde0cSDavid du Colombier fprint(2, "clean\n"); 1908aafde0cSDavid du Colombier clean(0, now+MINUTE, nil); 1918aafde0cSDavid du Colombier continue; 19200c1ae4bSDavid du Colombier } 1937dd7cddfSDavid du Colombier if(n < msglen){ 1947dd7cddfSDavid du Colombier print("bad len %d/%d\n", n, msglen); 1957dd7cddfSDavid du Colombier continue; 1967dd7cddfSDavid du Colombier } 19780ee5cbfSDavid du Colombier munged = 0; 1989a747e4fSDavid du Colombier for(i = 32; i < msglen; i++) 1999a747e4fSDavid du Colombier if(buf[i] != (i&0xff)) 20080ee5cbfSDavid du Colombier munged++; 20180ee5cbfSDavid du Colombier if(munged) 2029a747e4fSDavid du Colombier print("currupted reply\n"); 2039a747e4fSDavid du Colombier x = (ip->seq[1]<<8)|ip->seq[0]; 2047dd7cddfSDavid du Colombier if(ip->type != EchoReply || ip->code != 0) { 2057dd7cddfSDavid du Colombier print("bad sequence/code/type %d/%d/%d\n", 2067dd7cddfSDavid du Colombier ip->type, ip->code, x); 2077dd7cddfSDavid du Colombier continue; 2087dd7cddfSDavid du Colombier } 2093ff48bf5SDavid du Colombier clean(x, now, ip); 2107dd7cddfSDavid du Colombier } 21159cc4ca5SDavid du Colombier 21259cc4ca5SDavid du Colombier lock(&listlock); 21359cc4ca5SDavid du Colombier for(r = first; r; r = r->next) 2143ff48bf5SDavid du Colombier if(r->replied == 0) 21559cc4ca5SDavid du Colombier lostmsgs++; 21659cc4ca5SDavid du Colombier unlock(&listlock); 21759cc4ca5SDavid du Colombier 2187dd7cddfSDavid du Colombier if(lostmsgs) 2197dd7cddfSDavid du Colombier print("%d out of %d messages lost\n", lostmsgs, lostmsgs+rcvdmsgs); 22000c1ae4bSDavid du Colombier } 22100c1ae4bSDavid du Colombier 22200c1ae4bSDavid du Colombier void 22300c1ae4bSDavid du Colombier usage(void) 22400c1ae4bSDavid du Colombier { 22500c1ae4bSDavid du Colombier fprint(2, "usage: %s [-alq] [-s msgsize] [-i millisecs] [-n #pings] destination\n", argv0); 22600c1ae4bSDavid du Colombier exits("usage"); 2277dd7cddfSDavid du Colombier } 2287dd7cddfSDavid du Colombier 2297dd7cddfSDavid du Colombier void 2307dd7cddfSDavid du Colombier main(int argc, char **argv) 2317dd7cddfSDavid du Colombier { 2327dd7cddfSDavid du Colombier int fd; 2337dd7cddfSDavid du Colombier int msglen, interval, nmsg; 2347dd7cddfSDavid du Colombier 2357dd7cddfSDavid du Colombier nsec(); /* make sure time file is already open */ 2367dd7cddfSDavid du Colombier 2373ff48bf5SDavid du Colombier fmtinstall('V', eipfmt); 2383ff48bf5SDavid du Colombier 2397dd7cddfSDavid du Colombier msglen = interval = 0; 2407dd7cddfSDavid du Colombier nmsg = MAXMSG; 2417dd7cddfSDavid du Colombier ARGBEGIN { 24200c1ae4bSDavid du Colombier case 'l': 24300c1ae4bSDavid du Colombier lostonly++; 24400c1ae4bSDavid du Colombier break; 2457dd7cddfSDavid du Colombier case 'd': 2467dd7cddfSDavid du Colombier debug++; 2477dd7cddfSDavid du Colombier break; 2487dd7cddfSDavid du Colombier case 's': 2497dd7cddfSDavid du Colombier msglen = atoi(ARGF()); 2507dd7cddfSDavid du Colombier break; 2517dd7cddfSDavid du Colombier case 'i': 2527dd7cddfSDavid du Colombier interval = atoi(ARGF()); 2537dd7cddfSDavid du Colombier break; 2547dd7cddfSDavid du Colombier case 'n': 2557dd7cddfSDavid du Colombier nmsg = atoi(ARGF()); 2567dd7cddfSDavid du Colombier break; 2573ff48bf5SDavid du Colombier case 'a': 2583ff48bf5SDavid du Colombier addresses = 1; 2593ff48bf5SDavid du Colombier break; 2607dd7cddfSDavid du Colombier case 'q': 2617dd7cddfSDavid du Colombier quiet = 1; 2627dd7cddfSDavid du Colombier break; 263da51d93aSDavid du Colombier case 'r': 264da51d93aSDavid du Colombier rint = 1; 265da51d93aSDavid du Colombier break; 2667dd7cddfSDavid du Colombier } ARGEND; 2677dd7cddfSDavid du Colombier if(msglen < 32) 2687dd7cddfSDavid du Colombier msglen = 64; 2697dd7cddfSDavid du Colombier if(msglen >= 65*1024) 2707dd7cddfSDavid du Colombier msglen = 65*1024-1; 2717dd7cddfSDavid du Colombier if(interval <= 0) 2727dd7cddfSDavid du Colombier interval = SLEEPMS; 2737dd7cddfSDavid du Colombier 2747dd7cddfSDavid du Colombier if(argc < 1) 2757dd7cddfSDavid du Colombier usage(); 2767dd7cddfSDavid du Colombier 2777dd7cddfSDavid du Colombier notify(catch); 2787dd7cddfSDavid du Colombier 2797dd7cddfSDavid du Colombier fd = dial(netmkaddr(argv[0], "icmp", "1"), 0, 0, 0); 2807dd7cddfSDavid du Colombier if(fd < 0){ 2817dd7cddfSDavid du Colombier fprint(2, "%s: couldn't dial: %r\n", argv0); 2827dd7cddfSDavid du Colombier exits("dialing"); 2837dd7cddfSDavid du Colombier } 2847dd7cddfSDavid du Colombier 2857dd7cddfSDavid du Colombier print("sending %d %d byte messages %d ms apart\n", nmsg, msglen, interval); 2867dd7cddfSDavid du Colombier 2877dd7cddfSDavid du Colombier switch(rfork(RFPROC|RFMEM|RFFDG)){ 2887dd7cddfSDavid du Colombier case -1: 2897dd7cddfSDavid du Colombier fprint(2, "%s: can't fork: %r\n", argv0); 2907dd7cddfSDavid du Colombier case 0: 291*fbfc9db9SDavid du Colombier rcvr(fd, msglen, interval, nmsg); 2927dd7cddfSDavid du Colombier exits(0); 2937dd7cddfSDavid du Colombier default: 2947dd7cddfSDavid du Colombier sender(fd, msglen, interval, nmsg); 2959a747e4fSDavid du Colombier wait(); 296443f8232SDavid du Colombier exits(lostmsgs ? "lost messages" : ""); 2977dd7cddfSDavid du Colombier } 2987dd7cddfSDavid du Colombier } 2997dd7cddfSDavid du Colombier 3007dd7cddfSDavid du Colombier void 3013ff48bf5SDavid du Colombier reply(Req *r, Icmp *ip) 3027dd7cddfSDavid du Colombier { 3033ff48bf5SDavid du Colombier r->rtt /= 1000LL; 3043ff48bf5SDavid du Colombier sum += r->rtt; 3058aafde0cSDavid du Colombier if(!r->replied) 3068aafde0cSDavid du Colombier rcvdmsgs++; 30700c1ae4bSDavid du Colombier if(!quiet && !lostonly){ 3083ff48bf5SDavid du Colombier if(addresses) 3093ff48bf5SDavid du Colombier print("%ud: %V->%V rtt %lld µs, avg rtt %lld µs, ttl = %d\n", 3103ff48bf5SDavid du Colombier r->seq-firstseq, 3113ff48bf5SDavid du Colombier ip->src, ip->dst, 3123ff48bf5SDavid du Colombier r->rtt, sum/rcvdmsgs, r->ttl); 3133ff48bf5SDavid du Colombier else 3143ff48bf5SDavid du Colombier print("%ud: rtt %lld µs, avg rtt %lld µs, ttl = %d\n", 3153ff48bf5SDavid du Colombier r->seq-firstseq, 3163ff48bf5SDavid du Colombier r->rtt, sum/rcvdmsgs, r->ttl); 3173ff48bf5SDavid du Colombier } 3183ff48bf5SDavid du Colombier r->replied = 1; 3197dd7cddfSDavid du Colombier } 32000c1ae4bSDavid du Colombier 32100c1ae4bSDavid du Colombier void 32200c1ae4bSDavid du Colombier lost(Req *r, Icmp *ip) 32300c1ae4bSDavid du Colombier { 32400c1ae4bSDavid du Colombier if(!quiet){ 32500c1ae4bSDavid du Colombier if(addresses) 3268aafde0cSDavid du Colombier print("lost %ud: %V->%V\n", r->seq-firstseq, 3278aafde0cSDavid du Colombier ip->src, ip->dst); 32800c1ae4bSDavid du Colombier else 3298aafde0cSDavid du Colombier print("lost %ud\n", r->seq-firstseq); 33000c1ae4bSDavid du Colombier } 33100c1ae4bSDavid du Colombier lostmsgs++; 33200c1ae4bSDavid du Colombier } 3338aafde0cSDavid du Colombier 334