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)
306 uchar buf[64*1024+512];
311 while(lostmsgs+rcvdmsgs < nmsg){
312 alarm((nmsg-lostmsgs-rcvdmsgs)*interval+waittime);
313 n = read(fd, buf, sizeof buf);
316 if(n <= 0){ /* read interrupted - time to go */
317 clean(0, now+MINUTE, nil);
321 print("bad len %d/%d\n", n, msglen);
326 for(i = proto->iphdrsz + ICMP_HDRSIZE; i < msglen; i++)
327 if(buf[i] != (uchar)i)
330 print("corrupted reply\n");
331 x = nhgets(icmp->seq);
332 if(icmp->type != proto->echoreply || icmp->code != 0) {
333 print("bad type/code/sequence %d/%d/%d (want %d/%d/%d)\n",
334 icmp->type, icmp->code, x,
335 proto->echoreply, 0, x);
342 for(r = first; r; r = r->next)
347 if(!quiet && lostmsgs)
348 print("%d out of %d messages lost\n", lostmsgs,
353 isdottedquad(char *name)
355 int dot = 0, digit = 0;
357 for (; *name != '\0'; name++)
360 else if (isdigit(*name))
370 int colon = 0, hex = 0;
372 for (; *name != '\0'; name++)
375 else if (isxdigit(*name))
382 /* from /sys/src/libc/9sys/dial.c */
390 typedef struct DS DS;
405 * parse a dial string
408 _dial_string_parse(char *str, DS *ds)
412 strncpy(ds->buf, str, Maxstring);
413 ds->buf[Maxstring-1] = 0;
415 p = strchr(ds->buf, '!');
421 if(*ds->buf != '/' && *ds->buf != '#'){
425 for(p2 = p; *p2 != '/'; p2--)
428 ds->netdir = ds->buf;
436 /* end excerpt from /sys/src/libc/9sys/dial.c */
438 /* side effect: sets network & target */
443 char *root, *ip, *pr;
446 _dial_string_parse(name, &ds);
448 /* cope with leading /net.alt/icmp! and the like */
450 if (ds.netdir != nil) {
451 pr = strrchr(ds.netdir, '/');
457 network = strdup(root);
459 if (strcmp(pr, v4pr.net) == 0)
461 if (strcmp(pr, v6pr.net) == 0)
465 /* if it's a literal, it's obvious from syntax which proto it is */
467 target = strdup(ds.rem);
468 if (isdottedquad(ds.rem))
470 else if (isv6lit(ds.rem))
473 /* map name to ip and look at its syntax */
474 ip = csgetvalue(root, "sys", ds.rem, "ip", nil);
476 ip = csgetvalue(root, "dom", ds.rem, "ip", nil);
478 ip = csgetvalue(root, "sys", ds.rem, "ipv6", nil);
480 ip = csgetvalue(root, "dom", ds.rem, "ipv6", nil);
488 main(int argc, char **argv)
490 int fd, msglen, interval, nmsg;
493 nsec(); /* make sure time file is already open */
495 fmtinstall('V', eipfmt);
496 fmtinstall('I', eipfmt);
498 msglen = interval = 0;
514 interval = atoi(EARGF(usage()));
522 nmsg = atoi(EARGF(usage()));
533 msglen = atoi(EARGF(usage()));
536 waittime = atoi(EARGF(usage()));
545 if(msglen < proto->iphdrsz + ICMP_HDRSIZE)
546 msglen = proto->iphdrsz + ICMP_HDRSIZE;
549 if(msglen >= 64*1024)
551 if(interval <= 0 && !flood)
559 if (!isv4name(argv[0]))
561 ds = netmkaddr(argv[0], proto->net, "1");
562 fd = dial(ds, 0, 0, 0);
564 fprint(2, "%s: couldn't dial %s: %r\n", argv0, ds);
569 print("sending %d %d byte messages %d ms apart to %s\n",
570 nmsg, msglen, interval, ds);
572 switch(rfork(RFPROC|RFMEM|RFFDG)){
574 fprint(2, "%s: can't fork: %r\n", argv0);
577 rcvr(fd, msglen, interval, nmsg);
580 sender(fd, msglen, interval, nmsg);
582 exits(lostmsgs ? "lost messages" : "");
587 reply(Req *r, void *v)
593 if(!quiet && !lostonly)
595 (*proto->prreply)(r, v);
597 print("%ud: rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
598 r->seq - firstseq, r->rtt, sum/rcvdmsgs, r->ttl);
603 lost(Req *r, void *v)
606 if(addresses && v != nil)
607 (*proto->prlost)(r->seq - firstseq, v);
609 print("lost %ud\n", r->seq - firstseq);