709 lines · 12.1 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <bio.h>#include <ip.h> enum{	Version=	1,	Pasize=		4, 	/*	 *  definitions that are innately tied to BSD	 */	AF_INET=	2,	AF_UNSPEC=	0, 	/*	 *  Packet types.	 */	Request=	1,	Response=	2,	Traceon=	3,	Traceoff=	4, 	Infinity=	16,	/* infinite hop count */	Maxpacket=	488,	/* largest packet body */};  /* *  network info */typedef struct Rip	Rip;struct Rip{	uchar	family[2];	uchar	port[2];	uchar	addr[Pasize];	uchar	pad[8];	uchar	metric[4];};typedef struct Ripmsg	Ripmsg;struct Ripmsg{	uchar	type;	uchar	vers;	uchar	pad[2];	Rip	rip[1];		/* the rest of the packet consists of routes */}; enum{	Maxroutes=	(Maxpacket-4)/sizeof(Ripmsg),}; /* *  internal route info */enum{	Nroute=	2048,		/* this has to be smaller than what /ip has */	Nhash=	256,		/* routing hash buckets */	Nifc=	16,}; typedef struct Route	Route;struct Route{	Route	*next; 	uchar	dest[Pasize];	uchar	mask[Pasize];	uchar	gate[Pasize];	int	metric;	int	inuse;	long	time;};struct {	Route	route[Nroute];	Route	*hash[Nhash];	int	nroute;	Route	def;	/* default route (immutable by us) */} ralloc; typedef struct Ifc	Ifc;struct Ifc{	int	bcast;	uchar	addr[Pasize];	/* my address */	uchar	mask[Pasize];	/* subnet mask */	uchar	net[Pasize];	/* subnet */	uchar	*cmask;		/* class mask */	uchar	cnet[Pasize];	/* class net */};struct {	Ifc	ifc[Nifc];	int	nifc;} ialloc; /* *  specific networks to broadcast on */typedef struct Bnet Bnet;struct Bnet{	Bnet	*next;	uchar	addr[Pasize];};Bnet	*bnets; int	ripfd;long	now;int	debug;int	readonly;char	routefile[256];char	netdir[256]; int	openport(void);void	readroutes(void);void	readifcs(void);void	considerroute(Route*);void	installroute(Route*);void	removeroute(Route*);uchar	*getmask(uchar*);void	broadcast(void);void	timeoutroutes(void); voidfatal(int syserr, char *fmt, ...){	char buf[ERRMAX], sysbuf[ERRMAX];	va_list arg; 	va_start(arg, fmt);	vseprint(buf, buf+sizeof(buf), fmt, arg);	va_end(arg);	if(syserr) {		errstr(sysbuf, sizeof sysbuf);		fprint(2, "routed: %s: %s\n", buf, sysbuf);	}	else		fprint(2, "routed: %s\n", buf);	exits(buf);} ulongv4parseipmask(uchar *ip, char *p){	ulong x;	uchar v6ip[IPaddrlen]; 	x = parseipmask(v6ip, p);	memmove(ip, v6ip+IPv4off, 4);	return x;} uchar*v4defmask(uchar *ip){	uchar v6ip[IPaddrlen]; 	v4tov6(v6ip, ip);	ip = defmask(v6ip);	return ip+IPv4off;} voidv4maskip(uchar *from, uchar *mask, uchar *to){	int i; 	for(i = 0; i < Pasize; i++)		*to++ = *from++ & *mask++;} voidv6tov4mask(uchar *v4, uchar *v6){	memmove(v4, v6+IPv4off, 4);} #define equivip(a, b) (memcmp((a), (b), Pasize) == 0) voidding(void *u, char *msg){	USED(u); 	if(strstr(msg, "alarm"))		noted(NCONT);	noted(NDFLT);} voidusage(void){	fprint(2, "usage: %s [-bnd] [-x netmtpt]\n", argv0);	exits("usage");} voidmain(int argc, char *argv[]){	int dobroadcast, i, n;	long diff;	char *p;	char buf[2*1024];	uchar raddr[Pasize];	Bnet *bn, **l;	Udphdr *up;	Rip *r;	Ripmsg *m;	Route route;	static long btime; 	setnetmtpt(netdir, sizeof(netdir), nil);	dobroadcast = 0;	ARGBEGIN{	case 'b':		dobroadcast++;		break;	case 'd':		debug++;		break;	case 'n':		readonly++;		break;	case 'x':		p = ARGF();		if(p == nil)			usage();		setnetmtpt(netdir, sizeof(netdir), p);		break;	default:		usage();	}ARGEND 	/* specific broadcast nets */	l = &bnets;	while(argc > 0){		bn = (Bnet*)malloc(sizeof(Bnet));		if(bn == 0)			fatal(1, "out of mem");		v4parseip(bn->addr, *argv);		*l = bn;		l = &bn->next;		argc--;		argv++;		dobroadcast++;	} 	/* command returns */	if(!debug)		switch(rfork(RFNOTEG|RFPROC|RFFDG|RFNOWAIT)) {		case -1:			fatal(1, "fork");		case 0:			break;		default:			exits(0);		}  	fmtinstall('E', eipfmt);	fmtinstall('V', eipfmt); 	snprint(routefile, sizeof(routefile), "%s/iproute", netdir);	snprint(buf, sizeof(buf), "%s/iproute", netdir); 	now = time(0);	readifcs();	readroutes(); 	notify(ding); 	ripfd = openport();	for(;;) {		diff = btime - time(0);		if(diff <= 0){			if(dobroadcast)				broadcast();			timeoutroutes(); 			btime = time(0) + 2*60;			diff = 2*60;		}		alarm(diff*1000);		n = read(ripfd, buf, sizeof(buf));		alarm(0);		if(n <= 0)			continue; 		n = (n - Udphdrsize - 4) / sizeof(Rip);		if(n <= 0)			continue; 		up = (Udphdr*)buf;		m = (Ripmsg*)(buf+Udphdrsize);		if(m->type != Response || m->vers != Version)			continue;		v6tov4(raddr, up->raddr); 		/* ignore our own messages */		for(i = 0; i < ialloc.nifc; i++)			if(equivip(ialloc.ifc[i].addr, raddr))				continue; 		now = time(0);		for(r = m->rip; r < &m->rip[n]; r++){			memmove(route.gate, raddr, Pasize);			memmove(route.mask, getmask(r->addr), Pasize);			v4maskip(r->addr, route.mask, route.dest);			route.metric = nhgetl(r->metric) + 1;			if(route.metric < 1)				continue;			considerroute(&route);		}	}	/* not reached */} intopenport(void){	int ripctl, rip;	char data[128], devdir[40]; 	snprint(data, sizeof(data), "%s/udp!*!rip", netdir);	ripctl = announce(data, devdir);	if(ripctl < 0)		fatal(1, "can't announce");	if(fprint(ripctl, "headers") < 0)		fatal(1, "can't set header mode"); 	sprint(data, "%s/data", devdir);	rip = open(data, ORDWR);	if(rip < 0)		fatal(1, "open udp data");	return rip;} Ipifc *ifcs; voidreadifcs(void){	Ipifc *ifc;	Iplifc *lifc;	Ifc *ip;	Bnet *bn;	Route route;	int i; 	ifcs = readipifc(netdir, ifcs, -1);	i = 0;	for(ifc = ifcs; ifc != nil; ifc = ifc->next){		for(lifc = ifc->lifc; lifc != nil && i < Nifc; lifc = lifc->next){			// ignore any interfaces that aren't v4			if(memcmp(lifc->ip, v4prefix, IPaddrlen-IPv4addrlen) != 0)				continue;			ip = &ialloc.ifc[i++];			v6tov4(ip->addr, lifc->ip);			v6tov4mask(ip->mask, lifc->mask);			v6tov4(ip->net, lifc->net);			ip->cmask = v4defmask(ip->net);			v4maskip(ip->net, ip->cmask, ip->cnet);			ip->bcast = 0; 			/* add as a route */			memmove(route.mask, ip->mask, Pasize);			memmove(route.dest, ip->net, Pasize);			memset(route.gate, 0, Pasize);			route.metric = 0;			considerroute(&route); 			/* mark as broadcast */			if(bnets == 0)				ip->bcast = 1;			else for(bn = bnets; bn; bn = bn->next)				if(memcmp(bn->addr, ip->net, Pasize) == 0){					ip->bcast = 1;					break;				}		}	}	ialloc.nifc = i;} voidreadroutes(void){	int n;	char *p;	Biobuf *b;	char *f[6];	Route route; 	b = Bopen(routefile, OREAD);	if(b == 0)		return;	while(p = Brdline(b, '\n')){		p[Blinelen(b)-1] = 0;		n = getfields(p, f, 6, 1, " \t");		if(n < 5)			continue;		v4parseip(route.dest, f[0]);		v4parseipmask(route.mask, f[1]);		v4parseip(route.gate, f[2]);		route.metric = Infinity;		if(equivip(route.dest, ralloc.def.dest)		&& equivip(route.mask, ralloc.def.mask))			memmove(ralloc.def.gate, route.gate, Pasize);		else if(!equivip(route.dest, route.gate) && strchr(f[3], 'i') == 0)			considerroute(&route);	}	Bterm(b);} /* *  route's hashed by net, not subnet */ulongrhash(uchar *d){	ulong h;	uchar net[Pasize]; 	v4maskip(d, v4defmask(d), net);	h = net[0] + net[1] + net[2];	return h % Nhash;} /* *  consider installing a route.  Do so only if it is better than what *  we have. */voidconsiderroute(Route *r){	ulong h;	Route *hp; 	if(debug)		fprint(2, "consider %16V & %16V -> %16V %d\n", r->dest, r->mask, r->gate, r->metric); 	r->next = 0;	r->time = now;	r->inuse = 1; 	/* don't allow our default route to be highjacked */	if(equivip(r->dest, ralloc.def.dest) || equivip(r->mask, ralloc.def.mask))		return; 	h = rhash(r->dest);	for(hp = ralloc.hash[h]; hp; hp = hp->next){		if(equivip(hp->dest, r->dest)){			/*			 *  found a match, replace if better (or much newer)			 */			if(r->metric < hp->metric || now-hp->time > 5*60){				removeroute(hp);				memmove(hp->mask, r->mask, Pasize);				memmove(hp->gate, r->gate, Pasize);				hp->metric = r->metric;				installroute(hp);			}			if(equivip(hp->gate, r->gate))				hp->time = now;			return;		}	} 	/*	 *  no match, look for space	 */	for(hp = ralloc.route; hp < &ralloc.route[Nroute]; hp++)		if(hp->inuse == 0)			break; 	if(hp == 0)		fatal(0, "no more routes"); 	memmove(hp, r, sizeof(Route));	hp->next = ralloc.hash[h];	ralloc.hash[h] = hp;	installroute(hp);} voidremoveroute(Route *r){	int fd; 	fd = open(routefile, ORDWR);	if(fd < 0){		fprint(2, "can't open oproute\n");		return;	}	if(!readonly)		fprint(fd, "delete %V", r->dest);

Lines 1–500 of 709.