xref: /plan9/sys/src/9/ip/iproute.c (revision 3e87cd09ffd62dbb30f89f86caa5a8aa696a53bb)
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 void
v4delroute(Fs * f,uchar * a,uchar * mask,int dolock)400 v4delroute(Fs *f, uchar *a, uchar *mask, int dolock)
401 {
402 	Route **r, *p;
403 	Route rt;
404 	int h, eh;
405 	ulong m;
406 
407 	m = nhgetl(mask);
408 	rt.v4.address = nhgetl(a) & m;
409 	rt.v4.endaddress = rt.v4.address | ~m;
410 	rt.type = Rv4;
411 
412 	eh = V4H(rt.v4.endaddress);
413 	for(h=V4H(rt.v4.address); h<=eh; h++) {
414 		if(dolock)
415 			wlock(&routelock);
416 		r = looknode(&f->v4root[h], &rt);
417 		if(r) {
418 			p = *r;
419 			if(--(p->ref) == 0){
420 				*r = 0;
421 				addqueue(&f->queue, p->left);
422 				addqueue(&f->queue, p->mid);
423 				addqueue(&f->queue, p->right);
424 				freeroute(p);
425 				while(p = f->queue) {
426 					f->queue = p->mid;
427 					walkadd(f, &f->v4root[h], p->left);
428 					freeroute(p);
429 				}
430 			}
431 		}
432 		if(dolock)
433 			wunlock(&routelock);
434 	}
435 	v4routegeneration++;
436 
437 	ipifcremroute(f, Rv4, a, mask);
438 }
439 
440 void
v6delroute(Fs * f,uchar * a,uchar * mask,int dolock)441 v6delroute(Fs *f, uchar *a, uchar *mask, int dolock)
442 {
443 	Route **r, *p;
444 	Route rt;
445 	int h, eh;
446 	ulong x, y;
447 
448 	for(h = 0; h < IPllen; h++){
449 		x = nhgetl(a+4*h);
450 		y = nhgetl(mask+4*h);
451 		rt.v6.address[h] = x & y;
452 		rt.v6.endaddress[h] = x | ~y;
453 	}
454 	rt.type = 0;
455 
456 	eh = V6H(rt.v6.endaddress);
457 	for(h=V6H(rt.v6.address); h<=eh; h++) {
458 		if(dolock)
459 			wlock(&routelock);
460 		r = looknode(&f->v6root[h], &rt);
461 		if(r) {
462 			p = *r;
463 			if(--(p->ref) == 0){
464 				*r = 0;
465 				addqueue(&f->queue, p->left);
466 				addqueue(&f->queue, p->mid);
467 				addqueue(&f->queue, p->right);
468 				freeroute(p);
469 				while(p = f->queue) {
470 					f->queue = p->mid;
471 					walkadd(f, &f->v6root[h], p->left);
472 					freeroute(p);
473 				}
474 			}
475 		}
476 		if(dolock)
477 			wunlock(&routelock);
478 	}
479 	v6routegeneration++;
480 
481 	ipifcremroute(f, 0, a, mask);
482 }
483 
484 Route*
v4lookup(Fs * f,uchar * a,Conv * c)485 v4lookup(Fs *f, uchar *a, Conv *c)
486 {
487 	Route *p, *q;
488 	ulong la;
489 	uchar gate[IPaddrlen];
490 	Ipifc *ifc;
491 
492 	if(c != nil && c->r != nil && c->r->ifc != nil && c->rgen == v4routegeneration)
493 		return c->r;
494 
495 	la = nhgetl(a);
496 	q = nil;
497 	for(p=f->v4root[V4H(la)]; p;)
498 		if(la >= p->v4.address) {
499 			if(la <= p->v4.endaddress) {
500 				q = p;
501 				p = p->mid;
502 			} else
503 				p = p->right;
504 		} else
505 			p = p->left;
506 
507 	if(q && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){
508 		if(q->type & Rifc) {
509 			hnputl(gate+IPv4off, q->v4.address);
510 			memmove(gate, v4prefix, IPv4off);
511 		} else
512 			v4tov6(gate, q->v4.gate);
513 		ifc = findipifc(f, gate, q->type);
514 		if(ifc == nil)
515 			return nil;
516 		q->ifc = ifc;
517 		q->ifcid = ifc->ifcid;
518 	}
519 
520 	if(c != nil){
521 		c->r = q;
522 		c->rgen = v4routegeneration;
523 	}
524 
525 	return q;
526 }
527 
528 Route*
v6lookup(Fs * f,uchar * a,Conv * c)529 v6lookup(Fs *f, uchar *a, Conv *c)
530 {
531 	Route *p, *q;
532 	ulong la[IPllen];
533 	int h;
534 	ulong x, y;
535 	uchar gate[IPaddrlen];
536 	Ipifc *ifc;
537 
538 	if(memcmp(a, v4prefix, IPv4off) == 0){
539 		q = v4lookup(f, a+IPv4off, c);
540 		if(q != nil)
541 			return q;
542 	}
543 
544 	if(c != nil && c->r != nil && c->r->ifc != nil && c->rgen == v6routegeneration)
545 		return c->r;
546 
547 	for(h = 0; h < IPllen; h++)
548 		la[h] = nhgetl(a+4*h);
549 
550 	q = 0;
551 	for(p=f->v6root[V6H(la)]; p;){
552 		for(h = 0; h < IPllen; h++){
553 			x = la[h];
554 			y = p->v6.address[h];
555 			if(x == y)
556 				continue;
557 			if(x < y){
558 				p = p->left;
559 				goto next;
560 			}
561 			break;
562 		}
563 		for(h = 0; h < IPllen; h++){
564 			x = la[h];
565 			y = p->v6.endaddress[h];
566 			if(x == y)
567 				continue;
568 			if(x > y){
569 				p = p->right;
570 				goto next;
571 			}
572 			break;
573 		}
574 		q = p;
575 		p = p->mid;
576 next:		;
577 	}
578 
579 	if(q && (q->ifc == nil || q->ifcid != q->ifc->ifcid)){
580 		if(q->type & Rifc) {
581 			for(h = 0; h < IPllen; h++)
582 				hnputl(gate+4*h, q->v6.address[h]);
583 			ifc = findipifc(f, gate, q->type);
584 		} else
585 			ifc = findipifc(f, q->v6.gate, q->type);
586 		if(ifc == nil)
587 			return nil;
588 		q->ifc = ifc;
589 		q->ifcid = ifc->ifcid;
590 	}
591 	if(c != nil){
592 		c->r = q;
593 		c->rgen = v6routegeneration;
594 	}
595 
596 	return q;
597 }
598 
599 void
routetype(int type,char * p)600 routetype(int type, char *p)
601 {
602 	memset(p, ' ', 4);
603 	p[4] = 0;
604 	if(type & Rv4)
605 		*p++ = '4';
606 	else
607 		*p++ = '6';
608 	if(type & Rifc)
609 		*p++ = 'i';
610 	if(type & Runi)
611 		*p++ = 'u';
612 	else if(type & Rbcast)
613 		*p++ = 'b';
614 	else if(type & Rmulti)
615 		*p++ = 'm';
616 	if(type & Rptpt)
617 		*p = 'p';
618 }
619 
620 static char *rformat = "%-15I %-4M %-15I %4.4s %4.4s %3s\n";
621 
622 void
convroute(Route * r,uchar * addr,uchar * mask,uchar * gate,char * t,int * nifc)623 convroute(Route *r, uchar *addr, uchar *mask, uchar *gate, char *t, int *nifc)
624 {
625 	int i;
626 
627 	if(r->type & Rv4){
628 		memmove(addr, v4prefix, IPv4off);
629 		hnputl(addr+IPv4off, r->v4.address);
630 		memset(mask, 0xff, IPv4off);
631 		hnputl(mask+IPv4off, ~(r->v4.endaddress ^ r->v4.address));
632 		memmove(gate, v4prefix, IPv4off);
633 		memmove(gate+IPv4off, r->v4.gate, IPv4addrlen);
634 	} else {
635 		for(i = 0; i < IPllen; i++){
636 			hnputl(addr + 4*i, r->v6.address[i]);
637 			hnputl(mask + 4*i, ~(r->v6.endaddress[i] ^ r->v6.address[i]));
638 		}
639 		memmove(gate, r->v6.gate, IPaddrlen);
640 	}
641 
642 	routetype(r->type, t);
643 
644 	if(r->ifc)
645 		*nifc = r->ifc->conv->x;
646 	else
647 		*nifc = -1;
648 }
649 
650 /*
651  *  this code is not in rr to reduce stack size
652  */
653 static void
sprintroute(Route * r,Routewalk * rw)654 sprintroute(Route *r, Routewalk *rw)
655 {
656 	int nifc, n;
657 	char t[5], *iname, ifbuf[5];
658 	uchar addr[IPaddrlen], mask[IPaddrlen], gate[IPaddrlen];
659 	char *p;
660 
661 	convroute(r, addr, mask, gate, t, &nifc);
662 	iname = "-";
663 	if(nifc != -1) {
664 		iname = ifbuf;
665 		snprint(ifbuf, sizeof ifbuf, "%d", nifc);
666 	}
667 	p = seprint(rw->p, rw->e, rformat, addr, mask, gate, t, r->tag, iname);
668 	if(rw->o < 0){
669 		n = p - rw->p;
670 		if(n > -rw->o){
671 			memmove(rw->p, rw->p-rw->o, n+rw->o);
672 			rw->p = p + rw->o;
673 		}
674 		rw->o += n;
675 	} else
676 		rw->p = p;
677 }
678 
679 /*
680  *  recurse descending tree, applying the function in Routewalk
681  */
682 static int
rr(Route * r,Routewalk * rw)683 rr(Route *r, Routewalk *rw)
684 {
685 	int h;
686 
687 	if(rw->e <= rw->p)
688 		return 0;
689 	if(r == nil)
690 		return 1;
691 
692 	if(rr(r->left, rw) == 0)
693 		return 0;
694 
695 	if(r->type & Rv4)
696 		h = V4H(r->v4.address);
697 	else
698 		h = V6H(r->v6.address);
699 
700 	if(h == rw->h)
701 		rw->walk(r, rw);
702 
703 	if(rr(r->mid, rw) == 0)
704 		return 0;
705 
706 	return rr(r->right, rw);
707 }
708 
709 void
ipwalkroutes(Fs * f,Routewalk * rw)710 ipwalkroutes(Fs *f, Routewalk *rw)
711 {
712 	rlock(&routelock);
713 	if(rw->e > rw->p) {
714 		for(rw->h = 0; rw->h < nelem(f->v4root); rw->h++)
715 			if(rr(f->v4root[rw->h], rw) == 0)
716 				break;
717 	}
718 	if(rw->e > rw->p) {
719 		for(rw->h = 0; rw->h < nelem(f->v6root); rw->h++)
720 			if(rr(f->v6root[rw->h], rw) == 0)
721 				break;
722 	}
723 	runlock(&routelock);
724 }
725 
726 long
routeread(Fs * f,char * p,ulong offset,int n)727 routeread(Fs *f, char *p, ulong offset, int n)
728 {
729 	Routewalk rw;
730 
731 	rw.p = p;
732 	rw.e = p+n;
733 	rw.o = -offset;
734 	rw.walk = sprintroute;
735 
736 	ipwalkroutes(f, &rw);
737 
738 	return rw.p - p;
739 }
740 
741 /*
742  *  this code is not in routeflush to reduce stack size
743  */
744 void
delroute(Fs * f,Route * r,int dolock)745 delroute(Fs *f, Route *r, int dolock)
746 {
747 	uchar addr[IPaddrlen];
748 	uchar mask[IPaddrlen];
749 	uchar gate[IPaddrlen];
750 	char t[5];
751 	int nifc;
752 
753 	convroute(r, addr, mask, gate, t, &nifc);
754 	if(r->type & Rv4)
755 		v4delroute(f, addr+IPv4off, mask+IPv4off, dolock);
756 	else
757 		v6delroute(f, addr, mask, dolock);
758 }
759 
760 /*
761  *  recurse until one route is deleted
762  *    returns 0 if nothing is deleted, 1 otherwise
763  */
764 int
routeflush(Fs * f,Route * r,char * tag)765 routeflush(Fs *f, Route *r, char *tag)
766 {
767 	if(r == nil)
768 		return 0;
769 	if(routeflush(f, r->mid, tag))
770 		return 1;
771 	if(routeflush(f, r->left, tag))
772 		return 1;
773 	if(routeflush(f, r->right, tag))
774 		return 1;
775 	if((r->type & Rifc) == 0){
776 		if(tag == nil || strncmp(tag, r->tag, sizeof(r->tag)) == 0){
777 			delroute(f, r, 0);
778 			return 1;
779 		}
780 	}
781 	return 0;
782 }
783 
784 Route *
iproute(Fs * fs,uchar * ip)785 iproute(Fs *fs, uchar *ip)
786 {
787 	if(isv4(ip))
788 		return v4lookup(fs, ip+IPv4off, nil);
789 	else
790 		return v6lookup(fs, ip, nil);
791 }
792 
793 static void
printroute(Route * r)794 printroute(Route *r)
795 {
796 	int nifc;
797 	char t[5], *iname, ifbuf[5];
798 	uchar addr[IPaddrlen], mask[IPaddrlen], gate[IPaddrlen];
799 
800 	convroute(r, addr, mask, gate, t, &nifc);
801 	iname = "-";
802 	if(nifc != -1) {
803 		iname = ifbuf;
804 		snprint(ifbuf, sizeof ifbuf, "%d", nifc);
805 	}
806 	print(rformat, addr, mask, gate, t, r->tag, iname);
807 }
808 
809 long
routewrite(Fs * f,Chan * c,char * p,int n)810 routewrite(Fs *f, Chan *c, char *p, int n)
811 {
812 	int h, changed;
813 	char *tag;
814 	Cmdbuf *cb;
815 	uchar addr[IPaddrlen];
816 	uchar mask[IPaddrlen];
817 	uchar gate[IPaddrlen];
818 	IPaux *a, *na;
819 	Route *q;
820 
821 	cb = parsecmd(p, n);
822 	if(waserror()){
823 		free(cb);
824 		nexterror();
825 	}
826 
827 	if(strcmp(cb->f[0], "flush") == 0){
828 		tag = cb->f[1];
829 		for(h = 0; h < nelem(f->v4root); h++)
830 			for(changed = 1; changed;){
831 				wlock(&routelock);
832 				changed = routeflush(f, f->v4root[h], tag);
833 				wunlock(&routelock);
834 			}
835 		for(h = 0; h < nelem(f->v6root); h++)
836 			for(changed = 1; changed;){
837 				wlock(&routelock);
838 				changed = routeflush(f, f->v6root[h], tag);
839 				wunlock(&routelock);
840 			}
841 	} else if(strcmp(cb->f[0], "remove") == 0){
842 		if(cb->nf < 3)
843 			error(Ebadarg);
844 		if (parseip(addr, cb->f[1]) == -1)
845 			error(Ebadip);
846 		parseipmask(mask, cb->f[2]);
847 		if(memcmp(addr, v4prefix, IPv4off) == 0)
848 			v4delroute(f, addr+IPv4off, mask+IPv4off, 1);
849 		else
850 			v6delroute(f, addr, mask, 1);
851 	} else if(strcmp(cb->f[0], "add") == 0){
852 		if(cb->nf < 4)
853 			error(Ebadarg);
854 		if(parseip(addr, cb->f[1]) == -1 ||
855 		    parseip(gate, cb->f[3]) == -1)
856 			error(Ebadip);
857 		parseipmask(mask, cb->f[2]);
858 		tag = "none";
859 		if(c != nil){
860 			a = c->aux;
861 			tag = a->tag;
862 		}
863 		if(memcmp(addr, v4prefix, IPv4off) == 0)
864 			v4addroute(f, tag, addr+IPv4off, mask+IPv4off, gate+IPv4off, 0);
865 		else
866 			v6addroute(f, tag, addr, mask, gate, 0);
867 	} else if(strcmp(cb->f[0], "tag") == 0) {
868 		if(cb->nf < 2)
869 			error(Ebadarg);
870 
871 		a = c->aux;
872 		na = newipaux(a->owner, cb->f[1]);
873 		c->aux = na;
874 		free(a);
875 	} else if(strcmp(cb->f[0], "route") == 0) {
876 		if(cb->nf < 2)
877 			error(Ebadarg);
878 		if (parseip(addr, cb->f[1]) == -1)
879 			error(Ebadip);
880 
881 		q = iproute(f, addr);
882 		print("%I: ", addr);
883 		if(q == nil)
884 			print("no route\n");
885 		else
886 			printroute(q);
887 	}
888 
889 	poperror();
890 	free(cb);
891 	return n;
892 }
893