xref: /plan9/sys/src/cmd/ip/ping.c (revision 4ac22c891d8ae0eeadb1751300c3f2c67ae9fcb9)
1*4ac22c89SDavid du Colombier /* ping for ip v4 and v6 */
27dd7cddfSDavid du Colombier #include <u.h>
37dd7cddfSDavid du Colombier #include <libc.h>
43ff48bf5SDavid du Colombier #include <ip.h>
57dd7cddfSDavid du Colombier 
6*4ac22c89SDavid du Colombier enum
7*4ac22c89SDavid du Colombier {
8*4ac22c89SDavid du Colombier 	/* Packet Types */
9*4ac22c89SDavid du Colombier 	EchoReply	= 0,
10*4ac22c89SDavid du Colombier 	Unreachable	= 3,
11*4ac22c89SDavid du Colombier 	SrcQuench	= 4,
12*4ac22c89SDavid du Colombier 	EchoRequest	= 8,
13*4ac22c89SDavid du Colombier 	TimeExceed	= 11,
14*4ac22c89SDavid du Colombier 	Timestamp	= 13,
15*4ac22c89SDavid du Colombier 	TimestampReply	= 14,
16*4ac22c89SDavid du Colombier 	InfoRequest	= 15,
17*4ac22c89SDavid du Colombier 	InfoReply	= 16,
18*4ac22c89SDavid du Colombier 
19*4ac22c89SDavid du Colombier 	EchoReplyV6	= 129,
20*4ac22c89SDavid du Colombier 	EchoRequestV6	= 128,
21*4ac22c89SDavid du Colombier 
22*4ac22c89SDavid du Colombier 	MAXMSG		= 32,
23*4ac22c89SDavid du Colombier 	SLEEPMS		= 1000,
24*4ac22c89SDavid du Colombier 
25*4ac22c89SDavid du Colombier 	SECOND		= 1000000000LL,
26*4ac22c89SDavid du Colombier 	MINUTE		= 60*SECOND,
27*4ac22c89SDavid du Colombier };
28*4ac22c89SDavid du Colombier 
297dd7cddfSDavid du Colombier typedef struct Icmp Icmp;
307dd7cddfSDavid du Colombier struct Icmp
317dd7cddfSDavid du Colombier {
327dd7cddfSDavid du Colombier 	uchar	vihl;		/* Version and header length */
337dd7cddfSDavid du Colombier 	uchar	tos;		/* Type of service */
347dd7cddfSDavid du Colombier 	uchar	length[2];	/* packet length */
357dd7cddfSDavid du Colombier 	uchar	id[2];		/* Identification */
367dd7cddfSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
377dd7cddfSDavid du Colombier 	uchar	ttl;		/* Time to live */
387dd7cddfSDavid du Colombier 	uchar	proto;		/* Protocol */
397dd7cddfSDavid du Colombier 	uchar	ipcksum[2];	/* Header checksum */
407dd7cddfSDavid du Colombier 	uchar	src[4];		/* Ip source */
417dd7cddfSDavid du Colombier 	uchar	dst[4];		/* Ip destination */
427dd7cddfSDavid du Colombier 	uchar	type;
437dd7cddfSDavid du Colombier 	uchar	code;
447dd7cddfSDavid du Colombier 	uchar	cksum[2];
457dd7cddfSDavid du Colombier 	uchar	icmpid[2];
467dd7cddfSDavid du Colombier 	uchar	seq[2];
477dd7cddfSDavid du Colombier 	uchar	data[1];
487dd7cddfSDavid du Colombier };
497dd7cddfSDavid du Colombier 
50*4ac22c89SDavid du Colombier typedef struct Icmp6 Icmp6;
51*4ac22c89SDavid du Colombier struct Icmp6
52*4ac22c89SDavid du Colombier {
53*4ac22c89SDavid du Colombier 	uchar	vcf[4];
54*4ac22c89SDavid du Colombier 	uchar	ploadlen[2];
55*4ac22c89SDavid du Colombier 	uchar	proto;
56*4ac22c89SDavid du Colombier 	uchar	ttl;
57*4ac22c89SDavid du Colombier 	uchar	src[16];		/* Ip source */
58*4ac22c89SDavid du Colombier 	uchar	dst[16];		/* Ip destination */
59*4ac22c89SDavid du Colombier 	uchar	type;
60*4ac22c89SDavid du Colombier 	uchar	code;
61*4ac22c89SDavid du Colombier 	uchar	cksum[2];
62*4ac22c89SDavid du Colombier 	uchar	icmpid[2];
63*4ac22c89SDavid du Colombier 	uchar	seq[2];
64*4ac22c89SDavid du Colombier 	uchar	data[1];
657dd7cddfSDavid du Colombier };
667dd7cddfSDavid du Colombier 
677dd7cddfSDavid du Colombier typedef struct Req Req;
687dd7cddfSDavid du Colombier struct Req
697dd7cddfSDavid du Colombier {
70*4ac22c89SDavid du Colombier 	ushort	seq;	/* sequence number */
71*4ac22c89SDavid du Colombier 	vlong	time;	/* time sent */
723ff48bf5SDavid du Colombier 	vlong	rtt;
737dd7cddfSDavid du Colombier 	int	ttl;
743ff48bf5SDavid du Colombier 	int	replied;
757dd7cddfSDavid du Colombier 	Req	 *next;
767dd7cddfSDavid du Colombier };
77*4ac22c89SDavid du Colombier 
78*4ac22c89SDavid du Colombier typedef struct {
79*4ac22c89SDavid du Colombier 	char	*net;
80*4ac22c89SDavid du Colombier 	int	echoreply;
81*4ac22c89SDavid du Colombier 	unsigned icmphdrsz;
82*4ac22c89SDavid du Colombier 	int	(*getttl)(void *v);
83*4ac22c89SDavid du Colombier 	int	(*getseq)(void *v);
84*4ac22c89SDavid du Colombier 	void	(*putseq)(void *v, ushort seq);
85*4ac22c89SDavid du Colombier 	int	(*gettype)(void *v);
86*4ac22c89SDavid du Colombier 	void	(*settype)(void *v);
87*4ac22c89SDavid du Colombier 	int	(*getcode)(void *v);
88*4ac22c89SDavid du Colombier 	void	(*setcode)(void *v);
89*4ac22c89SDavid du Colombier 	void	(*prreply)(Req *r, void *v);
90*4ac22c89SDavid du Colombier 	void	(*prlost)(ushort seq, void *v);
91*4ac22c89SDavid du Colombier } Proto;
92*4ac22c89SDavid du Colombier 
93*4ac22c89SDavid du Colombier Req	*first;		/* request list */
94*4ac22c89SDavid du Colombier Req	*last;		/* ... */
957dd7cddfSDavid du Colombier Lock	listlock;
967dd7cddfSDavid du Colombier 
977dd7cddfSDavid du Colombier char *argv0;
987dd7cddfSDavid du Colombier int debug;
997dd7cddfSDavid du Colombier int quiet;
10000c1ae4bSDavid du Colombier int lostonly;
1017dd7cddfSDavid du Colombier int lostmsgs;
1027dd7cddfSDavid du Colombier int rcvdmsgs;
1037dd7cddfSDavid du Colombier int done;
104da51d93aSDavid du Colombier int rint;
1057dd7cddfSDavid du Colombier vlong sum;
1063ff48bf5SDavid du Colombier ushort firstseq;
1073ff48bf5SDavid du Colombier int addresses;
1080b9a5132SDavid du Colombier int flood;
1097dd7cddfSDavid du Colombier 
110*4ac22c89SDavid du Colombier void lost(Req*, void*);
111*4ac22c89SDavid du Colombier void reply(Req*, void*);
1127dd7cddfSDavid du Colombier 
113*4ac22c89SDavid du Colombier static void
114*4ac22c89SDavid du Colombier usage(void)
115*4ac22c89SDavid du Colombier {
116*4ac22c89SDavid du Colombier 	fprint(2,
117*4ac22c89SDavid du Colombier 	    "usage: %s [-6alq] [-s msgsize] [-i millisecs] [-n #pings] dest\n",
118*4ac22c89SDavid du Colombier 		argv0);
119*4ac22c89SDavid du Colombier 	exits("usage");
120*4ac22c89SDavid du Colombier }
1217dd7cddfSDavid du Colombier 
1227dd7cddfSDavid du Colombier static void
1237dd7cddfSDavid du Colombier catch(void *a, char *msg)
1247dd7cddfSDavid du Colombier {
1257dd7cddfSDavid du Colombier 	USED(a);
1267dd7cddfSDavid du Colombier 	if(strstr(msg, "alarm"))
1277dd7cddfSDavid du Colombier 		noted(NCONT);
1288aafde0cSDavid du Colombier 	else if(strstr(msg, "die"))
1298aafde0cSDavid du Colombier 		exits("errors");
1307dd7cddfSDavid du Colombier 	else
1317dd7cddfSDavid du Colombier 		noted(NDFLT);
1327dd7cddfSDavid du Colombier }
1337dd7cddfSDavid du Colombier 
134*4ac22c89SDavid du Colombier 
135*4ac22c89SDavid du Colombier static int
136*4ac22c89SDavid du Colombier getttl4(void *v)
137*4ac22c89SDavid du Colombier {
138*4ac22c89SDavid du Colombier 	return ((Icmp *)v)->ttl;
139*4ac22c89SDavid du Colombier }
140*4ac22c89SDavid du Colombier 
141*4ac22c89SDavid du Colombier static int
142*4ac22c89SDavid du Colombier getttl6(void *v)
143*4ac22c89SDavid du Colombier {
144*4ac22c89SDavid du Colombier 	return ((Icmp6 *)v)->ttl;
145*4ac22c89SDavid du Colombier }
146*4ac22c89SDavid du Colombier 
147*4ac22c89SDavid du Colombier static int
148*4ac22c89SDavid du Colombier getseq4(void *v)
149*4ac22c89SDavid du Colombier {
150*4ac22c89SDavid du Colombier 	return nhgets(((Icmp *)v)->seq);
151*4ac22c89SDavid du Colombier }
152*4ac22c89SDavid du Colombier 
153*4ac22c89SDavid du Colombier static int
154*4ac22c89SDavid du Colombier getseq6(void *v)
155*4ac22c89SDavid du Colombier {
156*4ac22c89SDavid du Colombier 	Icmp6 *ip6 = v;
157*4ac22c89SDavid du Colombier 
158*4ac22c89SDavid du Colombier 	return ip6->seq[1]<<8 | ip6->seq[0];
159*4ac22c89SDavid du Colombier }
160*4ac22c89SDavid du Colombier 
161*4ac22c89SDavid du Colombier static void
162*4ac22c89SDavid du Colombier putseq4(void *v, ushort seq)
163*4ac22c89SDavid du Colombier {
164*4ac22c89SDavid du Colombier 	hnputs(((Icmp *)v)->seq, seq);
165*4ac22c89SDavid du Colombier }
166*4ac22c89SDavid du Colombier 
167*4ac22c89SDavid du Colombier static void
168*4ac22c89SDavid du Colombier putseq6(void *v, ushort seq)
169*4ac22c89SDavid du Colombier {
170*4ac22c89SDavid du Colombier 	((Icmp6 *)v)->seq[0] = seq;
171*4ac22c89SDavid du Colombier 	((Icmp6 *)v)->seq[1] = seq>>8;
172*4ac22c89SDavid du Colombier }
173*4ac22c89SDavid du Colombier 
174*4ac22c89SDavid du Colombier static int
175*4ac22c89SDavid du Colombier gettype4(void *v)
176*4ac22c89SDavid du Colombier {
177*4ac22c89SDavid du Colombier 	return ((Icmp *)v)->type;
178*4ac22c89SDavid du Colombier }
179*4ac22c89SDavid du Colombier 
180*4ac22c89SDavid du Colombier static int
181*4ac22c89SDavid du Colombier gettype6(void *v)
182*4ac22c89SDavid du Colombier {
183*4ac22c89SDavid du Colombier 	return ((Icmp6 *)v)->type;
184*4ac22c89SDavid du Colombier }
185*4ac22c89SDavid du Colombier 
186*4ac22c89SDavid du Colombier static void
187*4ac22c89SDavid du Colombier settype4(void *v)
188*4ac22c89SDavid du Colombier {
189*4ac22c89SDavid du Colombier 	((Icmp *)v)->type = EchoRequest;
190*4ac22c89SDavid du Colombier }
191*4ac22c89SDavid du Colombier 
192*4ac22c89SDavid du Colombier static void
193*4ac22c89SDavid du Colombier settype6(void *v)
194*4ac22c89SDavid du Colombier {
195*4ac22c89SDavid du Colombier 	((Icmp6 *)v)->type = EchoRequestV6;
196*4ac22c89SDavid du Colombier }
197*4ac22c89SDavid du Colombier 
198*4ac22c89SDavid du Colombier static int
199*4ac22c89SDavid du Colombier getcode4(void *v)
200*4ac22c89SDavid du Colombier {
201*4ac22c89SDavid du Colombier 	return ((Icmp *)v)->code;
202*4ac22c89SDavid du Colombier }
203*4ac22c89SDavid du Colombier 
204*4ac22c89SDavid du Colombier static int
205*4ac22c89SDavid du Colombier getcode6(void *v)
206*4ac22c89SDavid du Colombier {
207*4ac22c89SDavid du Colombier 	return ((Icmp6 *)v)->code;
208*4ac22c89SDavid du Colombier }
209*4ac22c89SDavid du Colombier 
210*4ac22c89SDavid du Colombier static void
211*4ac22c89SDavid du Colombier setcode4(void *v)
212*4ac22c89SDavid du Colombier {
213*4ac22c89SDavid du Colombier 	((Icmp *)v)->code = 0;
214*4ac22c89SDavid du Colombier }
215*4ac22c89SDavid du Colombier 
216*4ac22c89SDavid du Colombier static void
217*4ac22c89SDavid du Colombier setcode6(void *v)
218*4ac22c89SDavid du Colombier {
219*4ac22c89SDavid du Colombier 	((Icmp6 *)v)->code = 0;
220*4ac22c89SDavid du Colombier }
221*4ac22c89SDavid du Colombier 
222*4ac22c89SDavid du Colombier static void
223*4ac22c89SDavid du Colombier prlost4(ushort seq, void *v)
224*4ac22c89SDavid du Colombier {
225*4ac22c89SDavid du Colombier 	Icmp *ip4 = v;
226*4ac22c89SDavid du Colombier 
227*4ac22c89SDavid du Colombier 	print("lost %ud: %V->%V\n", seq, ip4->src, ip4->dst);
228*4ac22c89SDavid du Colombier }
229*4ac22c89SDavid du Colombier 
230*4ac22c89SDavid du Colombier static void
231*4ac22c89SDavid du Colombier prlost6(ushort seq, void *v)
232*4ac22c89SDavid du Colombier {
233*4ac22c89SDavid du Colombier 	Icmp6 *ip6 = v;
234*4ac22c89SDavid du Colombier 
235*4ac22c89SDavid du Colombier 	print("lost %ud: %I->%I\n", seq, ip6->src, ip6->dst);
236*4ac22c89SDavid du Colombier }
237*4ac22c89SDavid du Colombier 
238*4ac22c89SDavid du Colombier static void
239*4ac22c89SDavid du Colombier prreply4(Req *r, void *v)
240*4ac22c89SDavid du Colombier {
241*4ac22c89SDavid du Colombier 	Icmp *ip4 = v;
242*4ac22c89SDavid du Colombier 
243*4ac22c89SDavid du Colombier 	print("%ud: %V->%V rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
244*4ac22c89SDavid du Colombier 		r->seq - firstseq, ip4->src, ip4->dst, r->rtt, sum/rcvdmsgs,
245*4ac22c89SDavid du Colombier 		r->ttl);
246*4ac22c89SDavid du Colombier }
247*4ac22c89SDavid du Colombier 
248*4ac22c89SDavid du Colombier static void
249*4ac22c89SDavid du Colombier prreply6(Req *r, void *v)
250*4ac22c89SDavid du Colombier {
251*4ac22c89SDavid du Colombier 	Icmp *ip6 = v;
252*4ac22c89SDavid du Colombier 
253*4ac22c89SDavid du Colombier 	print("%ud: %I->%I rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
254*4ac22c89SDavid du Colombier 		r->seq - firstseq, ip6->src, ip6->dst, r->rtt, sum/rcvdmsgs,
255*4ac22c89SDavid du Colombier 		r->ttl);
256*4ac22c89SDavid du Colombier }
257*4ac22c89SDavid du Colombier 
258*4ac22c89SDavid du Colombier static Proto v4pr = {
259*4ac22c89SDavid du Colombier 	"icmp",
260*4ac22c89SDavid du Colombier 	EchoReply,
261*4ac22c89SDavid du Colombier 	sizeof(Icmp),
262*4ac22c89SDavid du Colombier 	getttl4,
263*4ac22c89SDavid du Colombier 	getseq4,
264*4ac22c89SDavid du Colombier 	putseq4,
265*4ac22c89SDavid du Colombier 	gettype4,
266*4ac22c89SDavid du Colombier 	settype4,
267*4ac22c89SDavid du Colombier 	getcode4,
268*4ac22c89SDavid du Colombier 	setcode4,
269*4ac22c89SDavid du Colombier 	prreply4,
270*4ac22c89SDavid du Colombier 	prlost4,
271*4ac22c89SDavid du Colombier };
272*4ac22c89SDavid du Colombier static Proto v6pr = {
273*4ac22c89SDavid du Colombier 	"icmpv6",
274*4ac22c89SDavid du Colombier 	EchoReplyV6,
275*4ac22c89SDavid du Colombier 	sizeof(Icmp6),
276*4ac22c89SDavid du Colombier 	getttl6,
277*4ac22c89SDavid du Colombier 	getseq6,
278*4ac22c89SDavid du Colombier 	putseq6,
279*4ac22c89SDavid du Colombier 	gettype6,
280*4ac22c89SDavid du Colombier 	settype6,
281*4ac22c89SDavid du Colombier 	getcode6,
282*4ac22c89SDavid du Colombier 	setcode6,
283*4ac22c89SDavid du Colombier 	prreply6,
284*4ac22c89SDavid du Colombier 	prlost6,
285*4ac22c89SDavid du Colombier };
286*4ac22c89SDavid du Colombier 
287*4ac22c89SDavid du Colombier static Proto *proto = &v4pr;
288*4ac22c89SDavid du Colombier 
2897dd7cddfSDavid du Colombier void
290*4ac22c89SDavid du Colombier clean(ushort seq, vlong now, void *v)
2917dd7cddfSDavid du Colombier {
2927dd7cddfSDavid du Colombier 	Req **l, *r;
2937dd7cddfSDavid du Colombier 
2947dd7cddfSDavid du Colombier 	lock(&listlock);
2950b9a5132SDavid du Colombier 	last = nil;
2967dd7cddfSDavid du Colombier 	for(l = &first; *l; ){
2977dd7cddfSDavid du Colombier 		r = *l;
2983ff48bf5SDavid du Colombier 
299*4ac22c89SDavid du Colombier 		if(v && r->seq == seq){
3003ff48bf5SDavid du Colombier 			r->rtt = now-r->time;
301*4ac22c89SDavid du Colombier 			r->ttl = (*proto->getttl)(v);
302*4ac22c89SDavid du Colombier 			reply(r, v);
3033ff48bf5SDavid du Colombier 		}
3043ff48bf5SDavid du Colombier 
3053ff48bf5SDavid du Colombier 		if(now-r->time > MINUTE){
3067dd7cddfSDavid du Colombier 			*l = r->next;
3073ff48bf5SDavid du Colombier 			r->rtt = now-r->time;
308*4ac22c89SDavid du Colombier 			if(v)
309*4ac22c89SDavid du Colombier 				r->ttl = (*proto->getttl)(v);
3103ff48bf5SDavid du Colombier 			if(r->replied == 0)
311*4ac22c89SDavid du Colombier 				lost(r, v);
3127dd7cddfSDavid du Colombier 			free(r);
31359cc4ca5SDavid du Colombier 		}else{
31459cc4ca5SDavid du Colombier 			last = r;
315*4ac22c89SDavid du Colombier 			l = &r->next;
3167dd7cddfSDavid du Colombier 		}
31759cc4ca5SDavid du Colombier 	}
3187dd7cddfSDavid du Colombier 	unlock(&listlock);
3197dd7cddfSDavid du Colombier }
3207dd7cddfSDavid du Colombier 
3217dd7cddfSDavid du Colombier void
3227dd7cddfSDavid du Colombier sender(int fd, int msglen, int interval, int n)
3237dd7cddfSDavid du Colombier {
324da51d93aSDavid du Colombier 	int i, extra;
3257dd7cddfSDavid du Colombier 	ushort seq;
326*4ac22c89SDavid du Colombier 	char buf[64*1024+512];
327*4ac22c89SDavid du Colombier 	Req *r;
3287dd7cddfSDavid du Colombier 
3297dd7cddfSDavid du Colombier 	srand(time(0));
3303ff48bf5SDavid du Colombier 	firstseq = seq = rand();
3317dd7cddfSDavid du Colombier 
332*4ac22c89SDavid du Colombier 	for(i = proto->icmphdrsz; i < msglen; i++)
3339a747e4fSDavid du Colombier 		buf[i] = i;
334*4ac22c89SDavid du Colombier 	(*proto->settype)(buf);
335*4ac22c89SDavid du Colombier 	(*proto->setcode)(buf);
3367dd7cddfSDavid du Colombier 
3377dd7cddfSDavid du Colombier 	for(i = 0; i < n; i++){
338da51d93aSDavid du Colombier 		if(i != 0){
339da51d93aSDavid du Colombier 			extra = rint? nrand(interval): 0;
340da51d93aSDavid du Colombier 			sleep(interval + extra);
341da51d93aSDavid du Colombier 		}
3427dd7cddfSDavid du Colombier 		r = malloc(sizeof *r);
343*4ac22c89SDavid du Colombier 		if (r == nil)
344*4ac22c89SDavid du Colombier 			continue;
345*4ac22c89SDavid du Colombier 		(*proto->putseq)(buf, seq);
3467dd7cddfSDavid du Colombier 		r->seq = seq;
3477dd7cddfSDavid du Colombier 		r->next = nil;
3480b9a5132SDavid du Colombier 		r->replied = 0;
3490b9a5132SDavid du Colombier 		r->time = nsec();	/* avoid early free in reply! */
3507dd7cddfSDavid du Colombier 		lock(&listlock);
3517dd7cddfSDavid du Colombier 		if(first == nil)
3527dd7cddfSDavid du Colombier 			first = r;
3537dd7cddfSDavid du Colombier 		else
3547dd7cddfSDavid du Colombier 			last->next = r;
3557dd7cddfSDavid du Colombier 		last = r;
3567dd7cddfSDavid du Colombier 		unlock(&listlock);
3577dd7cddfSDavid du Colombier 		r->time = nsec();
358*4ac22c89SDavid du Colombier 		if(write(fd, buf, msglen) < msglen){
3597dd7cddfSDavid du Colombier 			fprint(2, "%s: write failed: %r\n", argv0);
3607dd7cddfSDavid du Colombier 			return;
3617dd7cddfSDavid du Colombier 		}
3627dd7cddfSDavid du Colombier 		seq++;
3637dd7cddfSDavid du Colombier 	}
3647dd7cddfSDavid du Colombier 	done = 1;
3657dd7cddfSDavid du Colombier }
3667dd7cddfSDavid du Colombier 
3677dd7cddfSDavid du Colombier void
368fbfc9db9SDavid du Colombier rcvr(int fd, int msglen, int interval, int nmsg)
3697dd7cddfSDavid du Colombier {
3707dd7cddfSDavid du Colombier 	int i, n, munged;
371*4ac22c89SDavid du Colombier 	ushort x;
3727dd7cddfSDavid du Colombier 	vlong now;
373*4ac22c89SDavid du Colombier 	uchar buf[64*1024+512];
37459cc4ca5SDavid du Colombier 	Req *r;
3757dd7cddfSDavid du Colombier 
3767dd7cddfSDavid du Colombier 	sum = 0;
3778aafde0cSDavid du Colombier 	while(lostmsgs+rcvdmsgs < nmsg){
37859cc4ca5SDavid du Colombier 		alarm((nmsg-lostmsgs-rcvdmsgs)*interval+5000);
379*4ac22c89SDavid du Colombier 		n = read(fd, buf, sizeof buf);
3807dd7cddfSDavid du Colombier 		alarm(0);
3817dd7cddfSDavid du Colombier 		now = nsec();
3828aafde0cSDavid du Colombier 		if(n <= 0){	/* read interrupted - time to go */
3838aafde0cSDavid du Colombier 			clean(0, now+MINUTE, nil);
3848aafde0cSDavid du Colombier 			continue;
38500c1ae4bSDavid du Colombier 		}
3867dd7cddfSDavid du Colombier 		if(n < msglen){
3877dd7cddfSDavid du Colombier 			print("bad len %d/%d\n", n, msglen);
3887dd7cddfSDavid du Colombier 			continue;
3897dd7cddfSDavid du Colombier 		}
39080ee5cbfSDavid du Colombier 		munged = 0;
391*4ac22c89SDavid du Colombier 		for(i = proto->icmphdrsz; i < msglen; i++)
392*4ac22c89SDavid du Colombier 			if(buf[i] != (uchar)i)
39380ee5cbfSDavid du Colombier 				munged++;
39480ee5cbfSDavid du Colombier 		if(munged)
395*4ac22c89SDavid du Colombier 			print("corrupted reply\n");
396*4ac22c89SDavid du Colombier 		x = (*proto->getseq)(buf);
397*4ac22c89SDavid du Colombier 		if((*proto->gettype)(buf) != proto->echoreply ||
398*4ac22c89SDavid du Colombier 		   (*proto->getcode)(buf) != 0) {
3997dd7cddfSDavid du Colombier 			print("bad sequence/code/type %d/%d/%d\n",
400*4ac22c89SDavid du Colombier 				(*proto->gettype)(buf), (*proto->getcode)(buf),
401*4ac22c89SDavid du Colombier 				x);
4027dd7cddfSDavid du Colombier 			continue;
4037dd7cddfSDavid du Colombier 		}
404*4ac22c89SDavid du Colombier 		clean(x, now, buf);
4057dd7cddfSDavid du Colombier 	}
40659cc4ca5SDavid du Colombier 
40759cc4ca5SDavid du Colombier 	lock(&listlock);
40859cc4ca5SDavid du Colombier 	for(r = first; r; r = r->next)
4093ff48bf5SDavid du Colombier 		if(r->replied == 0)
41059cc4ca5SDavid du Colombier 			lostmsgs++;
41159cc4ca5SDavid du Colombier 	unlock(&listlock);
41259cc4ca5SDavid du Colombier 
4137dd7cddfSDavid du Colombier 	if(lostmsgs)
414*4ac22c89SDavid du Colombier 		print("%d out of %d messages lost\n", lostmsgs,
415*4ac22c89SDavid du Colombier 			lostmsgs+rcvdmsgs);
4167dd7cddfSDavid du Colombier }
4177dd7cddfSDavid du Colombier 
4187dd7cddfSDavid du Colombier void
4197dd7cddfSDavid du Colombier main(int argc, char **argv)
4207dd7cddfSDavid du Colombier {
421*4ac22c89SDavid du Colombier 	int fd, msglen, interval, nmsg;
422*4ac22c89SDavid du Colombier 	char *ds;
4237dd7cddfSDavid du Colombier 
4247dd7cddfSDavid du Colombier 	nsec();		/* make sure time file is already open */
4257dd7cddfSDavid du Colombier 
4263ff48bf5SDavid du Colombier 	fmtinstall('V', eipfmt);
4273ff48bf5SDavid du Colombier 
4287dd7cddfSDavid du Colombier 	msglen = interval = 0;
4297dd7cddfSDavid du Colombier 	nmsg = MAXMSG;
4307dd7cddfSDavid du Colombier 	ARGBEGIN {
431*4ac22c89SDavid du Colombier 	case '6':
432*4ac22c89SDavid du Colombier 		proto = &v6pr;
433*4ac22c89SDavid du Colombier 		break;
434*4ac22c89SDavid du Colombier 	case 'a':
435*4ac22c89SDavid du Colombier 		addresses = 1;
43600c1ae4bSDavid du Colombier 		break;
4377dd7cddfSDavid du Colombier 	case 'd':
4387dd7cddfSDavid du Colombier 		debug++;
4397dd7cddfSDavid du Colombier 		break;
440*4ac22c89SDavid du Colombier 	case 'f':
441*4ac22c89SDavid du Colombier 		flood = 1;
4427dd7cddfSDavid du Colombier 		break;
4437dd7cddfSDavid du Colombier 	case 'i':
444*4ac22c89SDavid du Colombier 		interval = atoi(EARGF(usage()));
445*4ac22c89SDavid du Colombier 		break;
446*4ac22c89SDavid du Colombier 	case 'l':
447*4ac22c89SDavid du Colombier 		lostonly++;
4487dd7cddfSDavid du Colombier 		break;
4497dd7cddfSDavid du Colombier 	case 'n':
450*4ac22c89SDavid du Colombier 		nmsg = atoi(EARGF(usage()));
4513ff48bf5SDavid du Colombier 		break;
4527dd7cddfSDavid du Colombier 	case 'q':
4537dd7cddfSDavid du Colombier 		quiet = 1;
4547dd7cddfSDavid du Colombier 		break;
455da51d93aSDavid du Colombier 	case 'r':
456da51d93aSDavid du Colombier 		rint = 1;
457da51d93aSDavid du Colombier 		break;
458*4ac22c89SDavid du Colombier 	case 's':
459*4ac22c89SDavid du Colombier 		msglen = atoi(EARGF(usage()));
460*4ac22c89SDavid du Colombier 		break;
461*4ac22c89SDavid du Colombier 	default:
462*4ac22c89SDavid du Colombier 		usage();
4630b9a5132SDavid du Colombier 		break;
4647dd7cddfSDavid du Colombier 	} ARGEND;
465*4ac22c89SDavid du Colombier 
466*4ac22c89SDavid du Colombier 	if(msglen < proto->icmphdrsz)
467*4ac22c89SDavid du Colombier 		msglen = proto->icmphdrsz;
468*4ac22c89SDavid du Colombier 	if(msglen < 64)
4697dd7cddfSDavid du Colombier 		msglen = 64;
4707dd7cddfSDavid du Colombier 	if(msglen >= 65*1024)
4717dd7cddfSDavid du Colombier 		msglen = 65*1024-1;
4720b9a5132SDavid du Colombier 	if(interval <= 0 && !flood)
4737dd7cddfSDavid du Colombier 		interval = SLEEPMS;
4747dd7cddfSDavid du Colombier 
4757dd7cddfSDavid du Colombier 	if(argc < 1)
4767dd7cddfSDavid du Colombier 		usage();
4777dd7cddfSDavid du Colombier 
4787dd7cddfSDavid du Colombier 	notify(catch);
4797dd7cddfSDavid du Colombier 
480*4ac22c89SDavid du Colombier 	ds = netmkaddr(argv[0], proto->net, "1");
481*4ac22c89SDavid du Colombier 	fd = dial(ds, 0, 0, 0);
4827dd7cddfSDavid du Colombier 	if(fd < 0){
483*4ac22c89SDavid du Colombier 		fprint(2, "%s: couldn't dial %s: %r\n", argv0, ds);
4847dd7cddfSDavid du Colombier 		exits("dialing");
4857dd7cddfSDavid du Colombier 	}
4867dd7cddfSDavid du Colombier 
487*4ac22c89SDavid du Colombier 	print("sending %d %d byte messages %d ms apart to %s\n",
488*4ac22c89SDavid du Colombier 		nmsg, msglen, interval, ds);
4897dd7cddfSDavid du Colombier 
4907dd7cddfSDavid du Colombier 	switch(rfork(RFPROC|RFMEM|RFFDG)){
4917dd7cddfSDavid du Colombier 	case -1:
4927dd7cddfSDavid du Colombier 		fprint(2, "%s: can't fork: %r\n", argv0);
493*4ac22c89SDavid du Colombier 		/* fallthrough */
4947dd7cddfSDavid du Colombier 	case 0:
495fbfc9db9SDavid du Colombier 		rcvr(fd, msglen, interval, nmsg);
4967dd7cddfSDavid du Colombier 		exits(0);
4977dd7cddfSDavid du Colombier 	default:
4987dd7cddfSDavid du Colombier 		sender(fd, msglen, interval, nmsg);
4999a747e4fSDavid du Colombier 		wait();
500443f8232SDavid du Colombier 		exits(lostmsgs ? "lost messages" : "");
5017dd7cddfSDavid du Colombier 	}
5027dd7cddfSDavid du Colombier }
5037dd7cddfSDavid du Colombier 
5047dd7cddfSDavid du Colombier void
505*4ac22c89SDavid du Colombier reply(Req *r, void *v)
5067dd7cddfSDavid du Colombier {
5073ff48bf5SDavid du Colombier 	r->rtt /= 1000LL;
5083ff48bf5SDavid du Colombier 	sum += r->rtt;
5098aafde0cSDavid du Colombier 	if(!r->replied)
5108aafde0cSDavid du Colombier 		rcvdmsgs++;
511*4ac22c89SDavid du Colombier 	if(!quiet && !lostonly)
5123ff48bf5SDavid du Colombier 		if(addresses)
513*4ac22c89SDavid du Colombier 			(*proto->prreply)(r, v);
5143ff48bf5SDavid du Colombier 		else
5153ff48bf5SDavid du Colombier 			print("%ud: rtt %lld µs, avg rtt %lld µs, ttl = %d\n",
516*4ac22c89SDavid du Colombier 				r->seq - firstseq, r->rtt, sum/rcvdmsgs, r->ttl);
5173ff48bf5SDavid du Colombier 	r->replied = 1;
5187dd7cddfSDavid du Colombier }
51900c1ae4bSDavid du Colombier 
52000c1ae4bSDavid du Colombier void
521*4ac22c89SDavid du Colombier lost(Req *r, void *v)
52200c1ae4bSDavid du Colombier {
523*4ac22c89SDavid du Colombier 	if(!quiet)
524*4ac22c89SDavid du Colombier 		if(addresses && v != nil)
525*4ac22c89SDavid du Colombier 			(*proto->prlost)(r->seq - firstseq, v);
52600c1ae4bSDavid du Colombier 		else
5278aafde0cSDavid du Colombier 			print("lost %ud\n", r->seq - firstseq);
52800c1ae4bSDavid du Colombier 	lostmsgs++;
52900c1ae4bSDavid du Colombier }
530