Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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*
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
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
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
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
195
usage(void)
196
{
197
	fprint(2, "usage: %s [-bnd] [-x netmtpt]\n", argv0);
198
	exits("usage");
199
}
200
 
201
void
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
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
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
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
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
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
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
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
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*
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
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
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
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
}