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