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