620 lines · 10.9 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* 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; static char *network, *target; 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);} static uchar loopbacknet[IPaddrlen] = {	0, 0, 0, 0,	0, 0, 0, 0,	0, 0, 0xff, 0xff,	127, 0, 0, 0};static uchar loopbackmask[IPaddrlen] = {	0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff,	0xff, 0, 0, 0}; /* * find first ip addr suitable for proto and * that isn't the friggin loopback address. * deprecate link-local and multicast addresses. */static intmyipvnaddr(uchar *ip, Proto *proto, char *net){	int ipisv4, wantv4;	Ipifc *nifc;	Iplifc *lifc;	uchar mynet[IPaddrlen], linklocal[IPaddrlen];	static Ipifc *ifc; 	ipmove(linklocal, IPnoaddr);	wantv4 = proto->version == 4;	ifc = readipifc(net, ifc, -1);	for(nifc = ifc; nifc; nifc = nifc->next)		for(lifc = nifc->lifc; lifc; lifc = lifc->next){			maskip(lifc->ip, loopbackmask, mynet);			if(ipcmp(mynet, loopbacknet) == 0)				continue;			if(ISIPV6MCAST(lifc->ip) || ISIPV6LINKLOCAL(lifc->ip)) {				ipmove(linklocal, lifc->ip);				continue;			}			ipisv4 = isv4(lifc->ip) != 0;			if(ipcmp(lifc->ip, IPnoaddr) != 0 && wantv4 == ipisv4){				ipmove(ip, lifc->ip);				return 0;			}		}	/* no global unicast addrs found, fall back to link-local, if any */	ipmove(ip, linklocal);	return ipcmp(ip, IPnoaddr) == 0? -1: 0;} voidsender(int fd, int msglen, int interval, int n){	int i, extra;	ushort seq;	char buf[64*1024+512];	uchar me[IPaddrlen], mev4[IPv4addrlen];	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; 	/* arguably the kernel should fill in the right src addr. */	myipvnaddr(me, proto, network);	if (proto->version == 4) {		v6tov4(mev4, me);		memmove(((Ip4hdr *)buf)->src, mev4, IPv4addrlen);	} else		ipmove(((Ip6hdr *)buf)->src, me);	if (addresses)		print("\t%I -> %s\n", me, target); 	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;	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);		now = nsec();		if(n <= 0){	/* read interrupted - time to go */			clean(0, now+MINUTE, nil);			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);} static intisdottedquad(char *name){	int dot = 0, digit = 0; 	for (; *name != '\0'; name++)		if (*name == '.')			dot++;		else if (isdigit(*name))			digit++;		else			return 0;	return dot && digit;} static intisv6lit(char *name){	int colon = 0, hex = 0; 	for (; *name != '\0'; name++)		if (*name == ':')			colon++;		else if (isxdigit(*name))			hex++;		else			return 0;	return colon;} /* from /sys/src/libc/9sys/dial.c */ enum{	Maxstring	= 128,	Maxpath		= 256,}; typedef struct DS DS;struct DS {	/* dist string */	char	buf[Maxstring];	char	*netdir;	char	*proto;	char	*rem; 	/* other args */	char	*local;	char	*dir;	int	*cfdp;}; /* *  parse a dial string */static void_dial_string_parse(char *str, DS *ds){	char *p, *p2; 	strncpy(ds->buf, str, Maxstring);	ds->buf[Maxstring-1] = 0; 	p = strchr(ds->buf, '!');	if(p == 0) {		ds->netdir = 0;		ds->proto = "net";		ds->rem = ds->buf;	} else {		if(*ds->buf != '/' && *ds->buf != '#'){			ds->netdir = 0;			ds->proto = ds->buf;		} else {			for(p2 = p; *p2 != '/'; p2--)				;			*p2++ = 0;			ds->netdir = ds->buf;			ds->proto = p2;		}		*p = 0;		ds->rem = p + 1;	}} /* end excerpt from /sys/src/libc/9sys/dial.c */ /* side effect: sets network & target */static intisv4name(char *name){	int r = 1;	char *root, *ip, *pr;	DS ds; 	_dial_string_parse(name, &ds); 	/* cope with leading /net.alt/icmp! and the like */	root = nil;	if (ds.netdir != nil) {		pr = strrchr(ds.netdir, '/');		if (pr == nil)			pr = ds.netdir;		else {			*pr++ = '\0';			root = ds.netdir;			network = strdup(root);		}		if (strcmp(pr, v4pr.net) == 0)			return 1;		if (strcmp(pr, v6pr.net) == 0)			return 0;	} 	/* if it's a literal, it's obvious from syntax which proto it is */	free(target);	target = strdup(ds.rem);	if (isdottedquad(ds.rem))		return 1;	else if (isv6lit(ds.rem))		return 0; 	/* map name to ip and look at its syntax */	ip = csgetvalue(root, "sys", ds.rem, "ip", nil);	if (ip == nil)		ip = csgetvalue(root, "dom", ds.rem, "ip", nil);	if (ip == nil)		ip = csgetvalue(root, "sys", ds.rem, "ipv6", nil);	if (ip == nil)		ip = csgetvalue(root, "dom", ds.rem, "ipv6", nil);	if (ip != nil)		r = isv4name(ip);	free(ip);	return r;} voidmain(int argc, char **argv){	int fd, msglen, interval, nmsg;	char *ds;

Lines 1–500 of 620.