xref: /plan9/sys/src/cmd/ip/ping.c (revision 79a6d22eb4218e577fe5e07f95f3a4ec0e112f7f)
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