From mboxrd@z Thu Jan 1 00:00:00 1970 From: erik quanstrom Date: Fri, 1 Jan 2016 17:12:46 -0800 To: 9fans@9fans.net Message-ID: In-Reply-To: <2F96B09F-7524-473A-B883-9A7B7DD09978@ar.aichi-u.ac.jp> References: <4AE7714E-C18F-4897-ACC1-4F8D35C858AE@ar.aichi-u.ac.jp> <2F96B09F-7524-473A-B883-9A7B7DD09978@ar.aichi-u.ac.jp> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="upas-vnmxkrjeolzkfvxvfjtmgejori" Subject: Re: [9fans] bug or feature ? --- ip/ping -6 Topicbox-Message-UUID: 7ce5de78-ead9-11e9-9d60-3106f5b1d025 This is a multi-part message in MIME format. --upas-vnmxkrjeolzkfvxvfjtmgejori Content-Disposition: inline Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable on reading the man page, i found a small flaw in the implementation. acc= ording to the man page, -6 forces is of icmp6, even if the address is icmp4. i cha= nged ping to do that.as a result, i added a -4 flag which forces the ping to use icmp4= . obviously, there is no native 6-in-4, so this is an error. also, i corrected an indirection of a nil pointer when a name lookup fail= s. my pathetic excuse for unit testing, and a diff are below. my version (h= acks and all) is attached. - erik ps. the hand-implementation of csquery seems to be a result of not trust= ing ndb/cs to be running. odd. does anyone have context on why this would b= e useful? --- ; ip/ping -6an1 bwc sending 1 64 byte messages 1000 ms apart to icmpv6!2402:6b00:22cd:bf80::9= !1 2402:6b00:22cd:bf80::7 -> 2402:6b00:22cd:bf80::9 0: 2402:6b00:22cd:bf80::9 -> 2402:6b00:22cd:bf80::7 rtt 78 =C2=B5s, avg r= tt 78 =C2=B5s, ttl =3D 255 ; ip/ping -4an1 bwc sending 1 64 byte messages 1000 ms apart to icmp!10.1.1.9!1 10.1.1.7 -> 10.1.1.9 0: 10.1.1.9 -> 10.1.1.7 rtt 70 =C2=B5s, avg rtt 70 =C2=B5s, ttl =3D 255 ; ip/ping -an1 bwc sending 1 64 byte messages 1000 ms apart to icmp!10.1.1.9!1 10.1.1.7 -> 10.1.1.9 0: 10.1.1.9 -> 10.1.1.7 rtt 72 =C2=B5s, avg rtt 72 =C2=B5s, ttl =3D 255 ; ip/ping -4an1 10.1.1.7 sending 1 64 byte messages 1000 ms apart to icmp!10.1.1.7!1 10.1.1.7 -> 10.1.1.7 0: 10.1.1.7 -> 10.1.1.7 rtt 15 =C2=B5s, avg rtt 15 =C2=B5s, ttl =3D 255 ; ip/ping -6an1 10.1.1.7 sending 1 64 byte messages 1000 ms apart to icmpv6!10.1.1.7!1 2402:6b00:22cd:bf80::7 -> 10.1.1.7 0: 10.1.1.7 -> 10.1.1.7 rtt 18 =C2=B5s, avg rtt 18 =C2=B5s, ttl =3D 255 ; ip/ping -4an1 2402:6b00:22cd:bf80::7 ip/ping: ip/ping: nametoip: address 2402:6b00:22cd:bf80::7 does not match= proto 4 ; ip/ping -an1 missing ip/ping: cannot write tcp!missing!1 to/net/cs: cs: can't translate addres= s: dns: resource does not exist; negrcode ; diffy -c ping.c /n/dump/2016/0101/sys/src/cmd/ip/ping.c:391,505 - ping.c:391,461 return colon; } =20 - /* from /sys/src/libc/9sys/dial.c */ -=20 - enum + char* + nametoip(char *cs, char *name, int *ipver) { - Maxstring =3D 128, - Maxpath =3D 256, - }; + int n,fd; + char buf[128], ip6[128], ip4[128], *p, *addr, *ip; =20 - typedef struct DS DS; - struct DS { - /* dist string */ - char buf[Maxstring]; - char *netdir; - char *proto; - char *rem; -=20 - /* other args */ - char *local; - char *dir; - int *cfdp; - }; -=20 - /* - * parse a dial string - */ - static void - _dial_string_parse(char *str, DS *ds) - { - char *p, *p2; -=20 - strncpy(ds->buf, str, Maxstring); - ds->buf[Maxstring-1] =3D 0; -=20 - p =3D strchr(ds->buf, '!'); - if(p =3D=3D 0) { - ds->netdir =3D 0; - ds->proto =3D "net"; - ds->rem =3D ds->buf; - } else { - if(*ds->buf !=3D '/' && *ds->buf !=3D '#'){ - ds->netdir =3D 0; - ds->proto =3D ds->buf; - } else { - for(p2 =3D p; *p2 !=3D '/'; p2--) - ; - *p2++ =3D 0; - ds->netdir =3D ds->buf; - ds->proto =3D p2; - } - *p =3D 0; - ds->rem =3D p + 1; + ip6[0] =3D 0; + ip4[0] =3D 0; + if(isdottedquad(name)){ + snprint(ip4, sizeof ip4, "%s", name); + goto match; } - } + if(isv6lit(name)){ + snprint(ip6, sizeof ip6, "%s", name); + goto match; + } =20 - /* end excerpt from /sys/src/libc/9sys/dial.c */ -=20 - /* side effect: sets network & target */ - static int - isv4name(char *name) - { - int r =3D 1; - char *root, *ip, *pr; - DS ds; -=20 - _dial_string_parse(name, &ds); -=20 - /* cope with leading /net.alt/icmp! and the like */ - root =3D nil; - if (ds.netdir !=3D nil) { - pr =3D strrchr(ds.netdir, '/'); - if (pr =3D=3D nil) - pr =3D ds.netdir; - else { - *pr++ =3D '\0'; - root =3D ds.netdir; - network =3D strdup(root); + if(cs =3D=3D nil) + cs =3D "/net/cs"; + fd =3D open(cs, ORDWR); + if(fd < 0) + sysfatal("cannot open %s: %r", cs); + addr =3D smprint("tcp!%s!1", name); + if(write(fd, addr, strlen(addr)) !=3D strlen(addr)){ + close(fd); + sysfatal("cannot write %s to%s: %r", addr, cs); + } + free(addr); + seek(fd, 0, 0); + while((n =3D read(fd, buf, sizeof(buf)-1)) > 0){ + buf[n] =3D 0; + ip =3D strchr(buf,' '); + ip++; + p =3D strchr(ip,'!'); + *p =3D 0; + if(isdottedquad(ip)){ + if(ip4[0] =3D=3D 0) + snprint(ip4, sizeof ip4, "%s", ip); + }else if(isv6lit(ip)){ + if(ip6[0] =3D=3D 0) + snprint(ip6, sizeof ip6, "%s", ip); } - if (strcmp(pr, v4pr.net) =3D=3D 0) - return 1; - if (strcmp(pr, v6pr.net) =3D=3D 0) - return 0; } + close(fd); =20 - /* if it's a literal, it's obvious from syntax which proto it is */ - free(target); - target =3D strdup(ds.rem); - if (isdottedquad(ds.rem)) - return 1; - else if (isv6lit(ds.rem)) - return 0; + match: + if((*ipver =3D=3D 4 || *ipver =3D=3D -1) && ip4[0] !=3D 0){ + *ipver =3D 4; + return strdup(ip4); + }else if((*ipver =3D=3D 6 || *ipver =3D=3D -1) && ip6[0] !=3D 0){ + *ipver =3D 6; + return strdup(ip6); + }else if(*ipver =3D=3D 6 && ip4[0] !=3D 0) + return strdup(ip4); + else if(ip4[0] !=3D 0 || ip6[0] !=3D 0) + werrstr("address %s does not match proto %d", ip4[0]? ip4: ip6, *ipve= r); =20 - /* map name to ip and look at its syntax */ - ip =3D csgetvalue(root, "sys", ds.rem, "ip", nil); - if (ip =3D=3D nil) - ip =3D csgetvalue(root, "dom", ds.rem, "ip", nil); - if (ip =3D=3D nil) - ip =3D csgetvalue(root, "sys", ds.rem, "ipv6", nil); - if (ip =3D=3D nil) - ip =3D csgetvalue(root, "dom", ds.rem, "ipv6", nil); - if (ip !=3D nil) - r =3D isv4name(ip); - free(ip); - return r; + *ipver =3D -1; + return nil; } =20 void main(int argc, char **argv) { - int fd, msglen, interval, nmsg; + int fd, msglen, interval, nmsg, ipver; char *ds; =20 nsecfd =3D bintime(-1, nil, nil, nil); /n/dump/2016/0101/sys/src/cmd/ip/ping.c:509,517 - ping.c:465,475 =20 msglen =3D interval =3D 0; nmsg =3D MAXMSG; + ipver =3D -1; ARGBEGIN { + case '4': case '6': - proto =3D &v6pr; + ipver =3D ARGC() - '0'; break; case 'a': addresses =3D 1; /n/dump/2016/0101/sys/src/cmd/ip/ping.c:568,576 - ping.c:526,537 =20 notify(catch); =20 - if (!isv4name(argv[0])) - proto =3D &v6pr; - ds =3D netmkaddr(argv[0], proto->net, "1"); + target =3D nametoip(nil, argv[0], &ipver); + if(target =3D=3D nil) + sysfatal("%s: nametoip: %r", argv0); + proto =3D ipver=3D=3D4? &v4pr: &v6pr; +=20 + ds =3D netmkaddr(target, proto->net, "1"); fd =3D dial(ds, 0, 0, 0); if(fd < 0){ fprint(2, "%s: couldn't dial %s: %r\n", argv0, ds); /n/dump/2016/0101/sys/src/cmd/ip/ping.c:583,590 - ping.c:544,550 =20 switch(rfork(RFPROC|RFMEM|RFFDG)){ case -1: - fprint(2, "%s: can't fork: %r\n", argv0); - /* fallthrough */ + sysfatal("%s: can't fork: %r\n", argv0); case 0: rcvr(fd, msglen, interval, nmsg); exits(0); --upas-vnmxkrjeolzkfvxvfjtmgejori Content-Disposition: attachment; filename=ping.c Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable /* ping for ip v4 and v6 */ #include #include #include #include #include #include #include "icmp.h" enum { MAXMSG =3D 32, SLEEPMS =3D 1000, SECOND =3D 1000000000LL, MINUTE =3D 60*SECOND, }; typedef struct Req Req; struct Req { ushort seq; /* sequence number */ vlong time; /* time sent */ vlong rtt; int ttl; int replied; Req *next; }; typedef struct { int version; char *net; int echocmd; int echoreply; unsigned iphdrsz; void (*prreply)(Req *r, void *v); void (*prlost)(ushort seq, void *v); } Proto; Req *first; /* request list */ Req *last; /* ... */ Lock listlock; char *argv0; int addresses; int debug; int done; int flood; int lostmsgs; int lostonly; int quiet; int rcvdmsgs; int rint; int nsecfd; ushort firstseq; vlong sum; int waittime =3D 5000; static char *network, *target; void lost(Req*, void*); void reply(Req*, void*); static void usage(void) { fprint(2, "usage: %s [-6alq] [-s msgsize] [-i millisecs] [-n #pings] dest\n", argv0); exits("usage"); } static void catch(void *a, char *msg) { USED(a); if(strstr(msg, "alarm")) noted(NCONT); else if(strstr(msg, "die")) exits("errors"); else noted(NDFLT); } static void prlost4(ushort seq, void *v) { Ip4hdr *ip4 =3D v; print("lost %ud: %V -> %V\n", seq, ip4->src, ip4->dst); } static void prlost6(ushort seq, void *v) { Ip6hdr *ip6 =3D v; print("lost %ud: %I -> %I\n", seq, ip6->src, ip6->dst); } static void prreply4(Req *r, void *v) { Ip4hdr *ip4 =3D v; print("%ud: %V -> %V rtt %lld =C2=B5s, avg rtt %lld =C2=B5s, ttl =3D %d\= n", r->seq - firstseq, ip4->src, ip4->dst, r->rtt, sum/rcvdmsgs, r->ttl); } static void prreply6(Req *r, void *v) { Ip6hdr *ip6 =3D v; print("%ud: %I -> %I rtt %lld =C2=B5s, avg rtt %lld =C2=B5s, ttl =3D %d\= n", r->seq - firstseq, ip6->src, ip6->dst, r->rtt, sum/rcvdmsgs, r->ttl); } static Proto v4pr =3D { 4, "icmp", EchoRequest, EchoReply, IPV4HDR_LEN, prreply4, prlost4, }; static Proto v6pr =3D { 6, "icmpv6", EchoRequestV6, EchoReplyV6, IPV6HDR_LEN, prreply6, prlost6, }; static Proto *proto =3D &v4pr; Icmphdr * geticmp(void *v) { char *p =3D v; return (Icmphdr *)(p + proto->iphdrsz); } void clean(ushort seq, vlong now, void *v) { int ttl; Req **l, *r, *rlost, **rr; ttl =3D 0; if (v) { if (proto->version =3D=3D 4) ttl =3D ((Ip4hdr *)v)->ttl; else ttl =3D ((Ip6hdr *)v)->ttl; } rlost =3D nil; rr =3D &rlost; lock(&listlock); last =3D nil; for(l =3D &first; *l; ){ r =3D *l; if(v && r->seq =3D=3D seq){ r->rtt =3D now-r->time; r->ttl =3D ttl; reply(r, v); } if(now-r->time >=3D MINUTE){ *l =3D r->next; r->next =3D nil; *rr =3D r; rr =3D &(*rr)->next; }else{ last =3D r; l =3D &r->next; } } unlock(&listlock); for(; (r =3D rlost) !=3D nil;){ rlost =3D rlost->next; r->rtt =3D now-r->time; if(v) r->ttl =3D ttl; if(r->replied =3D=3D 0) lost(r, v); free(r); } } static uchar loopbacknet[IPaddrlen] =3D { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 0 }; static uchar loopbackmask[IPaddrlen] =3D { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0 }; /* * find first ip addr suitable for proto and * that isn't the friggin loopback address. * deprecate link-local and multicast addresses. */ static int myipvnaddr(uchar *ip, Proto *proto, char *net) { int ipisv4, wantv4; Ipifc *nifc; Iplifc *lifc; uchar mynet[IPaddrlen], linklocal[IPaddrlen]; static Ipifc *ifc; ipmove(linklocal, IPnoaddr); wantv4 =3D proto->version =3D=3D 4; ifc =3D readipifc(net, ifc, -1); for(nifc =3D ifc; nifc; nifc =3D nifc->next) for(lifc =3D nifc->lifc; lifc; lifc =3D lifc->next){ maskip(lifc->ip, loopbackmask, mynet); if(ipcmp(mynet, loopbacknet) =3D=3D 0) continue; if(ISIPV6MCAST(lifc->ip) || ISIPV6LINKLOCAL(lifc->ip)) { ipmove(linklocal, lifc->ip); continue; } ipisv4 =3D isv4(lifc->ip) !=3D 0; if(ipcmp(lifc->ip, IPnoaddr) !=3D 0 && wantv4 =3D=3D ipisv4){ ipmove(ip, lifc->ip); return 0; } } /* no global unicast addrs found, fall back to link-local, if any */ ipmove(ip, linklocal); return ipcmp(ip, IPnoaddr) =3D=3D 0? -1: 0; } void sender(int fd, int msglen, int interval, int n) { int i, extra; ushort seq; char buf[64*1024+512]; uchar me[IPaddrlen], mev4[IPv4addrlen]; Icmphdr *icmp; Req *r; srand(time(0)); firstseq =3D seq =3D rand(); icmp =3D geticmp(buf); memset(buf, 0, proto->iphdrsz + ICMP_HDRSIZE); for(i =3D proto->iphdrsz + ICMP_HDRSIZE; i < msglen; i++) buf[i] =3D i; icmp->type =3D proto->echocmd; icmp->code =3D 0; /* arguably the kernel should fill in the right src addr. */ myipvnaddr(me, proto, network); if (proto->version =3D=3D 4) { v6tov4(mev4, me); memmove(((Ip4hdr *)buf)->src, mev4, IPv4addrlen); } else ipmove(((Ip6hdr *)buf)->src, me); if (addresses) print("\t%I -> %s\n", me, target); if(rint !=3D 0 && interval <=3D 0) rint =3D 0; extra =3D 0; for(i =3D 0; i < n; i++){ if(i !=3D 0){ if(rint !=3D 0) extra =3D nrand(interval); sleep(interval + extra); } r =3D malloc(sizeof *r); if (r =3D=3D nil) continue; hnputs(icmp->seq, seq); r->seq =3D seq; r->next =3D nil; r->replied =3D 0; // nsecfd =3D bintime(nsecfd, &r->time, nil, nil); /* avoid early free i= n reply! */ r->time =3D 1ull<<62; lock(&listlock); if(first =3D=3D nil) first =3D r; else last->next =3D r; last =3D r; unlock(&listlock); nsecfd =3D bintime(nsecfd, &r->time, nil, nil); if(write(fd, buf, msglen) < msglen){ fprint(2, "%s: write failed: %r\n", argv0); return; } seq++; } done =3D 1; } void rcvr(int fd, int msglen, int interval, int nmsg) { int i, n, munged; ushort x; vlong now; uchar buf[64*1024+512]; Icmphdr *icmp; Req *r; sum =3D 0; while(lostmsgs+rcvdmsgs < nmsg){ alarm((nmsg-lostmsgs-rcvdmsgs)*interval+waittime); // alarm(interval+waittime); n =3D read(fd, buf, sizeof buf); alarm(0); nsecfd =3D bintime(nsecfd, &now, nil, nil); if(n <=3D 0){ /* read interrupted - time to go */ clean(0, now+MINUTE, nil); continue; } if(n < msglen){ print("bad len %d/%d\n", n, msglen); continue; } icmp =3D geticmp(buf); munged =3D 0; for(i =3D proto->iphdrsz + ICMP_HDRSIZE; i < msglen; i++) if(buf[i] !=3D (uchar)i) munged++; if(munged) print("corrupted reply\n"); x =3D nhgets(icmp->seq); if(icmp->type !=3D proto->echoreply || icmp->code !=3D 0) { print("bad type/code/sequence %d/%d/%d (want %d/%d/%d)\n", icmp->type, icmp->code, x, proto->echoreply, 0, x); continue; } clean(x, now, buf); } lock(&listlock); for(r =3D first; r; r =3D r->next) if(r->replied =3D=3D 0) lostmsgs++; unlock(&listlock); if(!quiet && lostmsgs) print("%d out of %d messages lost\n", lostmsgs, lostmsgs+rcvdmsgs); } static int isdottedquad(char *name) { int dot =3D 0, digit =3D 0; for (; *name !=3D '\0'; name++) if (*name =3D=3D '.') dot++; else if (isdigit(*name)) digit++; else return 0; return dot && digit; } static int isv6lit(char *name) { int colon =3D 0, hex =3D 0; for (; *name !=3D '\0'; name++) if (*name =3D=3D ':') colon++; else if (isxdigit(*name)) hex++; else return 0; return colon; } char* nametoip(char *cs, char *name, int *ipver) { int n,fd; char buf[128], ip6[128], ip4[128], *p, *addr, *ip; ip6[0] =3D 0; ip4[0] =3D 0; if(isdottedquad(name)){ snprint(ip4, sizeof ip4, "%s", name); goto match; } if(isv6lit(name)){ snprint(ip6, sizeof ip6, "%s", name); goto match; } if(cs =3D=3D nil) cs =3D "/net/cs"; fd =3D open(cs, ORDWR); if(fd < 0) sysfatal("cannot open %s: %r", cs); addr =3D smprint("tcp!%s!1", name); if(write(fd, addr, strlen(addr)) !=3D strlen(addr)){ close(fd); sysfatal("cannot write %s to%s: %r", addr, cs); } free(addr); seek(fd, 0, 0); while((n =3D read(fd, buf, sizeof(buf)-1)) > 0){ buf[n] =3D 0; ip =3D strchr(buf,' '); ip++; p =3D strchr(ip,'!'); *p =3D 0; if(isdottedquad(ip)){ if(ip4[0] =3D=3D 0) snprint(ip4, sizeof ip4, "%s", ip); }else if(isv6lit(ip)){ if(ip6[0] =3D=3D 0) snprint(ip6, sizeof ip6, "%s", ip); } } close(fd); match: if((*ipver =3D=3D 4 || *ipver =3D=3D -1) && ip4[0] !=3D 0){ *ipver =3D 4; return strdup(ip4); }else if((*ipver =3D=3D 6 || *ipver =3D=3D -1) && ip6[0] !=3D 0){ *ipver =3D 6; return strdup(ip6); }else if(*ipver =3D=3D 6 && ip4[0] !=3D 0) return strdup(ip4); else if(ip4[0] !=3D 0 || ip6[0] !=3D 0) werrstr("address %s does not match proto %d", ip4[0]? ip4: ip6, *ipver)= ; *ipver =3D -1; return nil; } void main(int argc, char **argv) { int fd, msglen, interval, nmsg, ipver; char *ds; nsecfd =3D bintime(-1, nil, nil, nil); fmtinstall('V', eipfmt); fmtinstall('I', eipfmt); msglen =3D interval =3D 0; nmsg =3D MAXMSG; ipver =3D -1; ARGBEGIN { case '4': case '6': ipver =3D ARGC() - '0'; break; case 'a': addresses =3D 1; break; case 'd': debug++; break; case 'f': flood =3D 1; break; case 'i': interval =3D atoi(EARGF(usage())); if(interval < 0) usage(); break; case 'l': lostonly++; break; case 'n': nmsg =3D atoi(EARGF(usage())); if(nmsg < 0) usage(); break; case 'q': quiet =3D 1; break; case 'r': rint =3D 1; break; case 's': msglen =3D atoi(EARGF(usage())); break; case 'w': waittime =3D atoi(EARGF(usage())); if(waittime < 0) usage(); break; default: usage(); break; } ARGEND; if(msglen < proto->iphdrsz + ICMP_HDRSIZE) msglen =3D proto->iphdrsz + ICMP_HDRSIZE; if(msglen < 64) msglen =3D 64; if(msglen >=3D 64*1024) msglen =3D 64*1024-1; if(interval <=3D 0 && !flood) interval =3D SLEEPMS; if(argc < 1) usage(); notify(catch); target =3D nametoip(nil, argv[0], &ipver); if(target =3D=3D nil) sysfatal("%s: nametoip: %r", argv0); proto =3D ipver=3D=3D4? &v4pr: &v6pr; ds =3D netmkaddr(target, proto->net, "1"); fd =3D dial(ds, 0, 0, 0); if(fd < 0){ fprint(2, "%s: couldn't dial %s: %r\n", argv0, ds); exits("dialing"); } if (!quiet) print("sending %d %d byte messages %d ms apart to %s\n", nmsg, msglen, interval, ds); switch(rfork(RFPROC|RFMEM|RFFDG)){ case -1: sysfatal("%s: can't fork: %r\n", argv0); case 0: rcvr(fd, msglen, interval, nmsg); exits(0); default: sender(fd, msglen, interval, nmsg); wait(); exits(lostmsgs ? "lost messages" : ""); } } void reply(Req *r, void *v) { r->rtt /=3D 1000LL; sum +=3D r->rtt; if(!r->replied) rcvdmsgs++; if(!quiet && !lostonly) if(addresses) (*proto->prreply)(r, v); else print("%ud: rtt %lld =C2=B5s, avg rtt %lld =C2=B5s, ttl =3D %d\n", r->seq - firstseq, r->rtt, sum/rcvdmsgs, r->ttl); r->replied =3D 1; } void lost(Req *r, void *v) { if(!quiet) if(addresses && v !=3D nil) (*proto->prlost)(r->seq - firstseq, v); else print("lost %ud\n", r->seq - firstseq); lostmsgs++; } --upas-vnmxkrjeolzkfvxvfjtmgejori--