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