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