xref: /plan9/sys/src/cmd/ip/ping.c (revision fbfc9db9f99ea2f256a29366a90c947e4cf58c64)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
33ff48bf5SDavid du Colombier #include <ip.h>
47dd7cddfSDavid du Colombier 
57dd7cddfSDavid du Colombier typedef struct Icmp Icmp;
67dd7cddfSDavid du Colombier struct Icmp
77dd7cddfSDavid du Colombier {
87dd7cddfSDavid du Colombier 	uchar	vihl;		/* Version and header length */
97dd7cddfSDavid du Colombier 	uchar	tos;		/* Type of service */
107dd7cddfSDavid du Colombier 	uchar	length[2];	/* packet length */
117dd7cddfSDavid du Colombier 	uchar	id[2];		/* Identification */
127dd7cddfSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
137dd7cddfSDavid du Colombier 	uchar	ttl;		/* Time to live */
147dd7cddfSDavid du Colombier 	uchar	proto;		/* Protocol */
157dd7cddfSDavid du Colombier 	uchar	ipcksum[2];	/* Header checksum */
167dd7cddfSDavid du Colombier 	uchar	src[4];		/* Ip source */
177dd7cddfSDavid du Colombier 	uchar	dst[4];		/* Ip destination */
187dd7cddfSDavid du Colombier 	uchar	type;
197dd7cddfSDavid du Colombier 	uchar	code;
207dd7cddfSDavid du Colombier 	uchar	cksum[2];
217dd7cddfSDavid du Colombier 	uchar	icmpid[2];
227dd7cddfSDavid du Colombier 	uchar	seq[2];
237dd7cddfSDavid du Colombier 	uchar	data[1];
247dd7cddfSDavid du Colombier };
257dd7cddfSDavid du Colombier 
267dd7cddfSDavid du Colombier enum
277dd7cddfSDavid du Colombier {			/* Packet Types */
287dd7cddfSDavid du Colombier 	EchoReply	= 0,
297dd7cddfSDavid du Colombier 	Unreachable	= 3,
307dd7cddfSDavid du Colombier 	SrcQuench	= 4,
317dd7cddfSDavid du Colombier 	EchoRequest	= 8,
327dd7cddfSDavid du Colombier 	TimeExceed	= 11,
337dd7cddfSDavid du Colombier 	Timestamp	= 13,
347dd7cddfSDavid du Colombier 	TimestampReply	= 14,
357dd7cddfSDavid du Colombier 	InfoRequest	= 15,
367dd7cddfSDavid du Colombier 	InfoReply	= 16,
377dd7cddfSDavid du Colombier 
387dd7cddfSDavid du Colombier 	ICMP_IPSIZE	= 20,
397dd7cddfSDavid du Colombier 	ICMP_HDRSIZE	= 8,
407dd7cddfSDavid du Colombier 
417dd7cddfSDavid du Colombier 	MAXMSG		= 32,
427dd7cddfSDavid du Colombier 	SLEEPMS		= 1000,
437dd7cddfSDavid du Colombier };
447dd7cddfSDavid du Colombier 
457dd7cddfSDavid du Colombier typedef struct Req Req;
467dd7cddfSDavid du Colombier struct Req
477dd7cddfSDavid du Colombier {
4800c1ae4bSDavid du Colombier 	ushort	seq;	// sequence number
497dd7cddfSDavid du Colombier 	vlong	time;	// time sent
503ff48bf5SDavid du Colombier 	vlong	rtt;
517dd7cddfSDavid du Colombier 	int	ttl;
523ff48bf5SDavid du Colombier 	int	replied;
537dd7cddfSDavid du Colombier 	Req	 *next;
547dd7cddfSDavid du Colombier };
557dd7cddfSDavid du Colombier Req	*first;		// request list
567dd7cddfSDavid du Colombier Req	*last;		// ...
577dd7cddfSDavid du Colombier Lock	listlock;
587dd7cddfSDavid du Colombier 
597dd7cddfSDavid du Colombier char *argv0;
607dd7cddfSDavid du Colombier int debug;
617dd7cddfSDavid du Colombier int quiet;
6200c1ae4bSDavid du Colombier int lostonly;
637dd7cddfSDavid du Colombier int lostmsgs;
647dd7cddfSDavid du Colombier int rcvdmsgs;
657dd7cddfSDavid du Colombier int done;
66da51d93aSDavid du Colombier int rint;
677dd7cddfSDavid du Colombier vlong sum;
683ff48bf5SDavid du Colombier ushort firstseq;
693ff48bf5SDavid du Colombier int addresses;
707dd7cddfSDavid du Colombier 
717dd7cddfSDavid du Colombier void usage(void);
7200c1ae4bSDavid du Colombier void lost(Req*, Icmp*);
733ff48bf5SDavid du Colombier void reply(Req*, Icmp*);
747dd7cddfSDavid du Colombier 
757dd7cddfSDavid du Colombier #define SECOND 1000000000LL
767dd7cddfSDavid du Colombier #define MINUTE (60LL*SECOND)
777dd7cddfSDavid du Colombier 
787dd7cddfSDavid du Colombier static void
797dd7cddfSDavid du Colombier catch(void *a, char *msg)
807dd7cddfSDavid du Colombier {
817dd7cddfSDavid du Colombier 	USED(a);
827dd7cddfSDavid du Colombier 	if(strstr(msg, "alarm"))
837dd7cddfSDavid du Colombier 		noted(NCONT);
848aafde0cSDavid du Colombier 	else if(strstr(msg, "die"))
858aafde0cSDavid du Colombier 		exits("errors");
867dd7cddfSDavid du Colombier 	else
877dd7cddfSDavid du Colombier 		noted(NDFLT);
887dd7cddfSDavid du Colombier }
897dd7cddfSDavid du Colombier 
907dd7cddfSDavid du Colombier void
913ff48bf5SDavid du Colombier clean(ushort seq, vlong now, Icmp *ip)
927dd7cddfSDavid du Colombier {
937dd7cddfSDavid du Colombier 	Req **l, *r;
947dd7cddfSDavid du Colombier 
957dd7cddfSDavid du Colombier 	lock(&listlock);
967dd7cddfSDavid du Colombier 	for(l = &first; *l; ){
977dd7cddfSDavid du Colombier 		r = *l;
983ff48bf5SDavid du Colombier 
998aafde0cSDavid du Colombier 		if(ip && r->seq == seq){
1003ff48bf5SDavid du Colombier 			r->rtt = now-r->time;
1013ff48bf5SDavid du Colombier 			r->ttl = ip->ttl;
1023ff48bf5SDavid du Colombier 			reply(r, ip);
1033ff48bf5SDavid du Colombier 		}
1043ff48bf5SDavid du Colombier 
1053ff48bf5SDavid du Colombier 		if(now-r->time > MINUTE){
1067dd7cddfSDavid du Colombier 			*l = r->next;
1073ff48bf5SDavid du Colombier 			r->rtt = now-r->time;
1088aafde0cSDavid du Colombier 			if(ip)
1093ff48bf5SDavid du Colombier 				r->ttl = ip->ttl;
1103ff48bf5SDavid du Colombier 			if(r->replied == 0)
11100c1ae4bSDavid du Colombier 				lost(r, ip);
1127dd7cddfSDavid du Colombier 			free(r);
11359cc4ca5SDavid du Colombier 		} else {
11459cc4ca5SDavid du Colombier 			last = r;
1157dd7cddfSDavid du Colombier 			l = &(r->next);
1167dd7cddfSDavid du Colombier 		}
11759cc4ca5SDavid du Colombier 	}
1187dd7cddfSDavid du Colombier 	unlock(&listlock);
1197dd7cddfSDavid du Colombier }
1207dd7cddfSDavid du Colombier 
1217dd7cddfSDavid du Colombier void
1227dd7cddfSDavid du Colombier sender(int fd, int msglen, int interval, int n)
1237dd7cddfSDavid du Colombier {
1247dd7cddfSDavid du Colombier 	char buf[64*1024+512];
1257dd7cddfSDavid du Colombier 	Icmp *ip;
126da51d93aSDavid du Colombier 	int i, extra;
1277dd7cddfSDavid du Colombier 	Req *r;
1287dd7cddfSDavid du Colombier 	ushort seq;
1297dd7cddfSDavid du Colombier 
1307dd7cddfSDavid du Colombier 	ip = (Icmp*)buf;
1317dd7cddfSDavid du Colombier 
1327dd7cddfSDavid du Colombier 	srand(time(0));
1333ff48bf5SDavid du Colombier 	firstseq = seq = rand();
1347dd7cddfSDavid du Colombier 
1359a747e4fSDavid du Colombier 	for(i = 32; i < msglen; i++)
1369a747e4fSDavid du Colombier 		buf[i] = i;
1377dd7cddfSDavid du Colombier 	ip->type = EchoRequest;
1387dd7cddfSDavid du Colombier 	ip->code = 0;
1397dd7cddfSDavid du Colombier 
1407dd7cddfSDavid du Colombier 	for(i = 0; i < n; i++){
141da51d93aSDavid du Colombier 		if(i != 0){
142da51d93aSDavid du Colombier 			extra = rint? nrand(interval): 0;
143da51d93aSDavid du Colombier 			sleep(interval + extra);
144da51d93aSDavid du Colombier 		}
1457dd7cddfSDavid du Colombier 		r = malloc(sizeof *r);
1467dd7cddfSDavid du Colombier 		if(r != nil){
1477dd7cddfSDavid du Colombier 			ip->seq[0] = seq;
1487dd7cddfSDavid du Colombier 			ip->seq[1] = seq>>8;
1497dd7cddfSDavid du Colombier 			r->seq = seq;
1507dd7cddfSDavid du Colombier 			r->next = nil;
1517dd7cddfSDavid du Colombier 			lock(&listlock);
1527dd7cddfSDavid du Colombier 			if(first == nil)
1537dd7cddfSDavid du Colombier 				first = r;
1547dd7cddfSDavid du Colombier 			else
1557dd7cddfSDavid du Colombier 				last->next = r;
1567dd7cddfSDavid du Colombier 			last = r;
1577dd7cddfSDavid du Colombier 			unlock(&listlock);
1583ff48bf5SDavid du Colombier 			r->replied = 0;
1597dd7cddfSDavid du Colombier 			r->time = nsec();
1607dd7cddfSDavid du Colombier 			if(write(fd, ip, msglen) < msglen){
1617dd7cddfSDavid du Colombier 				fprint(2, "%s: write failed: %r\n", argv0);
1627dd7cddfSDavid du Colombier 				return;
1637dd7cddfSDavid du Colombier 			}
1647dd7cddfSDavid du Colombier 			seq++;
1657dd7cddfSDavid du Colombier 		}
1667dd7cddfSDavid du Colombier 	}
1677dd7cddfSDavid du Colombier 	done = 1;
1687dd7cddfSDavid du Colombier }
1697dd7cddfSDavid du Colombier 
1707dd7cddfSDavid du Colombier void
171*fbfc9db9SDavid du Colombier rcvr(int fd, int msglen, int interval, int nmsg)
1727dd7cddfSDavid du Colombier {
1737dd7cddfSDavid du Colombier 	uchar buf[64*1024+512];
1747dd7cddfSDavid du Colombier 	Icmp *ip;
1757dd7cddfSDavid du Colombier 	ushort x;
1767dd7cddfSDavid du Colombier 	int i, n, munged;
1777dd7cddfSDavid du Colombier 	vlong now;
17859cc4ca5SDavid du Colombier 	Req *r;
1797dd7cddfSDavid du Colombier 
1807dd7cddfSDavid du Colombier 	ip = (Icmp*)buf;
1817dd7cddfSDavid du Colombier 	sum = 0;
1827dd7cddfSDavid du Colombier 
1838aafde0cSDavid du Colombier 	while(lostmsgs+rcvdmsgs < nmsg){
18459cc4ca5SDavid du Colombier 		alarm((nmsg-lostmsgs-rcvdmsgs)*interval+5000);
1857dd7cddfSDavid du Colombier 		n = read(fd, buf, sizeof(buf));
1867dd7cddfSDavid du Colombier 		alarm(0);
1877dd7cddfSDavid du Colombier 		now = nsec();
1888aafde0cSDavid du Colombier 		if(n <= 0){	/* read interrupted - time to go */
1898aafde0cSDavid du Colombier fprint(2, "clean\n");
1908aafde0cSDavid du Colombier 			clean(0, now+MINUTE, nil);
1918aafde0cSDavid du Colombier 			continue;
19200c1ae4bSDavid du Colombier 		}
1937dd7cddfSDavid du Colombier 		if(n < msglen){
1947dd7cddfSDavid du Colombier 			print("bad len %d/%d\n", n, msglen);
1957dd7cddfSDavid du Colombier 			continue;
1967dd7cddfSDavid du Colombier 		}
19780ee5cbfSDavid du Colombier 		munged = 0;
1989a747e4fSDavid du Colombier 		for(i = 32; i < msglen; i++)
1999a747e4fSDavid du Colombier 			if(buf[i] != (i&0xff))
20080ee5cbfSDavid du Colombier 				munged++;
20180ee5cbfSDavid du Colombier 		if(munged)
2029a747e4fSDavid du Colombier 			print("currupted reply\n");
2039a747e4fSDavid du Colombier 		x = (ip->seq[1]<<8)|ip->seq[0];
2047dd7cddfSDavid du Colombier 		if(ip->type != EchoReply || ip->code != 0) {
2057dd7cddfSDavid du Colombier 			print("bad sequence/code/type %d/%d/%d\n",
2067dd7cddfSDavid du Colombier 				ip->type, ip->code, x);
2077dd7cddfSDavid du Colombier 			continue;
2087dd7cddfSDavid du Colombier 		}
2093ff48bf5SDavid du Colombier 		clean(x, now, ip);
2107dd7cddfSDavid du Colombier 	}
21159cc4ca5SDavid du Colombier 
21259cc4ca5SDavid du Colombier 	lock(&listlock);
21359cc4ca5SDavid du Colombier 	for(r = first; r; r = r->next)
2143ff48bf5SDavid du Colombier 		if(r->replied == 0)
21559cc4ca5SDavid du Colombier 			lostmsgs++;
21659cc4ca5SDavid du Colombier 	unlock(&listlock);
21759cc4ca5SDavid du Colombier 
2187dd7cddfSDavid du Colombier 	if(lostmsgs)
2197dd7cddfSDavid du Colombier 		print("%d out of %d messages lost\n", lostmsgs, lostmsgs+rcvdmsgs);
22000c1ae4bSDavid du Colombier }
22100c1ae4bSDavid du Colombier 
22200c1ae4bSDavid du Colombier void
22300c1ae4bSDavid du Colombier usage(void)
22400c1ae4bSDavid du Colombier {
22500c1ae4bSDavid du Colombier 	fprint(2, "usage: %s [-alq] [-s msgsize] [-i millisecs] [-n #pings] destination\n", argv0);
22600c1ae4bSDavid du Colombier 	exits("usage");
2277dd7cddfSDavid du Colombier }
2287dd7cddfSDavid du Colombier 
2297dd7cddfSDavid du Colombier void
2307dd7cddfSDavid du Colombier main(int argc, char **argv)
2317dd7cddfSDavid du Colombier {
2327dd7cddfSDavid du Colombier 	int fd;
2337dd7cddfSDavid du Colombier 	int msglen, interval, nmsg;
2347dd7cddfSDavid du Colombier 
2357dd7cddfSDavid du Colombier 	nsec();		/* make sure time file is already open */
2367dd7cddfSDavid du Colombier 
2373ff48bf5SDavid du Colombier 	fmtinstall('V', eipfmt);
2383ff48bf5SDavid du Colombier 
2397dd7cddfSDavid du Colombier 	msglen = interval = 0;
2407dd7cddfSDavid du Colombier 	nmsg = MAXMSG;
2417dd7cddfSDavid du Colombier 	ARGBEGIN {
24200c1ae4bSDavid du Colombier 	case 'l':
24300c1ae4bSDavid du Colombier 		lostonly++;
24400c1ae4bSDavid du Colombier 		break;
2457dd7cddfSDavid du Colombier 	case 'd':
2467dd7cddfSDavid du Colombier 		debug++;
2477dd7cddfSDavid du Colombier 		break;
2487dd7cddfSDavid du Colombier 	case 's':
2497dd7cddfSDavid du Colombier 		msglen = atoi(ARGF());
2507dd7cddfSDavid du Colombier 		break;
2517dd7cddfSDavid du Colombier 	case 'i':
2527dd7cddfSDavid du Colombier 		interval = atoi(ARGF());
2537dd7cddfSDavid du Colombier 		break;
2547dd7cddfSDavid du Colombier 	case 'n':
2557dd7cddfSDavid du Colombier 		nmsg = atoi(ARGF());
2567dd7cddfSDavid du Colombier 		break;
2573ff48bf5SDavid du Colombier 	case 'a':
2583ff48bf5SDavid du Colombier 		addresses = 1;
2593ff48bf5SDavid du Colombier 		break;
2607dd7cddfSDavid du Colombier 	case 'q':
2617dd7cddfSDavid du Colombier 		quiet = 1;
2627dd7cddfSDavid du Colombier 		break;
263da51d93aSDavid du Colombier 	case 'r':
264da51d93aSDavid du Colombier 		rint = 1;
265da51d93aSDavid du Colombier 		break;
2667dd7cddfSDavid du Colombier 	} ARGEND;
2677dd7cddfSDavid du Colombier 	if(msglen < 32)
2687dd7cddfSDavid du Colombier 		msglen = 64;
2697dd7cddfSDavid du Colombier 	if(msglen >= 65*1024)
2707dd7cddfSDavid du Colombier 		msglen = 65*1024-1;
2717dd7cddfSDavid du Colombier 	if(interval <= 0)
2727dd7cddfSDavid du Colombier 		interval = SLEEPMS;
2737dd7cddfSDavid du Colombier 
2747dd7cddfSDavid du Colombier 	if(argc < 1)
2757dd7cddfSDavid du Colombier 		usage();
2767dd7cddfSDavid du Colombier 
2777dd7cddfSDavid du Colombier 	notify(catch);
2787dd7cddfSDavid du Colombier 
2797dd7cddfSDavid du Colombier 	fd = dial(netmkaddr(argv[0], "icmp", "1"), 0, 0, 0);
2807dd7cddfSDavid du Colombier 	if(fd < 0){
2817dd7cddfSDavid du Colombier 		fprint(2, "%s: couldn't dial: %r\n", argv0);
2827dd7cddfSDavid du Colombier 		exits("dialing");
2837dd7cddfSDavid du Colombier 	}
2847dd7cddfSDavid du Colombier 
2857dd7cddfSDavid du Colombier 	print("sending %d %d byte messages %d ms apart\n", nmsg, msglen, interval);
2867dd7cddfSDavid du Colombier 
2877dd7cddfSDavid du Colombier 	switch(rfork(RFPROC|RFMEM|RFFDG)){
2887dd7cddfSDavid du Colombier 	case -1:
2897dd7cddfSDavid du Colombier 		fprint(2, "%s: can't fork: %r\n", argv0);
2907dd7cddfSDavid du Colombier 	case 0:
291*fbfc9db9SDavid du Colombier 		rcvr(fd, msglen, interval, nmsg);
2927dd7cddfSDavid du Colombier 		exits(0);
2937dd7cddfSDavid du Colombier 	default:
2947dd7cddfSDavid du Colombier 		sender(fd, msglen, interval, nmsg);
2959a747e4fSDavid du Colombier 		wait();
296443f8232SDavid du Colombier 		exits(lostmsgs ? "lost messages" : "");
2977dd7cddfSDavid du Colombier 	}
2987dd7cddfSDavid du Colombier }
2997dd7cddfSDavid du Colombier 
3007dd7cddfSDavid du Colombier void
3013ff48bf5SDavid du Colombier reply(Req *r, Icmp *ip)
3027dd7cddfSDavid du Colombier {
3033ff48bf5SDavid du Colombier 	r->rtt /= 1000LL;
3043ff48bf5SDavid du Colombier 	sum += r->rtt;
3058aafde0cSDavid du Colombier 	if(!r->replied)
3068aafde0cSDavid du Colombier 		rcvdmsgs++;
30700c1ae4bSDavid du Colombier 	if(!quiet && !lostonly){
3083ff48bf5SDavid du Colombier 		if(addresses)
3093ff48bf5SDavid du Colombier 			print("%ud: %V->%V rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
3103ff48bf5SDavid du Colombier 				r->seq-firstseq,
3113ff48bf5SDavid du Colombier 				ip->src, ip->dst,
3123ff48bf5SDavid du Colombier 				r->rtt, sum/rcvdmsgs, r->ttl);
3133ff48bf5SDavid du Colombier 		else
3143ff48bf5SDavid du Colombier 			print("%ud: rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
3153ff48bf5SDavid du Colombier 				r->seq-firstseq,
3163ff48bf5SDavid du Colombier 				r->rtt, sum/rcvdmsgs, r->ttl);
3173ff48bf5SDavid du Colombier 	}
3183ff48bf5SDavid du Colombier 	r->replied = 1;
3197dd7cddfSDavid du Colombier }
32000c1ae4bSDavid du Colombier 
32100c1ae4bSDavid du Colombier void
32200c1ae4bSDavid du Colombier lost(Req *r, Icmp *ip)
32300c1ae4bSDavid du Colombier {
32400c1ae4bSDavid du Colombier 	if(!quiet){
32500c1ae4bSDavid du Colombier 		if(addresses)
3268aafde0cSDavid du Colombier 			print("lost %ud: %V->%V\n", r->seq-firstseq,
3278aafde0cSDavid du Colombier 				ip->src, ip->dst);
32800c1ae4bSDavid du Colombier 		else
3298aafde0cSDavid du Colombier 			print("lost %ud\n", r->seq-firstseq);
33000c1ae4bSDavid du Colombier 	}
33100c1ae4bSDavid du Colombier 	lostmsgs++;
33200c1ae4bSDavid du Colombier }
3338aafde0cSDavid du Colombier 
334