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