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