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