1 /* ping for ip v4 and v6 */
14 SECOND = 1000000000LL,
18 typedef struct Req Req;
21 ushort seq; /* sequence number */
22 vlong time; /* time sent */
36 void (*prreply)(Req *r, void *v);
37 void (*prlost)(ushort seq, void *v);
41 Req *first; /* request list */
60 static char *network, *target;
62 void lost(Req*, void*);
63 void reply(Req*, void*);
69 "usage: %s [-6alq] [-s msgsize] [-i millisecs] [-n #pings] dest\n",
75 catch(void *a, char *msg)
78 if(strstr(msg, "alarm"))
80 else if(strstr(msg, "die"))
87 prlost4(ushort seq, void *v)
91 print("lost %ud: %V -> %V\n", seq, ip4->src, ip4->dst);
95 prlost6(ushort seq, void *v)
99 print("lost %ud: %I -> %I\n", seq, ip6->src, ip6->dst);
103 prreply4(Req *r, void *v)
107 print("%ud: %V -> %V rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
108 r->seq - firstseq, ip4->src, ip4->dst, r->rtt, sum/rcvdmsgs,
113 prreply6(Req *r, void *v)
117 print("%ud: %I -> %I rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
118 r->seq - firstseq, ip6->src, ip6->dst, r->rtt, sum/rcvdmsgs,
122 static Proto v4pr = {
124 EchoRequest, EchoReply,
128 static Proto v6pr = {
130 EchoRequestV6, EchoReplyV6,
135 static Proto *proto = &v4pr;
143 return (Icmphdr *)(p + proto->iphdrsz);
147 clean(ushort seq, vlong now, void *v)
154 if (proto->version == 4)
155 ttl = ((Ip4hdr *)v)->ttl;
157 ttl = ((Ip6hdr *)v)->ttl;
161 for(l = &first; *l; ){
164 if(v && r->seq == seq){
165 r->rtt = now-r->time;
170 if(now-r->time > MINUTE){
172 r->rtt = now-r->time;
186 static uchar loopbacknet[IPaddrlen] = {
192 static uchar loopbackmask[IPaddrlen] = {
193 0xff, 0xff, 0xff, 0xff,
194 0xff, 0xff, 0xff, 0xff,
195 0xff, 0xff, 0xff, 0xff,
200 * find first ip addr suitable for proto and
201 * that isn't the friggin loopback address.
202 * deprecate link-local and multicast addresses.
205 myipvnaddr(uchar *ip, Proto *proto, char *net)
210 uchar mynet[IPaddrlen], linklocal[IPaddrlen];
213 ipmove(linklocal, IPnoaddr);
214 wantv4 = proto->version == 4;
215 ifc = readipifc(net, ifc, -1);
216 for(nifc = ifc; nifc; nifc = nifc->next)
217 for(lifc = nifc->lifc; lifc; lifc = lifc->next){
218 maskip(lifc->ip, loopbackmask, mynet);
219 if(ipcmp(mynet, loopbacknet) == 0)
221 if(ISIPV6MCAST(lifc->ip) || ISIPV6LINKLOCAL(lifc->ip)) {
222 ipmove(linklocal, lifc->ip);
225 ipisv4 = isv4(lifc->ip) != 0;
226 if(ipcmp(lifc->ip, IPnoaddr) != 0 && wantv4 == ipisv4){
227 ipmove(ip, lifc->ip);
231 /* no global unicast addrs found, fall back to link-local, if any */
232 ipmove(ip, linklocal);
233 return ipcmp(ip, IPnoaddr) == 0? -1: 0;
237 sender(int fd, int msglen, int interval, int n)
241 char buf[64*1024+512];
242 uchar me[IPaddrlen], mev4[IPv4addrlen];
247 firstseq = seq = rand();
250 memset(buf, 0, proto->iphdrsz + ICMP_HDRSIZE);
251 for(i = proto->iphdrsz + ICMP_HDRSIZE; i < msglen; i++)
253 icmp->type = proto->echocmd;
256 /* arguably the kernel should fill in the right src addr. */
257 myipvnaddr(me, proto, network);
258 if (proto->version == 4) {
260 memmove(((Ip4hdr *)buf)->src, mev4, IPv4addrlen);
262 ipmove(((Ip6hdr *)buf)->src, me);
264 print("\t%I -> %s\n", me, target);
266 if(rint != 0 && interval <= 0)
269 for(i = 0; i < n; i++){
272 extra = nrand(interval);
273 sleep(interval + extra);
275 r = malloc(sizeof *r);
278 hnputs(icmp->seq, seq);
282 r->time = nsec(); /* avoid early free in reply! */
291 if(write(fd, buf, msglen) < msglen){
292 fprint(2, "%s: write failed: %r\n", argv0);
301 rcvr(int fd, int msglen, int interval, int nmsg)
307 uchar buf[64*1024+512];
312 while(lostmsgs+rcvdmsgs < nmsg){
313 alarm((nmsg-lostmsgs-rcvdmsgs)*interval+waittime);
314 n = read(fd, buf, sizeof buf);
317 strcpy(err, "got eof");
319 rerrstr(err, sizeof(err));
323 clean(0, now+MINUTE, nil);
324 if(strstr(err, "interrupted") == nil)
329 print("bad len %d/%d\n", n, msglen);
334 for(i = proto->iphdrsz + ICMP_HDRSIZE; i < msglen; i++)
335 if(buf[i] != (uchar)i)
338 print("corrupted reply\n");
339 x = nhgets(icmp->seq);
340 if(icmp->type != proto->echoreply || icmp->code != 0) {
341 print("bad type/code/sequence %d/%d/%d (want %d/%d/%d)\n",
342 icmp->type, icmp->code, x,
343 proto->echoreply, 0, x);
350 for(r = first; r; r = r->next)
355 if(!quiet && lostmsgs)
356 print("%d out of %d messages lost\n", lostmsgs,
361 isdottedquad(char *name)
363 int dot = 0, digit = 0;
365 for (; *name != '\0'; name++)
368 else if (isdigit(*name))
378 int colon = 0, hex = 0;
380 for (; *name != '\0'; name++)
383 else if (isxdigit(*name))
390 /* from /sys/src/libc/9sys/dial.c */
398 typedef struct DS DS;
413 * parse a dial string
416 _dial_string_parse(char *str, DS *ds)
420 strncpy(ds->buf, str, Maxstring);
421 ds->buf[Maxstring-1] = 0;
423 p = strchr(ds->buf, '!');
429 if(*ds->buf != '/' && *ds->buf != '#'){
433 for(p2 = p; *p2 != '/'; p2--)
436 ds->netdir = ds->buf;
444 /* end excerpt from /sys/src/libc/9sys/dial.c */
446 /* side effect: sets network & target */
451 char *root, *ip, *pr;
454 _dial_string_parse(name, &ds);
456 /* cope with leading /net.alt/icmp! and the like */
458 if (ds.netdir != nil) {
459 pr = strrchr(ds.netdir, '/');
465 network = strdup(root);
467 if (strcmp(pr, v4pr.net) == 0)
469 if (strcmp(pr, v6pr.net) == 0)
473 /* if it's a literal, it's obvious from syntax which proto it is */
475 target = strdup(ds.rem);
476 if (isdottedquad(ds.rem))
478 else if (isv6lit(ds.rem))
481 /* map name to ip and look at its syntax */
482 ip = csgetvalue(root, "sys", ds.rem, "ip", nil);
484 ip = csgetvalue(root, "dom", ds.rem, "ip", nil);
486 ip = csgetvalue(root, "sys", ds.rem, "ipv6", nil);
488 ip = csgetvalue(root, "dom", ds.rem, "ipv6", nil);
496 main(int argc, char **argv)
498 int fd, msglen, interval, nmsg;
501 nsec(); /* make sure time file is already open */
503 fmtinstall('V', eipfmt);
504 fmtinstall('I', eipfmt);
506 msglen = interval = 0;
522 interval = atoi(EARGF(usage()));
530 nmsg = atoi(EARGF(usage()));
541 msglen = atoi(EARGF(usage()));
544 waittime = atoi(EARGF(usage()));
553 if(msglen < proto->iphdrsz + ICMP_HDRSIZE)
554 msglen = proto->iphdrsz + ICMP_HDRSIZE;
557 if(msglen >= 64*1024)
559 if(interval <= 0 && !flood)
567 if (!isv4name(argv[0]))
569 ds = netmkaddr(argv[0], proto->net, "1");
570 fd = dial(ds, 0, 0, 0);
572 fprint(2, "%s: couldn't dial %s: %r\n", argv0, ds);
577 print("sending %d %d byte messages %d ms apart to %s\n",
578 nmsg, msglen, interval, ds);
580 switch(rfork(RFPROC|RFMEM|RFFDG)){
582 fprint(2, "%s: can't fork: %r\n", argv0);
585 rcvr(fd, msglen, interval, nmsg);
588 sender(fd, msglen, interval, nmsg);
590 exits(lostmsgs ? "lost messages" : "");
595 reply(Req *r, void *v)
601 if(!quiet && !lostonly)
603 (*proto->prreply)(r, v);
605 print("%ud: rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
606 r->seq - firstseq, r->rtt, sum/rcvdmsgs, r->ttl);
611 lost(Req *r, void *v)
614 if(addresses && v != nil)
615 (*proto->prlost)(r->seq - firstseq, v);
617 print("lost %ud\n", r->seq - firstseq);