xref: /plan9/sys/src/cmd/ip/rip.c (revision f27a9a5a0b699d2f44893d9491ecc2336a1fbc19)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ip.h>
5 
6 enum
7 {
8 	Version=	1,
9 	Pasize=		4,
10 
11 	/*
12 	 *  definitions that are innately tied to BSD
13 	 */
14 	AF_INET=	2,
15 	AF_UNSPEC=	0,
16 
17 	/*
18 	 *  Packet types.
19 	 */
20 	Request=	1,
21 	Response=	2,
22 	Traceon=	3,
23 	Traceoff=	4,
24 
25 	Infinity=	16,	/* infinite hop count */
26 	Maxpacket=	488,	/* largest packet body */
27 };
28 
29 
30 /*
31  *  network info
32  */
33 typedef struct Rip	Rip;
34 struct Rip
35 {
36 	uchar	family[2];
37 	uchar	port[2];
38 	uchar	addr[Pasize];
39 	uchar	pad[8];
40 	uchar	metric[4];
41 };
42 typedef struct Ripmsg	Ripmsg;
43 struct Ripmsg
44 {
45 	uchar	type;
46 	uchar	vers;
47 	uchar	pad[2];
48 	Rip	rip[1];		/* the rest of the packet consists of routes */
49 };
50 
51 enum
52 {
53 	Maxroutes=	(Maxpacket-4)/sizeof(Ripmsg),
54 };
55 
56 /*
57  *  internal route info
58  */
59 enum
60 {
61 	Nroute=	2048,		/* this has to be smaller than what /ip has */
62 	Nhash=	256,		/* routing hash buckets */
63 	Nifc=	16,
64 };
65 
66 typedef struct Route	Route;
67 struct Route
68 {
69 	Route	*next;
70 
71 	uchar	dest[Pasize];
72 	uchar	mask[Pasize];
73 	uchar	gate[Pasize];
74 	int	metric;
75 	int	inuse;
76 	long	time;
77 };
78 struct {
79 	Route	route[Nroute];
80 	Route	*hash[Nhash];
81 	int	nroute;
82 	Route	def;	/* default route (immutable by us) */
83 } ralloc;
84 
85 typedef struct Ifc	Ifc;
86 struct Ifc
87 {
88 	int	bcast;
89 	uchar	addr[Pasize];	/* my address */
90 	uchar	mask[Pasize];	/* subnet mask */
91 	uchar	net[Pasize];	/* subnet */
92 	uchar	*cmask;		/* class mask */
93 	uchar	cnet[Pasize];	/* class net */
94 };
95 struct {
96 	Ifc	ifc[Nifc];
97 	int	nifc;
98 } ialloc;
99 
100 /*
101  *  specific networks to broadcast on
102  */
103 typedef struct Bnet Bnet;
104 struct Bnet
105 {
106 	Bnet	*next;
107 	uchar	addr[Pasize];
108 };
109 Bnet	*bnets;
110 
111 int	ripfd;
112 long	now;
113 int	debug;
114 int	readonly;
115 char	routefile[256];
116 char	netdir[256];
117 
118 int	openport(void);
119 void	readroutes(void);
120 void	readifcs(void);
121 void	considerroute(Route*);
122 void	installroute(Route*);
123 void	removeroute(Route*);
124 uchar	*getmask(uchar*);
125 void	broadcast(void);
126 void	timeoutroutes(void);
127 
128 void
fatal(int syserr,char * fmt,...)129 fatal(int syserr, char *fmt, ...)
130 {
131 	char buf[ERRMAX], sysbuf[ERRMAX];
132 	va_list arg;
133 
134 	va_start(arg, fmt);
135 	vseprint(buf, buf+sizeof(buf), fmt, arg);
136 	va_end(arg);
137 	if(syserr) {
138 		errstr(sysbuf, sizeof sysbuf);
139 		fprint(2, "routed: %s: %s\n", buf, sysbuf);
140 	}
141 	else
142 		fprint(2, "routed: %s\n", buf);
143 	exits(buf);
144 }
145 
146 ulong
v4parseipmask(uchar * ip,char * p)147 v4parseipmask(uchar *ip, char *p)
148 {
149 	ulong x;
150 	uchar v6ip[IPaddrlen];
151 
152 	x = parseipmask(v6ip, p);
153 	memmove(ip, v6ip+IPv4off, 4);
154 	return x;
155 }
156 
157 uchar*
v4defmask(uchar * ip)158 v4defmask(uchar *ip)
159 {
160 	uchar v6ip[IPaddrlen];
161 
162 	v4tov6(v6ip, ip);
163 	ip = defmask(v6ip);
164 	return ip+IPv4off;
165 }
166 
167 void
v4maskip(uchar * from,uchar * mask,uchar * to)168 v4maskip(uchar *from, uchar *mask, uchar *to)
169 {
170 	int i;
171 
172 	for(i = 0; i < Pasize; i++)
173 		*to++ = *from++ & *mask++;
174 }
175 
176 void
v6tov4mask(uchar * v4,uchar * v6)177 v6tov4mask(uchar *v4, uchar *v6)
178 {
179 	memmove(v4, v6+IPv4off, 4);
180 }
181 
182 #define equivip(a, b) (memcmp((a), (b), Pasize) == 0)
183 
184 void
ding(void * u,char * msg)185 ding(void *u, char *msg)
186 {
187 	USED(u);
188 
189 	if(strstr(msg, "alarm"))
190 		noted(NCONT);
191 	noted(NDFLT);
192 }
193 
194 void
usage(void)195 usage(void)
196 {
197 	fprint(2, "usage: %s [-bnd] [-x netmtpt]\n", argv0);
198 	exits("usage");
199 }
200 
201 void
main(int argc,char * argv[])202 main(int argc, char *argv[])
203 {
204 	int dobroadcast, i, n;
205 	long diff;
206 	char *p;
207 	char buf[2*1024];
208 	uchar raddr[Pasize];
209 	Bnet *bn, **l;
210 	Udphdr *up;
211 	Rip *r;
212 	Ripmsg *m;
213 	Route route;
214 	static long btime;
215 
216 	setnetmtpt(netdir, sizeof(netdir), nil);
217 	dobroadcast = 0;
218 	ARGBEGIN{
219 	case 'b':
220 		dobroadcast++;
221 		break;
222 	case 'd':
223 		debug++;
224 		break;
225 	case 'n':
226 		readonly++;
227 		break;
228 	case 'x':
229 		p = ARGF();
230 		if(p == nil)
231 			usage();
232 		setnetmtpt(netdir, sizeof(netdir), p);
233 		break;
234 	default:
235 		usage();
236 	}ARGEND
237 
238 	/* specific broadcast nets */
239 	l = &bnets;
240 	while(argc > 0){
241 		bn = (Bnet*)malloc(sizeof(Bnet));
242 		if(bn == 0)
243 			fatal(1, "out of mem");
244 		v4parseip(bn->addr, *argv);
245 		*l = bn;
246 		l = &bn->next;
247 		argc--;
248 		argv++;
249 		dobroadcast++;
250 	}
251 
252 	/* command returns */
253 	if(!debug)
254 		switch(rfork(RFNOTEG|RFPROC|RFFDG|RFNOWAIT)) {
255 		case -1:
256 			fatal(1, "fork");
257 		case 0:
258 			break;
259 		default:
260 			exits(0);
261 		}
262 
263 
264 	fmtinstall('E', eipfmt);
265 	fmtinstall('V', eipfmt);
266 
267 	snprint(routefile, sizeof(routefile), "%s/iproute", netdir);
268 	snprint(buf, sizeof(buf), "%s/iproute", netdir);
269 
270 	now = time(0);
271 	readifcs();
272 	readroutes();
273 
274 	notify(ding);
275 
276 	ripfd = openport();
277 	for(;;) {
278 		diff = btime - time(0);
279 		if(diff <= 0){
280 			if(dobroadcast)
281 				broadcast();
282 			timeoutroutes();
283 
284 			btime = time(0) + 2*60;
285 			diff = 2*60;
286 		}
287 		alarm(diff*1000);
288 		n = read(ripfd, buf, sizeof(buf));
289 		alarm(0);
290 		if(n <= 0)
291 			continue;
292 
293 		n = (n - Udphdrsize - 4) / sizeof(Rip);
294 		if(n <= 0)
295 			continue;
296 
297 		up = (Udphdr*)buf;
298 		m = (Ripmsg*)(buf+Udphdrsize);
299 		if(m->type != Response || m->vers != Version)
300 			continue;
301 		v6tov4(raddr, up->raddr);
302 
303 		/* ignore our own messages */
304 		for(i = 0; i < ialloc.nifc; i++)
305 			if(equivip(ialloc.ifc[i].addr, raddr))
306 				continue;
307 
308 		now = time(0);
309 		for(r = m->rip; r < &m->rip[n]; r++){
310 			memmove(route.gate, raddr, Pasize);
311 			memmove(route.mask, getmask(r->addr), Pasize);
312 			v4maskip(r->addr, route.mask, route.dest);
313 			route.metric = nhgetl(r->metric) + 1;
314 			if(route.metric < 1)
315 				continue;
316 			considerroute(&route);
317 		}
318 	}
319 	/* not reached */
320 }
321 
322 int
openport(void)323 openport(void)
324 {
325 	int ripctl, rip;
326 	char data[128], devdir[40];
327 
328 	snprint(data, sizeof(data), "%s/udp!*!rip", netdir);
329 	ripctl = announce(data, devdir);
330 	if(ripctl < 0)
331 		fatal(1, "can't announce");
332 	if(fprint(ripctl, "headers") < 0)
333 		fatal(1, "can't set header mode");
334 
335 	sprint(data, "%s/data", devdir);
336 	rip = open(data, ORDWR);
337 	if(rip < 0)
338 		fatal(1, "open udp data");
339 	return rip;
340 }
341 
342 Ipifc *ifcs;
343 
344 void
readifcs(void)345 readifcs(void)
346 {
347 	Ipifc *ifc;
348 	Iplifc *lifc;
349 	Ifc *ip;
350 	Bnet *bn;
351 	Route route;
352 	int i;
353 
354 	ifcs = readipifc(netdir, ifcs, -1);
355 	i = 0;
356 	for(ifc = ifcs; ifc != nil; ifc = ifc->next){
357 		for(lifc = ifc->lifc; lifc != nil && i < Nifc; lifc = lifc->next){
358 			// ignore any interfaces that aren't v4
359 			if(memcmp(lifc->ip, v4prefix, IPaddrlen-IPv4addrlen) != 0)
360 				continue;
361 			ip = &ialloc.ifc[i++];
362 			v6tov4(ip->addr, lifc->ip);
363 			v6tov4mask(ip->mask, lifc->mask);
364 			v6tov4(ip->net, lifc->net);
365 			ip->cmask = v4defmask(ip->net);
366 			v4maskip(ip->net, ip->cmask, ip->cnet);
367 			ip->bcast = 0;
368 
369 			/* add as a route */
370 			memmove(route.mask, ip->mask, Pasize);
371 			memmove(route.dest, ip->net, Pasize);
372 			memset(route.gate, 0, Pasize);
373 			route.metric = 0;
374 			considerroute(&route);
375 
376 			/* mark as broadcast */
377 			if(bnets == 0)
378 				ip->bcast = 1;
379 			else for(bn = bnets; bn; bn = bn->next)
380 				if(memcmp(bn->addr, ip->net, Pasize) == 0){
381 					ip->bcast = 1;
382 					break;
383 				}
384 		}
385 	}
386 	ialloc.nifc = i;
387 }
388 
389 void
readroutes(void)390 readroutes(void)
391 {
392 	int n;
393 	char *p;
394 	Biobuf *b;
395 	char *f[6];
396 	Route route;
397 
398 	b = Bopen(routefile, OREAD);
399 	if(b == 0)
400 		return;
401 	while(p = Brdline(b, '\n')){
402 		p[Blinelen(b)-1] = 0;
403 		n = getfields(p, f, 6, 1, " \t");
404 		if(n < 5)
405 			continue;
406 		v4parseip(route.dest, f[0]);
407 		v4parseipmask(route.mask, f[1]);
408 		v4parseip(route.gate, f[2]);
409 		route.metric = Infinity;
410 		if(equivip(route.dest, ralloc.def.dest)
411 		&& equivip(route.mask, ralloc.def.mask))
412 			memmove(ralloc.def.gate, route.gate, Pasize);
413 		else if(!equivip(route.dest, route.gate) && strchr(f[3], 'i') == 0)
414 			considerroute(&route);
415 	}
416 	Bterm(b);
417 }
418 
419 /*
420  *  route's hashed by net, not subnet
421  */
422 ulong
rhash(uchar * d)423 rhash(uchar *d)
424 {
425 	ulong h;
426 	uchar net[Pasize];
427 
428 	v4maskip(d, v4defmask(d), net);
429 	h = net[0] + net[1] + net[2];
430 	return h % Nhash;
431 }
432 
433 /*
434  *  consider installing a route.  Do so only if it is better than what
435  *  we have.
436  */
437 void
considerroute(Route * r)438 considerroute(Route *r)
439 {
440 	ulong h;
441 	Route *hp;
442 
443 	if(debug)
444 		fprint(2, "consider %16V & %16V -> %16V %d\n", r->dest, r->mask, r->gate, r->metric);
445 
446 	r->next = 0;
447 	r->time = now;
448 	r->inuse = 1;
449 
450 	/* don't allow our default route to be highjacked */
451 	if(equivip(r->dest, ralloc.def.dest) || equivip(r->mask, ralloc.def.mask))
452 		return;
453 
454 	h = rhash(r->dest);
455 	for(hp = ralloc.hash[h]; hp; hp = hp->next){
456 		if(equivip(hp->dest, r->dest)){
457 			/*
458 			 *  found a match, replace if better (or much newer)
459 			 */
460 			if(r->metric < hp->metric || now-hp->time > 5*60){
461 				removeroute(hp);
462 				memmove(hp->mask, r->mask, Pasize);
463 				memmove(hp->gate, r->gate, Pasize);
464 				hp->metric = r->metric;
465 				installroute(hp);
466 			}
467 			if(equivip(hp->gate, r->gate))
468 				hp->time = now;
469 			return;
470 		}
471 	}
472 
473 	/*
474 	 *  no match, look for space
475 	 */
476 	for(hp = ralloc.route; hp < &ralloc.route[Nroute]; hp++)
477 		if(hp->inuse == 0)
478 			break;
479 
480 	if(hp == 0)
481 		fatal(0, "no more routes");
482 
483 	memmove(hp, r, sizeof(Route));
484 	hp->next = ralloc.hash[h];
485 	ralloc.hash[h] = hp;
486 	installroute(hp);
487 }
488 
489 void
removeroute(Route * r)490 removeroute(Route *r)
491 {
492 	int fd;
493 
494 	fd = open(routefile, ORDWR);
495 	if(fd < 0){
496 		fprint(2, "can't open oproute\n");
497 		return;
498 	}
499 	if(!readonly)
500 		fprint(fd, "delete %V", r->dest);
501 	if(debug)
502 		fprint(2, "removeroute %V\n", r->dest);
503 	close(fd);
504 }
505 
506 /*
507  *  pass a route to the kernel or /ip.  Don't bother if it is just the default
508  *  gateway.
509  */
510 void
installroute(Route * r)511 installroute(Route *r)
512 {
513 	int fd;
514 	ulong h;
515 	Route *hp;
516 	uchar net[Pasize];
517 
518 	/*
519 	 *  don't install routes whose gateway is 00000000
520 	 */
521 	if(equivip(r->gate, ralloc.def.dest))
522 		return;
523 
524 	fd = open(routefile, ORDWR);
525 	if(fd < 0){
526 		fprint(2, "can't open oproute\n");
527 		return;
528 	}
529 	h = rhash(r->dest);
530 
531 	/*
532 	 *  if the gateway is the same as the default gateway
533 	 *  we may be able to avoid a entry in the kernel
534 	 */
535 	if(equivip(r->gate, ralloc.def.gate)){
536 		/*
537 		 *  look for a less specific match
538 		 */
539 		for(hp = ralloc.hash[h]; hp; hp = hp->next){
540 			v4maskip(hp->mask, r->dest, net);
541 			if(equivip(net, hp->dest) && !equivip(hp->gate, ralloc.def.gate))
542 				break;
543 		}
544 		/*
545 		 *  if no less specific match, just use the default
546 		 */
547 		if(hp == 0){
548 			if(!readonly)
549 				fprint(fd, "delete %V", r->dest);
550 			if(debug)
551 				fprint(2, "delete %V\n", r->dest);
552 			close(fd);
553 			return;
554 		}
555 	}
556 	if(!readonly)
557 		fprint(fd, "add %V %V %V", r->dest, r->mask, r->gate);
558 	if(debug)
559 		fprint(2, "add %V & %V -> %V\n", r->dest, r->mask, r->gate);
560 	close(fd);
561 }
562 
563 /*
564  *  return true of dest is on net
565  */
566 int
onnet(uchar * dest,uchar * net,uchar * netmask)567 onnet(uchar *dest, uchar *net, uchar *netmask)
568 {
569 	uchar dnet[Pasize];
570 
571 	v4maskip(dest, netmask, dnet);
572 	return equivip(dnet, net);
573 }
574 
575 /*
576  *  figure out what mask to use, if we have a direct connected network
577  *  with the same class net use its subnet mask.
578  */
579 uchar*
getmask(uchar * dest)580 getmask(uchar *dest)
581 {
582 	int i;
583 	Ifc *ip;
584 	ulong mask, nmask;
585 	uchar *m;
586 
587 	m = 0;
588 	mask = 0xffffffff;
589 	for(i = 0; i < ialloc.nifc; i++){
590 		ip = &ialloc.ifc[i];
591 		if(onnet(dest, ip->cnet, ip->cmask)){
592 			nmask = nhgetl(ip->mask);
593 			if(nmask < mask){
594 				mask = nmask;
595 				m = ip->mask;
596 			}
597 		}
598 	}
599 
600 	if(m == 0)
601 		m = v4defmask(dest);
602 	return m;
603 }
604 
605 /*
606  *  broadcast routes onto all networks
607  */
608 void
sendto(Ifc * ip)609 sendto(Ifc *ip)
610 {
611 	int h, n;
612 	uchar raddr[Pasize], mbuf[Udphdrsize+512];
613 	Ripmsg *m;
614 	Route *r;
615 	Udphdr *u;
616 
617 	u = (Udphdr*)mbuf;
618 	for(n = 0; n < Pasize; n++)
619 		raddr[n] = ip->net[n] | ~(ip->mask[n]);
620 	v4tov6(u->raddr, raddr);
621 	hnputs(u->rport, 520);
622 	m = (Ripmsg*)(mbuf+Udphdrsize);
623 	m->type = Response;
624 	m->vers = Version;
625 	if(debug)
626 		fprint(2, "to %V\n", u->raddr);
627 
628 	n = 0;
629 	for(h = 0; h < Nhash; h++){
630 		for(r = ralloc.hash[h]; r; r = r->next){
631 			/*
632 			 *  don't send any route back to the net
633 			 *  it came from
634 			 */
635 			if(onnet(r->gate, ip->net, ip->mask))
636 				continue;
637 
638 			/*
639 			 *  don't tell a network about itself
640 			 */
641 			if(equivip(r->dest, ip->net))
642 				continue;
643 
644 			/*
645 			 *  don't tell nets about other net's subnets
646 			 */
647 			if(!equivip(r->mask, v4defmask(r->dest))
648 			&& !equivip(ip->cmask, v4defmask(r->dest)))
649 				continue;
650 
651 			memset(&m->rip[n], 0, sizeof(m->rip[n]));
652 			memmove(m->rip[n].addr, r->dest, Pasize);
653 			if(r->metric < 1)
654 				hnputl(m->rip[n].metric, 1);
655 			else
656 				hnputl(m->rip[n].metric, r->metric);
657 			hnputs(m->rip[n].family, AF_INET);
658 
659 			if(debug)
660 				fprint(2, " %16V & %16V -> %16V %2d\n",
661 					r->dest, r->mask, r->gate, r->metric);
662 
663 			if(++n == Maxroutes && !readonly){
664 				write(ripfd, mbuf, Udphdrsize + 4 + n*20);
665 				n = 0;
666 			}
667 		}
668 	}
669 
670 	if(n && !readonly)
671 		write(ripfd, mbuf, Udphdrsize+4+n*20);
672 }
673 void
broadcast(void)674 broadcast(void)
675 {
676 	int i;
677 
678 	readifcs();
679 	for(i = 0; i < ialloc.nifc; i++){
680 		if(ialloc.ifc[i].bcast)
681 			sendto(&ialloc.ifc[i]);
682 	}
683 }
684 
685 /*
686  *  timeout any routes that haven't been refreshed and aren't wired
687  */
688 void
timeoutroutes(void)689 timeoutroutes(void)
690 {
691 	int h;
692 	long now;
693 	Route *r, **l;
694 
695 	now = time(0);
696 
697 	for(h = 0; h < Nhash; h++){
698 		l = &ralloc.hash[h];
699 		for(r = *l; r; r = *l){
700 			if(r->metric < Infinity && now - r->time > 10*60){
701 				removeroute(r);
702 				r->inuse = 0;
703 				*l = r->next;
704 				continue;
705 			}
706 			l = &r->next;
707 		}
708 	}
709 }
710