14ac22c89SDavid du Colombier /* ping for ip v4 and v6 */
27dd7cddfSDavid du Colombier #include <u.h>
37dd7cddfSDavid du Colombier #include <libc.h>
45a354e27SDavid du Colombier #include <ctype.h>
53ff48bf5SDavid du Colombier #include <ip.h>
65a354e27SDavid du Colombier #include <bio.h>
75a354e27SDavid du Colombier #include <ndb.h>
85a354e27SDavid du Colombier #include "icmp.h"
97dd7cddfSDavid du Colombier
105a354e27SDavid du Colombier enum {
114ac22c89SDavid du Colombier MAXMSG = 32,
124ac22c89SDavid du Colombier SLEEPMS = 1000,
134ac22c89SDavid du Colombier
144ac22c89SDavid du Colombier SECOND = 1000000000LL,
154ac22c89SDavid du Colombier MINUTE = 60*SECOND,
164ac22c89SDavid du Colombier };
174ac22c89SDavid du Colombier
187dd7cddfSDavid du Colombier typedef struct Req Req;
197dd7cddfSDavid du Colombier struct Req
207dd7cddfSDavid du Colombier {
214ac22c89SDavid du Colombier ushort seq; /* sequence number */
224ac22c89SDavid du Colombier vlong time; /* time sent */
233ff48bf5SDavid du Colombier vlong rtt;
247dd7cddfSDavid du Colombier int ttl;
253ff48bf5SDavid du Colombier int replied;
267dd7cddfSDavid du Colombier Req *next;
277dd7cddfSDavid du Colombier };
284ac22c89SDavid du Colombier
294ac22c89SDavid du Colombier typedef struct {
305e1edbcaSDavid du Colombier int version;
314ac22c89SDavid du Colombier char *net;
325e1edbcaSDavid du Colombier int echocmd;
334ac22c89SDavid du Colombier int echoreply;
345e1edbcaSDavid du Colombier unsigned iphdrsz;
355e1edbcaSDavid du Colombier
364ac22c89SDavid du Colombier void (*prreply)(Req *r, void *v);
374ac22c89SDavid du Colombier void (*prlost)(ushort seq, void *v);
384ac22c89SDavid du Colombier } Proto;
394ac22c89SDavid du Colombier
405e1edbcaSDavid du Colombier
414ac22c89SDavid du Colombier Req *first; /* request list */
424ac22c89SDavid du Colombier Req *last; /* ... */
437dd7cddfSDavid du Colombier Lock listlock;
447dd7cddfSDavid du Colombier
457dd7cddfSDavid du Colombier char *argv0;
465e1edbcaSDavid du Colombier
473ff48bf5SDavid du Colombier int addresses;
485e1edbcaSDavid du Colombier int debug;
495e1edbcaSDavid du Colombier int done;
500b9a5132SDavid du Colombier int flood;
515e1edbcaSDavid du Colombier int lostmsgs;
525e1edbcaSDavid du Colombier int lostonly;
535e1edbcaSDavid du Colombier int quiet;
545e1edbcaSDavid du Colombier int rcvdmsgs;
555e1edbcaSDavid du Colombier int rint;
565e1edbcaSDavid du Colombier ushort firstseq;
575e1edbcaSDavid du Colombier vlong sum;
583b86f2f8SDavid du Colombier int waittime = 5000;
595e1edbcaSDavid du Colombier
605e1edbcaSDavid du Colombier static char *network, *target;
617dd7cddfSDavid du Colombier
624ac22c89SDavid du Colombier void lost(Req*, void*);
634ac22c89SDavid du Colombier void reply(Req*, void*);
647dd7cddfSDavid du Colombier
654ac22c89SDavid du Colombier static void
usage(void)664ac22c89SDavid du Colombier usage(void)
674ac22c89SDavid du Colombier {
684ac22c89SDavid du Colombier fprint(2,
694ac22c89SDavid du Colombier "usage: %s [-6alq] [-s msgsize] [-i millisecs] [-n #pings] dest\n",
704ac22c89SDavid du Colombier argv0);
714ac22c89SDavid du Colombier exits("usage");
724ac22c89SDavid du Colombier }
737dd7cddfSDavid du Colombier
747dd7cddfSDavid du Colombier static void
catch(void * a,char * msg)757dd7cddfSDavid du Colombier catch(void *a, char *msg)
767dd7cddfSDavid du Colombier {
777dd7cddfSDavid du Colombier USED(a);
787dd7cddfSDavid du Colombier if(strstr(msg, "alarm"))
797dd7cddfSDavid du Colombier noted(NCONT);
808aafde0cSDavid du Colombier else if(strstr(msg, "die"))
818aafde0cSDavid du Colombier exits("errors");
827dd7cddfSDavid du Colombier else
837dd7cddfSDavid du Colombier noted(NDFLT);
847dd7cddfSDavid du Colombier }
857dd7cddfSDavid du Colombier
864ac22c89SDavid du Colombier static void
prlost4(ushort seq,void * v)874ac22c89SDavid du Colombier prlost4(ushort seq, void *v)
884ac22c89SDavid du Colombier {
8994aa1c4cSDavid du Colombier Ip4hdr *ip4 = v;
904ac22c89SDavid du Colombier
914ac22c89SDavid du Colombier print("lost %ud: %V -> %V\n", seq, ip4->src, ip4->dst);
924ac22c89SDavid du Colombier }
934ac22c89SDavid du Colombier
944ac22c89SDavid du Colombier static void
prlost6(ushort seq,void * v)954ac22c89SDavid du Colombier prlost6(ushort seq, void *v)
964ac22c89SDavid du Colombier {
9794aa1c4cSDavid du Colombier Ip6hdr *ip6 = v;
984ac22c89SDavid du Colombier
994ac22c89SDavid du Colombier print("lost %ud: %I -> %I\n", seq, ip6->src, ip6->dst);
1004ac22c89SDavid du Colombier }
1014ac22c89SDavid du Colombier
1024ac22c89SDavid du Colombier static void
prreply4(Req * r,void * v)1034ac22c89SDavid du Colombier prreply4(Req *r, void *v)
1044ac22c89SDavid du Colombier {
10594aa1c4cSDavid du Colombier Ip4hdr *ip4 = v;
1064ac22c89SDavid du Colombier
1074ac22c89SDavid du Colombier print("%ud: %V -> %V rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
1084ac22c89SDavid du Colombier r->seq - firstseq, ip4->src, ip4->dst, r->rtt, sum/rcvdmsgs,
1094ac22c89SDavid du Colombier r->ttl);
1104ac22c89SDavid du Colombier }
1114ac22c89SDavid du Colombier
1124ac22c89SDavid du Colombier static void
prreply6(Req * r,void * v)1134ac22c89SDavid du Colombier prreply6(Req *r, void *v)
1144ac22c89SDavid du Colombier {
11594aa1c4cSDavid du Colombier Ip6hdr *ip6 = v;
1164ac22c89SDavid du Colombier
1174ac22c89SDavid du Colombier print("%ud: %I -> %I rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
1184ac22c89SDavid du Colombier r->seq - firstseq, ip6->src, ip6->dst, r->rtt, sum/rcvdmsgs,
1194ac22c89SDavid du Colombier r->ttl);
1204ac22c89SDavid du Colombier }
1214ac22c89SDavid du Colombier
1224ac22c89SDavid du Colombier static Proto v4pr = {
1235e1edbcaSDavid du Colombier 4, "icmp",
1245e1edbcaSDavid du Colombier EchoRequest, EchoReply,
1255e1edbcaSDavid du Colombier IPV4HDR_LEN,
1265e1edbcaSDavid du Colombier prreply4, prlost4,
1274ac22c89SDavid du Colombier };
1284ac22c89SDavid du Colombier static Proto v6pr = {
1295e1edbcaSDavid du Colombier 6, "icmpv6",
1305e1edbcaSDavid du Colombier EchoRequestV6, EchoReplyV6,
1315e1edbcaSDavid du Colombier IPV6HDR_LEN,
1325e1edbcaSDavid du Colombier prreply6, prlost6,
1334ac22c89SDavid du Colombier };
1344ac22c89SDavid du Colombier
1354ac22c89SDavid du Colombier static Proto *proto = &v4pr;
1364ac22c89SDavid du Colombier
1375a354e27SDavid du Colombier
1385e1edbcaSDavid du Colombier Icmphdr *
geticmp(void * v)1395e1edbcaSDavid du Colombier geticmp(void *v)
1405e1edbcaSDavid du Colombier {
1415e1edbcaSDavid du Colombier char *p = v;
1425e1edbcaSDavid du Colombier
14394aa1c4cSDavid du Colombier return (Icmphdr *)(p + proto->iphdrsz);
1445e1edbcaSDavid du Colombier }
1455e1edbcaSDavid du Colombier
1467dd7cddfSDavid du Colombier void
clean(ushort seq,vlong now,void * v)1474ac22c89SDavid du Colombier clean(ushort seq, vlong now, void *v)
1487dd7cddfSDavid du Colombier {
1495e1edbcaSDavid du Colombier int ttl;
1507dd7cddfSDavid du Colombier Req **l, *r;
1517dd7cddfSDavid du Colombier
1525e1edbcaSDavid du Colombier ttl = 0;
15394aa1c4cSDavid du Colombier if (v) {
15494aa1c4cSDavid du Colombier if (proto->version == 4)
15594aa1c4cSDavid du Colombier ttl = ((Ip4hdr *)v)->ttl;
15694aa1c4cSDavid du Colombier else
15794aa1c4cSDavid du Colombier ttl = ((Ip6hdr *)v)->ttl;
15894aa1c4cSDavid du Colombier }
1597dd7cddfSDavid du Colombier lock(&listlock);
1600b9a5132SDavid du Colombier last = nil;
1617dd7cddfSDavid du Colombier for(l = &first; *l; ){
1627dd7cddfSDavid du Colombier r = *l;
1633ff48bf5SDavid du Colombier
1644ac22c89SDavid du Colombier if(v && r->seq == seq){
1653ff48bf5SDavid du Colombier r->rtt = now-r->time;
1665e1edbcaSDavid du Colombier r->ttl = ttl;
1674ac22c89SDavid du Colombier reply(r, v);
1683ff48bf5SDavid du Colombier }
1693ff48bf5SDavid du Colombier
1703ff48bf5SDavid du Colombier if(now-r->time > MINUTE){
1717dd7cddfSDavid du Colombier *l = r->next;
1723ff48bf5SDavid du Colombier r->rtt = now-r->time;
1734ac22c89SDavid du Colombier if(v)
1745e1edbcaSDavid du Colombier r->ttl = ttl;
1753ff48bf5SDavid du Colombier if(r->replied == 0)
1764ac22c89SDavid du Colombier lost(r, v);
1777dd7cddfSDavid du Colombier free(r);
17859cc4ca5SDavid du Colombier }else{
17959cc4ca5SDavid du Colombier last = r;
1804ac22c89SDavid du Colombier l = &r->next;
1817dd7cddfSDavid du Colombier }
18259cc4ca5SDavid du Colombier }
1837dd7cddfSDavid du Colombier unlock(&listlock);
1847dd7cddfSDavid du Colombier }
1857dd7cddfSDavid du Colombier
1865e1edbcaSDavid du Colombier static uchar loopbacknet[IPaddrlen] = {
1875e1edbcaSDavid du Colombier 0, 0, 0, 0,
1885e1edbcaSDavid du Colombier 0, 0, 0, 0,
1895e1edbcaSDavid du Colombier 0, 0, 0xff, 0xff,
1905e1edbcaSDavid du Colombier 127, 0, 0, 0
1915e1edbcaSDavid du Colombier };
1925e1edbcaSDavid du Colombier static uchar loopbackmask[IPaddrlen] = {
1935e1edbcaSDavid du Colombier 0xff, 0xff, 0xff, 0xff,
1945e1edbcaSDavid du Colombier 0xff, 0xff, 0xff, 0xff,
1955e1edbcaSDavid du Colombier 0xff, 0xff, 0xff, 0xff,
1965e1edbcaSDavid du Colombier 0xff, 0, 0, 0
1975e1edbcaSDavid du Colombier };
1985e1edbcaSDavid du Colombier
1995e1edbcaSDavid du Colombier /*
2005e1edbcaSDavid du Colombier * find first ip addr suitable for proto and
20182cd249fSDavid du Colombier * that isn't the friggin loopback address.
20282cd249fSDavid du Colombier * deprecate link-local and multicast addresses.
2035e1edbcaSDavid du Colombier */
2045e1edbcaSDavid du Colombier static int
myipvnaddr(uchar * ip,Proto * proto,char * net)2055e1edbcaSDavid du Colombier myipvnaddr(uchar *ip, Proto *proto, char *net)
2065e1edbcaSDavid du Colombier {
2075e1edbcaSDavid du Colombier int ipisv4, wantv4;
2085e1edbcaSDavid du Colombier Ipifc *nifc;
2095e1edbcaSDavid du Colombier Iplifc *lifc;
21082cd249fSDavid du Colombier uchar mynet[IPaddrlen], linklocal[IPaddrlen];
2115e1edbcaSDavid du Colombier static Ipifc *ifc;
2125e1edbcaSDavid du Colombier
21382cd249fSDavid du Colombier ipmove(linklocal, IPnoaddr);
2145e1edbcaSDavid du Colombier wantv4 = proto->version == 4;
2155e1edbcaSDavid du Colombier ifc = readipifc(net, ifc, -1);
2165e1edbcaSDavid du Colombier for(nifc = ifc; nifc; nifc = nifc->next)
2175e1edbcaSDavid du Colombier for(lifc = nifc->lifc; lifc; lifc = lifc->next){
2185e1edbcaSDavid du Colombier maskip(lifc->ip, loopbackmask, mynet);
2195e1edbcaSDavid du Colombier if(ipcmp(mynet, loopbacknet) == 0)
2205e1edbcaSDavid du Colombier continue;
22182cd249fSDavid du Colombier if(ISIPV6MCAST(lifc->ip) || ISIPV6LINKLOCAL(lifc->ip)) {
22282cd249fSDavid du Colombier ipmove(linklocal, lifc->ip);
22382cd249fSDavid du Colombier continue;
22482cd249fSDavid du Colombier }
2255e1edbcaSDavid du Colombier ipisv4 = isv4(lifc->ip) != 0;
2265e1edbcaSDavid du Colombier if(ipcmp(lifc->ip, IPnoaddr) != 0 && wantv4 == ipisv4){
2275e1edbcaSDavid du Colombier ipmove(ip, lifc->ip);
2285e1edbcaSDavid du Colombier return 0;
2295e1edbcaSDavid du Colombier }
2305e1edbcaSDavid du Colombier }
23182cd249fSDavid du Colombier /* no global unicast addrs found, fall back to link-local, if any */
23282cd249fSDavid du Colombier ipmove(ip, linklocal);
23382cd249fSDavid du Colombier return ipcmp(ip, IPnoaddr) == 0? -1: 0;
2345e1edbcaSDavid du Colombier }
2355e1edbcaSDavid du Colombier
2367dd7cddfSDavid du Colombier void
sender(int fd,int msglen,int interval,int n)2377dd7cddfSDavid du Colombier sender(int fd, int msglen, int interval, int n)
2387dd7cddfSDavid du Colombier {
239da51d93aSDavid du Colombier int i, extra;
2407dd7cddfSDavid du Colombier ushort seq;
2414ac22c89SDavid du Colombier char buf[64*1024+512];
2425e1edbcaSDavid du Colombier uchar me[IPaddrlen], mev4[IPv4addrlen];
2435e1edbcaSDavid du Colombier Icmphdr *icmp;
2444ac22c89SDavid du Colombier Req *r;
2457dd7cddfSDavid du Colombier
2467dd7cddfSDavid du Colombier srand(time(0));
2473ff48bf5SDavid du Colombier firstseq = seq = rand();
2487dd7cddfSDavid du Colombier
2495e1edbcaSDavid du Colombier icmp = geticmp(buf);
2505e1edbcaSDavid du Colombier memset(buf, 0, proto->iphdrsz + ICMP_HDRSIZE);
2515e1edbcaSDavid du Colombier for(i = proto->iphdrsz + ICMP_HDRSIZE; i < msglen; i++)
2529a747e4fSDavid du Colombier buf[i] = i;
2535e1edbcaSDavid du Colombier icmp->type = proto->echocmd;
2545e1edbcaSDavid du Colombier icmp->code = 0;
2555e1edbcaSDavid du Colombier
2565e1edbcaSDavid du Colombier /* arguably the kernel should fill in the right src addr. */
2575e1edbcaSDavid du Colombier myipvnaddr(me, proto, network);
2585e1edbcaSDavid du Colombier if (proto->version == 4) {
2595e1edbcaSDavid du Colombier v6tov4(mev4, me);
26094aa1c4cSDavid du Colombier memmove(((Ip4hdr *)buf)->src, mev4, IPv4addrlen);
2615e1edbcaSDavid du Colombier } else
2625e1edbcaSDavid du Colombier ipmove(((Ip6hdr *)buf)->src, me);
2635e1edbcaSDavid du Colombier if (addresses)
2645e1edbcaSDavid du Colombier print("\t%I -> %s\n", me, target);
2657dd7cddfSDavid du Colombier
266*79a6d22eSDavid du Colombier if(rint != 0 && interval <= 0)
267*79a6d22eSDavid du Colombier rint = 0;
268*79a6d22eSDavid du Colombier extra = 0;
2697dd7cddfSDavid du Colombier for(i = 0; i < n; i++){
270da51d93aSDavid du Colombier if(i != 0){
271*79a6d22eSDavid du Colombier if(rint != 0)
272*79a6d22eSDavid du Colombier extra = nrand(interval);
273da51d93aSDavid du Colombier sleep(interval + extra);
274da51d93aSDavid du Colombier }
2757dd7cddfSDavid du Colombier r = malloc(sizeof *r);
2764ac22c89SDavid du Colombier if (r == nil)
2774ac22c89SDavid du Colombier continue;
2785e1edbcaSDavid du Colombier hnputs(icmp->seq, seq);
2797dd7cddfSDavid du Colombier r->seq = seq;
2807dd7cddfSDavid du Colombier r->next = nil;
2810b9a5132SDavid du Colombier r->replied = 0;
2820b9a5132SDavid du Colombier r->time = nsec(); /* avoid early free in reply! */
2837dd7cddfSDavid du Colombier lock(&listlock);
2847dd7cddfSDavid du Colombier if(first == nil)
2857dd7cddfSDavid du Colombier first = r;
2867dd7cddfSDavid du Colombier else
2877dd7cddfSDavid du Colombier last->next = r;
2887dd7cddfSDavid du Colombier last = r;
2897dd7cddfSDavid du Colombier unlock(&listlock);
2907dd7cddfSDavid du Colombier r->time = nsec();
2914ac22c89SDavid du Colombier if(write(fd, buf, msglen) < msglen){
2927dd7cddfSDavid du Colombier fprint(2, "%s: write failed: %r\n", argv0);
2937dd7cddfSDavid du Colombier return;
2947dd7cddfSDavid du Colombier }
2957dd7cddfSDavid du Colombier seq++;
2967dd7cddfSDavid du Colombier }
2977dd7cddfSDavid du Colombier done = 1;
2987dd7cddfSDavid du Colombier }
2997dd7cddfSDavid du Colombier
3007dd7cddfSDavid du Colombier void
rcvr(int fd,int msglen,int interval,int nmsg)301fbfc9db9SDavid du Colombier rcvr(int fd, int msglen, int interval, int nmsg)
3027dd7cddfSDavid du Colombier {
3037dd7cddfSDavid du Colombier int i, n, munged;
3044ac22c89SDavid du Colombier ushort x;
3057dd7cddfSDavid du Colombier vlong now;
3064ac22c89SDavid du Colombier uchar buf[64*1024+512];
3075e1edbcaSDavid du Colombier Icmphdr *icmp;
30859cc4ca5SDavid du Colombier Req *r;
3097dd7cddfSDavid du Colombier
3107dd7cddfSDavid du Colombier sum = 0;
3118aafde0cSDavid du Colombier while(lostmsgs+rcvdmsgs < nmsg){
3123b86f2f8SDavid du Colombier alarm((nmsg-lostmsgs-rcvdmsgs)*interval+waittime);
3134ac22c89SDavid du Colombier n = read(fd, buf, sizeof buf);
3147dd7cddfSDavid du Colombier alarm(0);
3157dd7cddfSDavid du Colombier now = nsec();
3168aafde0cSDavid du Colombier if(n <= 0){ /* read interrupted - time to go */
3178aafde0cSDavid du Colombier clean(0, now+MINUTE, nil);
3188aafde0cSDavid du Colombier continue;
31900c1ae4bSDavid du Colombier }
3207dd7cddfSDavid du Colombier if(n < msglen){
3217dd7cddfSDavid du Colombier print("bad len %d/%d\n", n, msglen);
3227dd7cddfSDavid du Colombier continue;
3237dd7cddfSDavid du Colombier }
3245e1edbcaSDavid du Colombier icmp = geticmp(buf);
32580ee5cbfSDavid du Colombier munged = 0;
3265e1edbcaSDavid du Colombier for(i = proto->iphdrsz + ICMP_HDRSIZE; i < msglen; i++)
3274ac22c89SDavid du Colombier if(buf[i] != (uchar)i)
32880ee5cbfSDavid du Colombier munged++;
32980ee5cbfSDavid du Colombier if(munged)
3304ac22c89SDavid du Colombier print("corrupted reply\n");
3315e1edbcaSDavid du Colombier x = nhgets(icmp->seq);
3325e1edbcaSDavid du Colombier if(icmp->type != proto->echoreply || icmp->code != 0) {
3335e1edbcaSDavid du Colombier print("bad type/code/sequence %d/%d/%d (want %d/%d/%d)\n",
3345e1edbcaSDavid du Colombier icmp->type, icmp->code, x,
3355e1edbcaSDavid du Colombier proto->echoreply, 0, x);
3367dd7cddfSDavid du Colombier continue;
3377dd7cddfSDavid du Colombier }
3384ac22c89SDavid du Colombier clean(x, now, buf);
3397dd7cddfSDavid du Colombier }
34059cc4ca5SDavid du Colombier
34159cc4ca5SDavid du Colombier lock(&listlock);
34259cc4ca5SDavid du Colombier for(r = first; r; r = r->next)
3433ff48bf5SDavid du Colombier if(r->replied == 0)
34459cc4ca5SDavid du Colombier lostmsgs++;
34559cc4ca5SDavid du Colombier unlock(&listlock);
34659cc4ca5SDavid du Colombier
34776783259SDavid du Colombier if(!quiet && lostmsgs)
3484ac22c89SDavid du Colombier print("%d out of %d messages lost\n", lostmsgs,
3494ac22c89SDavid du Colombier lostmsgs+rcvdmsgs);
3507dd7cddfSDavid du Colombier }
3517dd7cddfSDavid du Colombier
3525a354e27SDavid du Colombier static int
isdottedquad(char * name)3535a354e27SDavid du Colombier isdottedquad(char *name)
3545a354e27SDavid du Colombier {
3555a354e27SDavid du Colombier int dot = 0, digit = 0;
3565a354e27SDavid du Colombier
3575a354e27SDavid du Colombier for (; *name != '\0'; name++)
3585a354e27SDavid du Colombier if (*name == '.')
3595a354e27SDavid du Colombier dot++;
3605a354e27SDavid du Colombier else if (isdigit(*name))
3615a354e27SDavid du Colombier digit++;
3625a354e27SDavid du Colombier else
3635a354e27SDavid du Colombier return 0;
3645a354e27SDavid du Colombier return dot && digit;
3655a354e27SDavid du Colombier }
3665a354e27SDavid du Colombier
3675a354e27SDavid du Colombier static int
isv6lit(char * name)3685a354e27SDavid du Colombier isv6lit(char *name)
3695a354e27SDavid du Colombier {
3705a354e27SDavid du Colombier int colon = 0, hex = 0;
3715a354e27SDavid du Colombier
3725a354e27SDavid du Colombier for (; *name != '\0'; name++)
3735a354e27SDavid du Colombier if (*name == ':')
3745a354e27SDavid du Colombier colon++;
3755a354e27SDavid du Colombier else if (isxdigit(*name))
3765a354e27SDavid du Colombier hex++;
3775a354e27SDavid du Colombier else
3785a354e27SDavid du Colombier return 0;
3795a354e27SDavid du Colombier return colon;
3805a354e27SDavid du Colombier }
3815a354e27SDavid du Colombier
3825a354e27SDavid du Colombier /* from /sys/src/libc/9sys/dial.c */
3835a354e27SDavid du Colombier
3845a354e27SDavid du Colombier enum
3855a354e27SDavid du Colombier {
3865a354e27SDavid du Colombier Maxstring = 128,
3875a354e27SDavid du Colombier Maxpath = 256,
3885a354e27SDavid du Colombier };
3895a354e27SDavid du Colombier
3905a354e27SDavid du Colombier typedef struct DS DS;
3915a354e27SDavid du Colombier struct DS {
3925a354e27SDavid du Colombier /* dist string */
3935a354e27SDavid du Colombier char buf[Maxstring];
3945a354e27SDavid du Colombier char *netdir;
3955a354e27SDavid du Colombier char *proto;
3965a354e27SDavid du Colombier char *rem;
3975a354e27SDavid du Colombier
3985a354e27SDavid du Colombier /* other args */
3995a354e27SDavid du Colombier char *local;
4005a354e27SDavid du Colombier char *dir;
4015a354e27SDavid du Colombier int *cfdp;
4025a354e27SDavid du Colombier };
4035a354e27SDavid du Colombier
4045a354e27SDavid du Colombier /*
4055a354e27SDavid du Colombier * parse a dial string
4065a354e27SDavid du Colombier */
4075a354e27SDavid du Colombier static void
_dial_string_parse(char * str,DS * ds)4085a354e27SDavid du Colombier _dial_string_parse(char *str, DS *ds)
4095a354e27SDavid du Colombier {
4105a354e27SDavid du Colombier char *p, *p2;
4115a354e27SDavid du Colombier
4125a354e27SDavid du Colombier strncpy(ds->buf, str, Maxstring);
4135a354e27SDavid du Colombier ds->buf[Maxstring-1] = 0;
4145a354e27SDavid du Colombier
4155a354e27SDavid du Colombier p = strchr(ds->buf, '!');
4165a354e27SDavid du Colombier if(p == 0) {
4175a354e27SDavid du Colombier ds->netdir = 0;
4185a354e27SDavid du Colombier ds->proto = "net";
4195a354e27SDavid du Colombier ds->rem = ds->buf;
4205a354e27SDavid du Colombier } else {
4215a354e27SDavid du Colombier if(*ds->buf != '/' && *ds->buf != '#'){
4225a354e27SDavid du Colombier ds->netdir = 0;
4235a354e27SDavid du Colombier ds->proto = ds->buf;
4245a354e27SDavid du Colombier } else {
4255a354e27SDavid du Colombier for(p2 = p; *p2 != '/'; p2--)
4265a354e27SDavid du Colombier ;
4275a354e27SDavid du Colombier *p2++ = 0;
4285a354e27SDavid du Colombier ds->netdir = ds->buf;
4295a354e27SDavid du Colombier ds->proto = p2;
4305a354e27SDavid du Colombier }
4315a354e27SDavid du Colombier *p = 0;
4325a354e27SDavid du Colombier ds->rem = p + 1;
4335a354e27SDavid du Colombier }
4345a354e27SDavid du Colombier }
4355a354e27SDavid du Colombier
4365e1edbcaSDavid du Colombier /* end excerpt from /sys/src/libc/9sys/dial.c */
4375e1edbcaSDavid du Colombier
4385e1edbcaSDavid du Colombier /* side effect: sets network & target */
4395a354e27SDavid du Colombier static int
isv4name(char * name)4405a354e27SDavid du Colombier isv4name(char *name)
4415a354e27SDavid du Colombier {
4425a354e27SDavid du Colombier int r = 1;
4435a354e27SDavid du Colombier char *root, *ip, *pr;
4445a354e27SDavid du Colombier DS ds;
4455a354e27SDavid du Colombier
4465a354e27SDavid du Colombier _dial_string_parse(name, &ds);
4475a354e27SDavid du Colombier
4485a354e27SDavid du Colombier /* cope with leading /net.alt/icmp! and the like */
4495a354e27SDavid du Colombier root = nil;
4505a354e27SDavid du Colombier if (ds.netdir != nil) {
4515a354e27SDavid du Colombier pr = strrchr(ds.netdir, '/');
4525a354e27SDavid du Colombier if (pr == nil)
4535a354e27SDavid du Colombier pr = ds.netdir;
4545a354e27SDavid du Colombier else {
4555a354e27SDavid du Colombier *pr++ = '\0';
4565a354e27SDavid du Colombier root = ds.netdir;
4575e1edbcaSDavid du Colombier network = strdup(root);
4585a354e27SDavid du Colombier }
4595a354e27SDavid du Colombier if (strcmp(pr, v4pr.net) == 0)
4605a354e27SDavid du Colombier return 1;
4615a354e27SDavid du Colombier if (strcmp(pr, v6pr.net) == 0)
4625a354e27SDavid du Colombier return 0;
4635a354e27SDavid du Colombier }
4645a354e27SDavid du Colombier
4655a354e27SDavid du Colombier /* if it's a literal, it's obvious from syntax which proto it is */
4665e1edbcaSDavid du Colombier free(target);
4675e1edbcaSDavid du Colombier target = strdup(ds.rem);
4685a354e27SDavid du Colombier if (isdottedquad(ds.rem))
4695a354e27SDavid du Colombier return 1;
4705a354e27SDavid du Colombier else if (isv6lit(ds.rem))
4715a354e27SDavid du Colombier return 0;
4725a354e27SDavid du Colombier
4735a354e27SDavid du Colombier /* map name to ip and look at its syntax */
4745a354e27SDavid du Colombier ip = csgetvalue(root, "sys", ds.rem, "ip", nil);
4755a354e27SDavid du Colombier if (ip == nil)
4765a354e27SDavid du Colombier ip = csgetvalue(root, "dom", ds.rem, "ip", nil);
4775a354e27SDavid du Colombier if (ip == nil)
4785a354e27SDavid du Colombier ip = csgetvalue(root, "sys", ds.rem, "ipv6", nil);
4795a354e27SDavid du Colombier if (ip == nil)
4805a354e27SDavid du Colombier ip = csgetvalue(root, "dom", ds.rem, "ipv6", nil);
4815a354e27SDavid du Colombier if (ip != nil)
4825a354e27SDavid du Colombier r = isv4name(ip);
4835a354e27SDavid du Colombier free(ip);
4845a354e27SDavid du Colombier return r;
4855a354e27SDavid du Colombier }
4865a354e27SDavid du Colombier
4877dd7cddfSDavid du Colombier void
main(int argc,char ** argv)4887dd7cddfSDavid du Colombier main(int argc, char **argv)
4897dd7cddfSDavid du Colombier {
4904ac22c89SDavid du Colombier int fd, msglen, interval, nmsg;
4914ac22c89SDavid du Colombier char *ds;
4927dd7cddfSDavid du Colombier
4937dd7cddfSDavid du Colombier nsec(); /* make sure time file is already open */
4947dd7cddfSDavid du Colombier
4953ff48bf5SDavid du Colombier fmtinstall('V', eipfmt);
4965a354e27SDavid du Colombier fmtinstall('I', eipfmt);
4973ff48bf5SDavid du Colombier
4987dd7cddfSDavid du Colombier msglen = interval = 0;
4997dd7cddfSDavid du Colombier nmsg = MAXMSG;
5007dd7cddfSDavid du Colombier ARGBEGIN {
5014ac22c89SDavid du Colombier case '6':
5024ac22c89SDavid du Colombier proto = &v6pr;
5034ac22c89SDavid du Colombier break;
5044ac22c89SDavid du Colombier case 'a':
5054ac22c89SDavid du Colombier addresses = 1;
50600c1ae4bSDavid du Colombier break;
5077dd7cddfSDavid du Colombier case 'd':
5087dd7cddfSDavid du Colombier debug++;
5097dd7cddfSDavid du Colombier break;
5104ac22c89SDavid du Colombier case 'f':
5114ac22c89SDavid du Colombier flood = 1;
5127dd7cddfSDavid du Colombier break;
5137dd7cddfSDavid du Colombier case 'i':
5144ac22c89SDavid du Colombier interval = atoi(EARGF(usage()));
515*79a6d22eSDavid du Colombier if(interval < 0)
516*79a6d22eSDavid du Colombier usage();
5174ac22c89SDavid du Colombier break;
5184ac22c89SDavid du Colombier case 'l':
5194ac22c89SDavid du Colombier lostonly++;
5207dd7cddfSDavid du Colombier break;
5217dd7cddfSDavid du Colombier case 'n':
5224ac22c89SDavid du Colombier nmsg = atoi(EARGF(usage()));
523*79a6d22eSDavid du Colombier if(nmsg < 0)
524*79a6d22eSDavid du Colombier usage();
5253ff48bf5SDavid du Colombier break;
5267dd7cddfSDavid du Colombier case 'q':
5277dd7cddfSDavid du Colombier quiet = 1;
5287dd7cddfSDavid du Colombier break;
529da51d93aSDavid du Colombier case 'r':
530da51d93aSDavid du Colombier rint = 1;
531da51d93aSDavid du Colombier break;
5324ac22c89SDavid du Colombier case 's':
5334ac22c89SDavid du Colombier msglen = atoi(EARGF(usage()));
5344ac22c89SDavid du Colombier break;
5353b86f2f8SDavid du Colombier case 'w':
5363b86f2f8SDavid du Colombier waittime = atoi(EARGF(usage()));
537*79a6d22eSDavid du Colombier if(waittime < 0)
538*79a6d22eSDavid du Colombier usage();
5393b86f2f8SDavid du Colombier break;
5404ac22c89SDavid du Colombier default:
5414ac22c89SDavid du Colombier usage();
5420b9a5132SDavid du Colombier break;
5437dd7cddfSDavid du Colombier } ARGEND;
5444ac22c89SDavid du Colombier
5455e1edbcaSDavid du Colombier if(msglen < proto->iphdrsz + ICMP_HDRSIZE)
5465e1edbcaSDavid du Colombier msglen = proto->iphdrsz + ICMP_HDRSIZE;
5474ac22c89SDavid du Colombier if(msglen < 64)
5487dd7cddfSDavid du Colombier msglen = 64;
549ccaac148SDavid du Colombier if(msglen >= 64*1024)
550ccaac148SDavid du Colombier msglen = 64*1024-1;
5510b9a5132SDavid du Colombier if(interval <= 0 && !flood)
5527dd7cddfSDavid du Colombier interval = SLEEPMS;
5537dd7cddfSDavid du Colombier
5547dd7cddfSDavid du Colombier if(argc < 1)
5557dd7cddfSDavid du Colombier usage();
5567dd7cddfSDavid du Colombier
5577dd7cddfSDavid du Colombier notify(catch);
5587dd7cddfSDavid du Colombier
5595a354e27SDavid du Colombier if (!isv4name(argv[0]))
5605a354e27SDavid du Colombier proto = &v6pr;
5614ac22c89SDavid du Colombier ds = netmkaddr(argv[0], proto->net, "1");
5624ac22c89SDavid du Colombier fd = dial(ds, 0, 0, 0);
5637dd7cddfSDavid du Colombier if(fd < 0){
5644ac22c89SDavid du Colombier fprint(2, "%s: couldn't dial %s: %r\n", argv0, ds);
5657dd7cddfSDavid du Colombier exits("dialing");
5667dd7cddfSDavid du Colombier }
5677dd7cddfSDavid du Colombier
56876783259SDavid du Colombier if (!quiet)
5694ac22c89SDavid du Colombier print("sending %d %d byte messages %d ms apart to %s\n",
5704ac22c89SDavid du Colombier nmsg, msglen, interval, ds);
5717dd7cddfSDavid du Colombier
5727dd7cddfSDavid du Colombier switch(rfork(RFPROC|RFMEM|RFFDG)){
5737dd7cddfSDavid du Colombier case -1:
5747dd7cddfSDavid du Colombier fprint(2, "%s: can't fork: %r\n", argv0);
5754ac22c89SDavid du Colombier /* fallthrough */
5767dd7cddfSDavid du Colombier case 0:
577fbfc9db9SDavid du Colombier rcvr(fd, msglen, interval, nmsg);
5787dd7cddfSDavid du Colombier exits(0);
5797dd7cddfSDavid du Colombier default:
5807dd7cddfSDavid du Colombier sender(fd, msglen, interval, nmsg);
5819a747e4fSDavid du Colombier wait();
582443f8232SDavid du Colombier exits(lostmsgs ? "lost messages" : "");
5837dd7cddfSDavid du Colombier }
5847dd7cddfSDavid du Colombier }
5857dd7cddfSDavid du Colombier
5867dd7cddfSDavid du Colombier void
reply(Req * r,void * v)5874ac22c89SDavid du Colombier reply(Req *r, void *v)
5887dd7cddfSDavid du Colombier {
5893ff48bf5SDavid du Colombier r->rtt /= 1000LL;
5903ff48bf5SDavid du Colombier sum += r->rtt;
5918aafde0cSDavid du Colombier if(!r->replied)
5928aafde0cSDavid du Colombier rcvdmsgs++;
5934ac22c89SDavid du Colombier if(!quiet && !lostonly)
5943ff48bf5SDavid du Colombier if(addresses)
5954ac22c89SDavid du Colombier (*proto->prreply)(r, v);
5963ff48bf5SDavid du Colombier else
5973ff48bf5SDavid du Colombier print("%ud: rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
5984ac22c89SDavid du Colombier r->seq - firstseq, r->rtt, sum/rcvdmsgs, r->ttl);
5993ff48bf5SDavid du Colombier r->replied = 1;
6007dd7cddfSDavid du Colombier }
60100c1ae4bSDavid du Colombier
60200c1ae4bSDavid du Colombier void
lost(Req * r,void * v)6034ac22c89SDavid du Colombier lost(Req *r, void *v)
60400c1ae4bSDavid du Colombier {
6054ac22c89SDavid du Colombier if(!quiet)
6064ac22c89SDavid du Colombier if(addresses && v != nil)
6074ac22c89SDavid du Colombier (*proto->prlost)(r->seq - firstseq, v);
60800c1ae4bSDavid du Colombier else
6098aafde0cSDavid du Colombier print("lost %ud\n", r->seq - firstseq);
61000c1ae4bSDavid du Colombier lostmsgs++;
61100c1ae4bSDavid du Colombier }
612