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