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