xref: /plan9-contrib/sys/src/9k/ip/iproute.c (revision 55ba833bbc29e52fa60df7522a0e5f7350ace33d)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"../port/error.h"
7 
8 #include	"ip.h"
9 
10 static void	walkadd(Fs*, Route**, Route*);
11 static void	addnode(Fs*, Route**, Route*);
12 static void	calcd(Route*);
13 
14 /* these are used for all instances of IP */
15 static Route*	v4freelist;
16 static Route*	v6freelist;
17 static RWlock	routelock;
18 static ulong	v4routegeneration, v6routegeneration;
19 
20 static void
freeroute(Route * r)21 freeroute(Route *r)
22 {
23 	Route **l;
24 
25 	r->left = nil;
26 	r->right = nil;
27 	if(r->type & Rv4)
28 		l = &v4freelist;
29 	else
30 		l = &v6freelist;
31 	r->mid = *l;
32 	*l = r;
33 }
34 
35 static Route*
allocroute(int type)36 allocroute(int type)
37 {
38 	Route *r;
39 	int n;
40 	Route **l;
41 
42 	if(type & Rv4){
43 		n = sizeof(RouteTree) + sizeof(V4route);
44 		l = &v4freelist;
45 	} else {
46 		n = sizeof(RouteTree) + sizeof(V6route);
47 		l = &v6freelist;
48 	}
49 
50 	r = *l;
51 	if(r != nil){
52 		*l = r->mid;
53 	} else {
54 		r = malloc(n);
55 		if(r == nil)
56 			panic("out of routing nodes");
57 	}
58 	memset(r, 0, n);
59 	r->type = type;
60 	r->ifc = nil;
61 	r->ref = 1;
62 
63 	return r;
64 }
65 
66 static void
addqueue(Route ** q,Route * r)67 addqueue(Route **q, Route *r)
68 {
69 	Route *l;
70 
71 	if(r == nil)
72 		return;
73 
74 	l = allocroute(r->type);
75 	l->mid = *q;
76 	*q = l;
77 	l->left = r;
78 }
79 
80 /*
81  *   compare 2 v6 addresses
82  */
83 static int
lcmp(ulong * a,ulong * b)84 lcmp(ulong *a, ulong *b)
85 {
86 	int i;
87 
88 	for(i = 0; i < IPllen; i++){
89 		if(a[i] > b[i])
90 			return 1;
91 		if(a[i] < b[i])
92 			return -1;
93 	}
94 	return 0;
95 }
96 
97 /*
98  *  compare 2 v4 or v6 ranges
99  */
100 enum
101 {
102 	Rpreceeds,
103 	Rfollows,
104 	Requals,
105 	Rcontains,
106 	Rcontained,
107 };
108 
109 static int
rangecompare(Route * a,Route * b)110 rangecompare(Route *a, Route *b)
111 {
112 	if(a->type & Rv4){
113 		if(a->v4.endaddress < b->v4.address)
114 			return Rpreceeds;
115 
116 		if(a->v4.address > b->v4.endaddress)
117 			return Rfollows;
118 
119 		if(a->v4.address <= b->v4.address
120 		&& a->v4.endaddress >= b->v4.endaddress){
121 			if(a->v4.address == b->v4.address
122 			&& a->v4.endaddress == b->v4.endaddress)
123 				return Requals;
124 			return Rcontains;
125 		}
126 		return Rcontained;
127 	}
128 
129 	if(lcmp(a->v6.endaddress, b->v6.address) < 0)
130 		return Rpreceeds;
131 
132 	if(lcmp(a->v6.address, b->v6.endaddress) > 0)
133 		return Rfollows;
134 
135 	if(lcmp(a->v6.address, b->v6.address) <= 0
136 	&& lcmp(a->v6.endaddress, b->v6.endaddress) >= 0){
137 		if(lcmp(a->v6.address, b->v6.address) == 0
138 		&& lcmp(a->v6.endaddress, b->v6.endaddress) == 0)
139 				return Requals;
140 		return Rcontains;
141 	}
142 
143 	return Rcontained;
144 }
145 
146 static void
copygate(Route * old,Route * new)147 copygate(Route *old, Route *new)
148 {
149 	if(new->type & Rv4)
150 		memmove(old->v4.gate, new->v4.gate, IPv4addrlen);
151 	else
152 		memmove(old->v6.gate, new->v6.gate, IPaddrlen);
153 }
154 
155 /*
156  *  walk down a tree adding nodes back in
157  */
158 static void
walkadd(Fs * f,Route ** root,Route * p)159 walkadd(Fs *f, Route **root, Route *p)
160 {
161 	Route *l, *r;
162 
163 	l = p->left;
164 	r = p->right;
165 	p->left = 0;
166 	p->right = 0;
167 	addnode(f, root, p);
168 	if(l)
169 		walkadd(f, root, l);
170 	if(r)
171 		walkadd(f, root, r);
172 }
173 
174 /*
175  *  calculate depth
176  */
177 static void
calcd(Route * p)178 calcd(Route *p)
179 {
180 	Route *q;
181 	int d;
182 
183 	if(p) {
184 		d = 0;
185 		q = p->left;
186 		if(q)
187 			d = q->depth;
188 		q = p->right;
189 		if(q && q->depth > d)
190 			d = q->depth;
191 		q = p->mid;
192 		if(q && q->depth > d)
193 			d = q->depth;
194 		p->depth = d+1;
195 	}
196 }
197 
198 /*
199  *  balance the tree at the current node
200  */
201 static void
balancetree(Route ** cur)202 balancetree(Route **cur)
203 {
204 	Route *p, *l, *r;
205 	int dl, dr;
206 
207 	/*
208 	 * if left and right are
209 	 * too out of balance,
210 	 * rotate tree node
211 	 */
212 	p = *cur;
213 	dl = 0; if(l = p->left) dl = l->depth;
214 	dr = 0; if(r = p->right) dr = r->depth;
215 
216 	if(dl > dr+1) {
217 		p->left = l->right;
218 		l->right = p;
219 		*cur = l;
220 		calcd(p);
221 		calcd(l);
222 	} else
223 	if(dr > dl+1) {
224 		p->right = r->left;
225 		r->left = p;
226 		*cur = r;
227 		calcd(p);
228 		calcd(r);
229 	} else
230 		calcd(p);
231 }
232 
233 /*
234  *  add a new node to the tree
235  */
236 static void
addnode(Fs * f,Route ** cur,Route * new)237 addnode(Fs *f, Route **cur, Route *new)
238 {
239 	Route *p;
240 
241 	p = *cur;
242 	if(p == 0) {
243 		*cur = new;
244 		new->depth = 1;
245 		return;
246 	}
247 
248 	switch(rangecompare(new, p)){
249 	case Rpreceeds:
250 		addnode(f, &p->left, new);
251 		break;
252 	case Rfollows:
253 		addnode(f, &p->right, new);
254 		break;
255 	case Rcontains:
256 		/*
257 		 *  if new node is superset
258 		 *  of tree node,
259 		 *  replace tree node and
260 		 *  queue tree node to be
261 		 *  merged into root.
262 		 */
263 		*cur = new;
264 		new->depth = 1;
265 		addqueue(&f->queue, p);
266 		break;
267 	case Requals:
268 		/*
269 		 *  supercede the old entry if the old one isn't
270 		 *  a local interface.
271 		 */
272 		if((p->type & Rifc) == 0){
273 			p->type = new->type;
274 			p->ifcid = -1;
275 			copygate(p, new);
276 		} else if(new->type & Rifc)
277 			p->ref++;
278 		freeroute(new);
279 		break;
280 	case Rcontained:
281 		addnode(f, &p->mid, new);
282 		break;
283 	}
284 
285 	balancetree(cur);
286 }
287 
288 #define	V4H(a)	((a&0x07ffffff)>>(32-Lroot-5))
289 
290 void
v4addroute(Fs * f,char * tag,uchar * a,uchar * mask,uchar * gate,int type)291 v4addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type)
292 {
293 	Route *p;
294 	ulong sa;
295 	ulong m;
296 	ulong ea;
297 	int h, eh;
298 
299 	m = nhgetl(mask);
300 	sa = nhgetl(a) & m;
301 	ea = sa | ~m;
302 
303 	eh = V4H(ea);
304 	for(h=V4H(sa); h<=eh; h++) {
305 		p = allocroute(Rv4 | type);
306 		p->v4.address = sa;
307 		p->v4.endaddress = ea;
308 		memmove(p->v4.gate, gate, sizeof(p->v4.gate));
309 		memmove(p->tag, tag, sizeof(p->tag));
310 
311 		wlock(&routelock);
312 		addnode(f, &f->v4root[h], p);
313 		while(p = f->queue) {
314 			f->queue = p->mid;
315 			walkadd(f, &f->v4root[h], p->left);
316 			freeroute(p);
317 		}
318 		wunlock(&routelock);
319 	}
320 	v4routegeneration++;
321 
322 	ipifcaddroute(f, Rv4, a, mask, gate, type);
323 }
324 
325 #define	V6H(a)	(((a)[IPllen-1] & 0x07ffffff)>>(32-Lroot-5))
326 #define ISDFLT(a, mask, tag) ((ipcmp((a),v6Unspecified)==0) && (ipcmp((mask),v6Unspecified)==0) && (strcmp((tag), "ra")!=0))
327 
328 void
v6addroute(Fs * f,char * tag,uchar * a,uchar * mask,uchar * gate,int type)329 v6addroute(Fs *f, char *tag, uchar *a, uchar *mask, uchar *gate, int type)
330 {
331 	Route *p;
332 	ulong sa[IPllen], ea[IPllen];
333 	ulong x, y;
334 	int h, eh;
335 
336 	/*
337 	if(ISDFLT(a, mask, tag))
338 		f->v6p->cdrouter = -1;
339 	*/
340 
341 
342 	for(h = 0; h < IPllen; h++){
343 		x = nhgetl(a+4*h);
344 		y = nhgetl(mask+4*h);
345 		sa[h] = x & y;
346 		ea[h] = x | ~y;
347 	}
348 
349 	eh = V6H(ea);
350 	for(h = V6H(sa); h <= eh; h++) {
351 		p = allocroute(type);
352 		memmove(p->v6.address, sa, IPaddrlen);
353 		memmove(p->v6.endaddress, ea, IPaddrlen);
354 		memmove(p->v6.gate, gate, IPaddrlen);
355 		memmove(p->tag, tag, sizeof(p->tag));
356 
357 		wlock(&routelock);
358 		addnode(f, &f->v6root[h], p);
359 		while(p = f->queue) {
360 			f->queue = p->mid;
361 			walkadd(f, &f->v6root[h], p->left);
362 			freeroute(p);
363 		}
364 		wunlock(&routelock);
365 	}
366 	v6routegeneration++;
367 
368 	ipifcaddroute(f, 0, a, mask, gate, type);
369 }
370 
371 Route**
looknode(Route ** cur,Route * r)372 looknode(Route **cur, Route *r)
373 {
374 	Route *p;
375 
376 	for(;;){
377 		p = *cur;
378 		if(p == 0)
379 			return 0;
380 
381 		switch(rangecompare(r, p)){
382 		case Rcontains:
383 			return 0;
384 		case Rpreceeds:
385 			cur = &p->left;
386 			break;
387 		case Rfollows:
388 			cur = &p->right;
389 			break;
390 		case Rcontained:
391 			cur = &p->mid;
392 			break;
393 		case Requals:
394 			return cur;
395 		}
396 	}
397 }
398 
399 static void
del1route(Fs * f,Route ** root,Route * rtp,int h,int dolock)400 del1route(Fs *f, Route **root, Route *rtp, int h, int dolock)
401 {
402 	Route **r, *p;
403 
404 	if(dolock)
405 		wlock(&routelock);
406 	r = looknode(&root[h], rtp);
407 	if(r) {
408 		p = *r;
409 		if(--(p->ref) == 0){
410 			*r = 0;
411 			addqueue(&f->queue, p->left);
412 			addqueue(&f->queue, p->mid);
413 			addqueue(&f->queue, p->right);
414 			freeroute(p);
415 			while(p = f->queue) {
416 				f->queue = p->mid;
417 				walkadd(f, &root[h], p->left);
418 				freeroute(p);
419 			}
420 		}
421 	}
422 	if(dolock)
423 		wunlock(&routelock);
424 }
425 
426 void
v4delroute(Fs * f,uchar * a,uchar * mask,int dolock)427 v4delroute(Fs *f, uchar *a, uchar *mask, int dolock)
428 {
429 	Route rt;
430 	int h, eh;
431 	ulong m;
432 
433 	m = nhgetl(mask);
434 	rt.v4.address = nhgetl(a) & m;
435 	rt.v4.endaddress = rt.v4.address | ~m;
436 	rt.type = Rv4;
437 
438 	eh = V4H(rt.v4.endaddress);
439 	for(h=V4H(rt.v4.address); h<=eh; h++)
440 		del1route(f, f->v4root, &rt, h, dolock);
441 	v4routegeneration++;
442 
443 	ipifcremroute(f, Rv4, a, mask);
444 }
445 
446 void
v6delroute(Fs * f,uchar * a,uchar * mask,int dolock)447 v6delroute(Fs *f, uchar *a, uchar *mask, int dolock)
448 {
449 	Route rt;
450 	int h, eh;
451 	ulong x, y;
452 
453 	for(h = 0; h < IPllen; h++){
454 		x = nhgetl(a+4*h);
455 		y = nhgetl(mask+4*h);
456 		rt.v6.address[h] = x & y;
457 		rt.v6.endaddress[h] = x | ~y;
458 	}
459 	rt.type = 0;
460 
461 	eh = V6H(rt.v6.endaddress);
462 	for(h=V6H(rt.v6.address); h<=eh; h++)
463 		del1route(f, f->v6root, &rt, h, dolock);
464 	v6routegeneration++;
465 
466 	ipifcremroute(f, 0, a, mask);
467 }
468 
469 Route*
v4lookup(Fs * f,uchar * a,Conv * c)470 v4lookup(Fs *f, uchar *a, Conv *c)
471 {
472 	Route *p, *q;
473 	ulong la;
474 	uchar gate[IPaddrlen];
475 	Ipifc *ifc;
476 
477 	if(c != nil && c->r != nil && c->r->ifc != nil && c->rgen == v4routegeneration)
478 		return c->r;
479 
480 	la = nhgetl(a);
481 again:
482 	q = nil;
483 	for(p=f->v4root[V4H(la)]; p;)
484 		if(la >= p->v4.address) {
485 			if(la <= p->v4.endaddress) {
486 				q = p;
487 				p = p->mid;
488 			} else
489 				p = p->right;
490 		} else
491 			p = p->left;
492 
493 	if(q && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){
494 		if(q->type & Rifc) {
495 			hnputl(gate+IPv4off, q->v4.address);
496 			memmove(gate, v4prefix, IPv4off);
497 		} else
498 			v4tov6(gate, q->v4.gate);
499 		ifc = findipifc(f, gate, q->type);
500 		if(ifc == nil){
501 			/* find a direct attached route */
502 			if(q->v4.address == 0 && q->v4.endaddress == ~0){
503 				la = nhgetl(q->v4.gate);
504 				goto again;
505 			}
506 			return nil;
507 		}
508 		q->ifc = ifc;
509 		q->ifcid = ifc->ifcid;
510 	}
511 
512 	if(c != nil){
513 		c->r = q;
514 		c->rgen = v4routegeneration;
515 	}
516 
517 	return q;
518 }
519 
520 Route*
v6lookup(Fs * f,uchar * a,Conv * c)521 v6lookup(Fs *f, uchar *a, Conv *c)
522 {
523 	Route *p, *q;
524 	ulong la[IPllen];
525 	int h;
526 	ulong x, y;
527 	uchar gate[IPaddrlen];
528 	Ipifc *ifc;
529 
530 	if(memcmp(a, v4prefix, IPv4off) == 0){
531 		q = v4lookup(f, a+IPv4off, c);
532 		if(q != nil)
533 			return q;
534 	}
535 
536 	if(c != nil && c->r != nil && c->r->ifc != nil && c->rgen == v6routegeneration)
537 		return c->r;
538 
539 	for(h = 0; h < IPllen; h++)
540 		la[h] = nhgetl(a+4*h);
541 
542 	q = 0;
543 	for(p=f->v6root[V6H(la)]; p;){
544 		for(h = 0; h < IPllen; h++){
545 			x = la[h];
546 			y = p->v6.address[h];
547 			if(x == y)
548 				continue;
549 			if(x < y){
550 				p = p->left;
551 				goto next;
552 			}
553 			break;
554 		}
555 		for(h = 0; h < IPllen; h++){
556 			x = la[h];
557 			y = p->v6.endaddress[h];
558 			if(x == y)
559 				continue;
560 			if(x > y){
561 				p = p->right;
562 				goto next;
563 			}
564 			break;
565 		}
566 		q = p;
567 		p = p->mid;
568 next:		;
569 	}
570 
571 	if(q && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){
572 		if(q->type & Rifc) {
573 			for(h = 0; h < IPllen; h++)
574 				hnputl(gate+4*h, q->v6.address[h]);
575 			ifc = findipifc(f, gate, q->type);
576 		} else
577 			ifc = findipifc(f, q->v6.gate, q->type);
578 		if(ifc == nil)
579 			return nil;
580 		q->ifc = ifc;
581 		q->ifcid = ifc->ifcid;
582 	}
583 	if(c != nil){
584 		c->r = q;
585 		c->rgen = v6routegeneration;
586 	}
587 
588 	return q;
589 }
590 
591 void
routetype(int type,char * p)592 routetype(int type, char *p)
593 {
594 	memset(p, ' ', 4);
595 	p[4] = 0;
596 	if(type & Rv4)
597 		*p++ = '4';
598 	else
599 		*p++ = '6';
600 	if(type & Rifc)
601 		*p++ = 'i';
602 	if(type & Runi)
603 		*p++ = 'u';
604 	else if(type & Rbcast)
605 		*p++ = 'b';
606 	else if(type & Rmulti)
607 		*p++ = 'm';
608 	if(type & Rptpt)
609 		*p = 'p';
610 }
611 
612 static char *rformat = "%-15I %-4M %-15I %4.4s %4.4s %3s\n";
613 
614 void
convroute(Route * r,uchar * addr,uchar * mask,uchar * gate,char * t,int * nifc)615 convroute(Route *r, uchar *addr, uchar *mask, uchar *gate, char *t, int *nifc)
616 {
617 	int i;
618 
619 	if(r->type & Rv4){
620 		memmove(addr, v4prefix, IPv4off);
621 		hnputl(addr+IPv4off, r->v4.address);
622 		memset(mask, 0xff, IPv4off);
623 		hnputl(mask+IPv4off, ~(r->v4.endaddress ^ r->v4.address));
624 		memmove(gate, v4prefix, IPv4off);
625 		memmove(gate+IPv4off, r->v4.gate, IPv4addrlen);
626 	} else {
627 		for(i = 0; i < IPllen; i++){
628 			hnputl(addr + 4*i, r->v6.address[i]);
629 			hnputl(mask + 4*i, ~(r->v6.endaddress[i] ^ r->v6.address[i]));
630 		}
631 		memmove(gate, r->v6.gate, IPaddrlen);
632 	}
633 
634 	routetype(r->type, t);
635 
636 	if(r->ifc)
637 		*nifc = r->ifc->conv->x;
638 	else
639 		*nifc = -1;
640 }
641 
642 /*
643  *  this code is not in rr to reduce stack size
644  */
645 static void
sprintroute(Route * r,Routewalk * rw)646 sprintroute(Route *r, Routewalk *rw)
647 {
648 	int nifc, n;
649 	char t[5], *iname, ifbuf[5];
650 	uchar addr[IPaddrlen], mask[IPaddrlen], gate[IPaddrlen];
651 	char *p;
652 
653 	convroute(r, addr, mask, gate, t, &nifc);
654 	iname = "-";
655 	if(nifc != -1) {
656 		iname = ifbuf;
657 		snprint(ifbuf, sizeof ifbuf, "%d", nifc);
658 	}
659 	p = seprint(rw->p, rw->e, rformat, addr, mask, gate, t, r->tag, iname);
660 	if(rw->o < 0){
661 		n = p - rw->p;
662 		if(n > -rw->o){
663 			memmove(rw->p, rw->p-rw->o, n+rw->o);
664 			rw->p = p + rw->o;
665 		}
666 		rw->o += n;
667 	} else
668 		rw->p = p;
669 }
670 
671 /*
672  *  recurse descending tree, applying the function in Routewalk
673  */
674 static int
rr(Route * r,Routewalk * rw)675 rr(Route *r, Routewalk *rw)
676 {
677 	int h;
678 
679 	if(rw->e <= rw->p)
680 		return 0;
681 	if(r == nil)
682 		return 1;
683 
684 	if(rr(r->left, rw) == 0)
685 		return 0;
686 
687 	if(r->type & Rv4)
688 		h = V4H(r->v4.address);
689 	else
690 		h = V6H(r->v6.address);
691 
692 	if(h == rw->h)
693 		rw->walk(r, rw);
694 
695 	if(rr(r->mid, rw) == 0)
696 		return 0;
697 
698 	return rr(r->right, rw);
699 }
700 
701 void
ipwalkroutes(Fs * f,Routewalk * rw)702 ipwalkroutes(Fs *f, Routewalk *rw)
703 {
704 	rlock(&routelock);
705 	if(rw->e > rw->p) {
706 		for(rw->h = 0; rw->h < nelem(f->v4root); rw->h++)
707 			if(rr(f->v4root[rw->h], rw) == 0)
708 				break;
709 	}
710 	if(rw->e > rw->p) {
711 		for(rw->h = 0; rw->h < nelem(f->v6root); rw->h++)
712 			if(rr(f->v6root[rw->h], rw) == 0)
713 				break;
714 	}
715 	runlock(&routelock);
716 }
717 
718 long
routeread(Fs * f,char * p,ulong offset,int n)719 routeread(Fs *f, char *p, ulong offset, int n)
720 {
721 	Routewalk rw;
722 
723 	rw.p = p;
724 	rw.e = p+n;
725 	rw.o = -offset;
726 	rw.walk = sprintroute;
727 
728 	ipwalkroutes(f, &rw);
729 
730 	return rw.p - p;
731 }
732 
733 /*
734  *  this code is not in routeflush to reduce stack size
735  */
736 void
delroute(Fs * f,Route * r,int dolock)737 delroute(Fs *f, Route *r, int dolock)
738 {
739 	uchar addr[IPaddrlen];
740 	uchar mask[IPaddrlen];
741 	uchar gate[IPaddrlen];
742 	char t[5];
743 	int nifc;
744 
745 	convroute(r, addr, mask, gate, t, &nifc);
746 	if(r->type & Rv4)
747 		v4delroute(f, addr+IPv4off, mask+IPv4off, dolock);
748 	else
749 		v6delroute(f, addr, mask, dolock);
750 }
751 
752 /*
753  *  recurse until one route is deleted
754  *    returns 0 if nothing is deleted, 1 otherwise
755  */
756 int
routeflush(Fs * f,Route * r,char * tag)757 routeflush(Fs *f, Route *r, char *tag)
758 {
759 	if(r == nil)
760 		return 0;
761 	if(routeflush(f, r->mid, tag))
762 		return 1;
763 	if(routeflush(f, r->left, tag))
764 		return 1;
765 	if(routeflush(f, r->right, tag))
766 		return 1;
767 	if((r->type & Rifc) == 0){
768 		if(tag == nil || strncmp(tag, r->tag, sizeof(r->tag)) == 0){
769 			delroute(f, r, 0);
770 			return 1;
771 		}
772 	}
773 	return 0;
774 }
775 
776 Route *
iproute(Fs * fs,uchar * ip)777 iproute(Fs *fs, uchar *ip)
778 {
779 	if(isv4(ip))
780 		return v4lookup(fs, ip+IPv4off, nil);
781 	else
782 		return v6lookup(fs, ip, nil);
783 }
784 
785 static void
printroute(Route * r)786 printroute(Route *r)
787 {
788 	int nifc;
789 	char t[5], *iname, ifbuf[5];
790 	uchar addr[IPaddrlen], mask[IPaddrlen], gate[IPaddrlen];
791 
792 	convroute(r, addr, mask, gate, t, &nifc);
793 	iname = "-";
794 	if(nifc != -1) {
795 		iname = ifbuf;
796 		snprint(ifbuf, sizeof ifbuf, "%d", nifc);
797 	}
798 	print(rformat, addr, mask, gate, t, r->tag, iname);
799 }
800 
801 long
routewrite(Fs * f,Chan * c,char * p,int n)802 routewrite(Fs *f, Chan *c, char *p, int n)
803 {
804 	int h, changed;
805 	char *tag;
806 	Cmdbuf *cb;
807 	uchar addr[IPaddrlen];
808 	uchar mask[IPaddrlen];
809 	uchar gate[IPaddrlen];
810 	IPaux *a, *na;
811 	Route *q;
812 	uchar type;
813 
814 	cb = parsecmd(p, n);
815 	if(waserror()){
816 		free(cb);
817 		nexterror();
818 	}
819 
820 	if(strcmp(cb->f[0], "flush") == 0){
821 		tag = cb->f[1];
822 		for(h = 0; h < nelem(f->v4root); h++)
823 			for(changed = 1; changed;){
824 				wlock(&routelock);
825 				changed = routeflush(f, f->v4root[h], tag);
826 				wunlock(&routelock);
827 			}
828 		for(h = 0; h < nelem(f->v6root); h++)
829 			for(changed = 1; changed;){
830 				wlock(&routelock);
831 				changed = routeflush(f, f->v6root[h], tag);
832 				wunlock(&routelock);
833 			}
834 	} else if(strcmp(cb->f[0], "remove") == 0){
835 		if(cb->nf < 3)
836 			error(Ebadarg);
837 		if (parseip(addr, cb->f[1]) == -1)
838 			error(Ebadip);
839 		parseipmask(mask, cb->f[2]);
840 		if(memcmp(addr, v4prefix, IPv4off) == 0)
841 			v4delroute(f, addr+IPv4off, mask+IPv4off, 1);
842 		else
843 			v6delroute(f, addr, mask, 1);
844 	} else if(strcmp(cb->f[0], "add") == 0){
845 		if(cb->nf < 4)
846 			error(Ebadarg);
847 		if(parseip(addr, cb->f[1]) == -1 ||
848 		    parseip(gate, cb->f[3]) == -1)
849 			error(Ebadip);
850 		parseipmask(mask, cb->f[2]);
851 		tag = "none";
852 		if(c != nil){
853 			a = c->aux;
854 			tag = a->tag;
855 		}
856 		if(memcmp(addr, v4prefix, IPv4off) == 0){
857 			type = 0;
858 			if(ipcmp(mask, IPallbits) == 0)
859 				type = Rbcast;
860 			v4addroute(f, tag, addr+IPv4off, mask+IPv4off, gate+IPv4off, type);
861 		}else
862 			v6addroute(f, tag, addr, mask, gate, 0);
863 	} else if(strcmp(cb->f[0], "tag") == 0) {
864 		if(cb->nf < 2)
865 			error(Ebadarg);
866 
867 		a = c->aux;
868 		na = newipaux(a->owner, cb->f[1]);
869 		c->aux = na;
870 		free(a);
871 	} else if(strcmp(cb->f[0], "route") == 0) {
872 		if(cb->nf < 2)
873 			error(Ebadarg);
874 		if (parseip(addr, cb->f[1]) == -1)
875 			error(Ebadip);
876 
877 		q = iproute(f, addr);
878 		print("%I: ", addr);
879 		if(q == nil)
880 			print("no route\n");
881 		else
882 			printroute(q);
883 	}
884 
885 	poperror();
886 	free(cb);
887 	return n;
888 }
889