431 lines · 7.1 KB · C
RawDownload
/* ping for ip v4 and v6 */#include <u.h>#include <libc.h>#include <ctype.h>#include <ip.h>#include <bio.h>#include <ndb.h>#include "icmp.h" enum {	MAXMSG		= 32,	SLEEPMS		= 1000, 	SECOND		= 1000000000LL,	MINUTE		= 60*SECOND,}; typedef struct Req Req;struct Req{	ushort	seq;	/* sequence number */	vlong	time;	/* time sent */	vlong	rtt;	int	ttl;	int	replied;	Req	 *next;}; typedef struct {	int	version;	char	*net;	int	echocmd;	int	echoreply;	unsigned iphdrsz; 	void	(*prreply)(Req *r, void *v);	void	(*prlost)(ushort seq, void *v);} Proto;  Req	*first;		/* request list */Req	*last;		/* ... */Lock	listlock; char *argv0; int addresses;int debug;int done;int flood;int lostmsgs;int lostonly;int quiet;int rcvdmsgs;int rint;ushort firstseq;vlong sum;int waittime = 5000; void lost(Req*, void*);void reply(Req*, void*); static voidusage(void){	fprint(2,	    "usage: %s [-6alq] [-s msgsize] [-i millisecs] [-n #pings] dest\n",		argv0);	exits("usage");} static voidcatch(void *a, char *msg){	USED(a);	if(strstr(msg, "alarm"))		noted(NCONT);	else if(strstr(msg, "die"))		exits("errors");	else		noted(NDFLT);} static voidprlost4(ushort seq, void *v){	Ip4hdr *ip4 = v; 	print("lost %ud: %V -> %V\n", seq, ip4->src, ip4->dst);} static voidprlost6(ushort seq, void *v){	Ip6hdr *ip6 = v; 	print("lost %ud: %I -> %I\n", seq, ip6->src, ip6->dst);} static voidprreply4(Req *r, void *v){	Ip4hdr *ip4 = v; 	print("%ud: %V -> %V rtt %lld µs, avg rtt %lld µs, ttl = %d\n",		r->seq - firstseq, ip4->src, ip4->dst, r->rtt, sum/rcvdmsgs,		r->ttl);} static voidprreply6(Req *r, void *v){	Ip6hdr *ip6 = v; 	print("%ud: %I -> %I rtt %lld µs, avg rtt %lld µs, ttl = %d\n",		r->seq - firstseq, ip6->src, ip6->dst, r->rtt, sum/rcvdmsgs,		r->ttl);} static Proto v4pr = {	4,		"icmp",	EchoRequest,	EchoReply,	IPV4HDR_LEN,	prreply4,	prlost4,};static Proto v6pr = {	6,		"icmpv6",	EchoRequestV6,	EchoReplyV6,	IPV6HDR_LEN,	prreply6,	prlost6,}; static Proto *proto = &v4pr;  Icmphdr *geticmp(void *v){	char *p = v; 	return (Icmphdr *)(p + proto->iphdrsz);} voidclean(ushort seq, vlong now, void *v){	int ttl;	Req **l, *r; 	ttl = 0;	if (v) {		if (proto->version == 4)			ttl = ((Ip4hdr *)v)->ttl;		else			ttl = ((Ip6hdr *)v)->ttl;	}	lock(&listlock);	last = nil;	for(l = &first; *l; ){		r = *l; 		if(v && r->seq == seq){			r->rtt = now-r->time;			r->ttl = ttl;			reply(r, v);		} 		if(now-r->time > MINUTE){			*l = r->next;			r->rtt = now-r->time;			if(v)				r->ttl = ttl;			if(r->replied == 0)				lost(r, v);			free(r);		}else{			last = r;			l = &r->next;		}	}	unlock(&listlock);} voidsender(int fd, int msglen, int interval, int n){	int i, extra;	ushort seq;	char buf[64*1024+512];	Icmphdr *icmp;	Req *r; 	srand(time(0));	firstseq = seq = rand(); 	icmp = geticmp(buf);	memset(buf, 0, proto->iphdrsz + ICMP_HDRSIZE);	for(i = proto->iphdrsz + ICMP_HDRSIZE; i < msglen; i++)		buf[i] = i;	icmp->type = proto->echocmd;	icmp->code = 0; 	if(rint != 0 && interval <= 0)		rint = 0;	extra = 0;	for(i = 0; i < n; i++){		if(i != 0){			if(rint != 0)				extra = nrand(interval);			sleep(interval + extra);		}		r = malloc(sizeof *r);		if (r == nil)			continue;		hnputs(icmp->seq, seq);		r->seq = seq;		r->next = nil;		r->replied = 0;		r->time = nsec();	/* avoid early free in reply! */		lock(&listlock);		if(first == nil)			first = r;		else			last->next = r;		last = r;		unlock(&listlock);		r->time = nsec();		if(write(fd, buf, msglen) < msglen){			fprint(2, "%s: write failed: %r\n", argv0);			return;		}		seq++;	}	done = 1;} voidrcvr(int fd, int msglen, int interval, int nmsg){	int i, n, munged;	ushort x;	vlong now;	char err[ERRMAX];	uchar buf[64*1024+512];	Icmphdr *icmp;	Req *r; 	sum = 0;	while(lostmsgs+rcvdmsgs < nmsg){		alarm((nmsg-lostmsgs-rcvdmsgs)*interval+waittime);		n = read(fd, buf, sizeof buf);		alarm(0);		if(n == 0)			strcpy(err, "got eof");		else if(n < 0)			rerrstr(err, sizeof(err));		now = nsec();		if(n <= 0){			print("%s\n", err);			clean(0, now+MINUTE, nil);			if(strstr(err, "interrupted") == nil)				sleep(waittime);			continue;		}		if(n < msglen){			print("bad len %d/%d\n", n, msglen);			continue;		}		icmp = geticmp(buf);		munged = 0;		for(i = proto->iphdrsz + ICMP_HDRSIZE; i < msglen; i++)			if(buf[i] != (uchar)i)				munged++;		if(munged)			print("corrupted reply\n");		x = nhgets(icmp->seq);		if(icmp->type != proto->echoreply || icmp->code != 0) {			print("bad type/code/sequence %d/%d/%d (want %d/%d/%d)\n",				icmp->type, icmp->code, x,				proto->echoreply, 0, x);			continue;		}		clean(x, now, buf);	} 	lock(&listlock);	for(r = first; r; r = r->next)		if(r->replied == 0)			lostmsgs++;	unlock(&listlock); 	if(!quiet && lostmsgs)		print("%d out of %d messages lost\n", lostmsgs,			lostmsgs+rcvdmsgs);} enum{	Maxstring	= 128,	Maxpath		= 256,}; voidmain(int argc, char **argv){	int fd, msglen, interval, nmsg; 	nsec();		/* make sure time file is already open */ 	fmtinstall('V', eipfmt);	fmtinstall('I', eipfmt); 	msglen = interval = 0;	nmsg = MAXMSG;	ARGBEGIN {	case '6':		proto = &v6pr;		break;	case 'a':		addresses = 1;		break;	case 'd':		debug++;		break;	case 'f':		flood = 1;		break;	case 'i':		interval = atoi(EARGF(usage()));		if(interval < 0)			usage();		break;	case 'l':		lostonly++;		break;	case 'n':		nmsg = atoi(EARGF(usage()));		if(nmsg < 0)			usage();		break;	case 'q':		quiet = 1;		break;	case 'r':		rint = 1;		break;	case 's':		msglen = atoi(EARGF(usage()));		break;	case 'w':		waittime = atoi(EARGF(usage()));		if(waittime < 0)			usage();		break;	default:		usage();		break;	} ARGEND; 	if(msglen < proto->iphdrsz + ICMP_HDRSIZE)		msglen = proto->iphdrsz + ICMP_HDRSIZE;	if(msglen < 64)		msglen = 64;	if(msglen >= 64*1024)		msglen = 64*1024-1;	if(interval <= 0 && !flood)		interval = SLEEPMS; 	if(argc < 1)		usage(); 	notify(catch); 	if(strstr(argv[0], "icmpv6!") != nil)		proto = &v6pr;again:	fd = dial(netmkaddr(argv[0], proto->net, "1"), nil, nil, nil);	if(fd < 0){		if(proto == &v4pr){			proto = &v6pr;			goto again;		}		fprint(2, "%s: couldn't dial: %r\n", argv0);		exits("dialing");	}	if (!quiet){		NetConnInfo *nci = getnetconninfo(nil, fd);		print("sending %d %d byte messages %d ms apart to %s\n",			nmsg, msglen, interval, nci != nil? nci->raddr: argv[0]);	} 	switch(rfork(RFPROC|RFMEM|RFFDG)){	case -1:		fprint(2, "%s: can't fork: %r\n", argv0);		exits("forking");	case 0:		rcvr(fd, msglen, interval, nmsg);		exits(0);	default:		sender(fd, msglen, interval, nmsg);		wait();		exits(lostmsgs ? "lost messages" : "");	}} voidreply(Req *r, void *v){	r->rtt /= 1000LL;	sum += r->rtt;	if(!r->replied)		rcvdmsgs++;	if(!quiet && !lostonly)		if(addresses)			(*proto->prreply)(r, v);		else			print("%ud: rtt %lld µs, avg rtt %lld µs, ttl = %d\n",				r->seq - firstseq, r->rtt, sum/rcvdmsgs, r->ttl);	r->replied = 1;} voidlost(Req *r, void *v){	if(!quiet)		if(addresses && v != nil)			(*proto->prlost)(r->seq - firstseq, v);		else			print("lost %ud\n", r->seq - firstseq);	lostmsgs++;}