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