]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/ip/ipifc.c
usbohci: use 64-bit io base address, disable interrupts before reset, remove resetlck
[plan9front.git] / sys / src / 9 / ip / ipifc.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7
8 #include "ip.h"
9 #include "ipv6.h"
10
11 #define DPRINT if(0)print
12
13 enum {
14         Maxmedia        = 32,
15         Nself           = Maxmedia*5,
16         NHASH           = 1<<6,
17         NCACHE          = 256,
18         QMAX            = 192*1024-1,
19 };
20
21 Medium *media[Maxmedia] = { 0 };
22
23 /*
24  *  cache of local addresses (addresses we answer to)
25  */
26 struct Ipself
27 {
28         uchar   a[IPaddrlen];
29         Ipself  *next;          /* next address in the hash table */
30         Iplink  *link;          /* binding twixt Ipself and Ipifc */
31         ulong   expire;
32         uchar   type;           /* type of address */
33 };
34
35 struct Ipselftab
36 {
37         QLock;
38         int     inited;
39         int     acceptall;      /* true if an interface has the null address */
40         Ipself  *hash[NHASH];   /* hash chains */
41 };
42
43 /*
44  *  Multicast addresses are chained onto a Chan so that
45  *  we can remove them when the Chan is closed.
46  */
47 typedef struct Ipmcast Ipmcast;
48 struct Ipmcast
49 {
50         Ipmcast *next;
51         uchar   ma[IPaddrlen];  /* multicast address */
52         uchar   ia[IPaddrlen];  /* interface address */
53 };
54
55 /* quick hash for ip addresses */
56 #define hashipa(a) ( ( ((a)[IPaddrlen-2]<<8) | (a)[IPaddrlen-1] )%NHASH )
57
58 static char tifc[] = "ifc ";
59
60 static void     addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type);
61 static void     remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a);
62 static void     ipifcregisteraddr(Fs*, Ipifc*, Iplifc*, uchar*);
63 static void     ipifcregisterproxy(Fs*, Ipifc*, uchar*, int);
64 static char*    ipifcremlifc(Ipifc*, Iplifc**);
65
66 static char Ebound[] = "interface already bound";
67 static char Eunbound[] = "interface not bound";
68
69 enum {
70         unknownv6,              /* UGH */
71         unspecifiedv6,
72         linklocalv6,
73         globalv6,
74 };
75
76 static int
77 v6addrtype(uchar *addr)
78 {
79         if(isv4(addr) || ipcmp(addr, IPnoaddr) == 0)
80                 return unknownv6;
81         else if(islinklocal(addr) || ipcmp(addr, v6loopback) == 0 ||
82             isv6mcast(addr) && (addr[1] & 0xF) <= Link_local_scop)
83                 return linklocalv6;
84         else
85                 return globalv6;
86 }
87
88 static int
89 comprefixlen(uchar *a, uchar *b, int n)
90 {
91         int i, c;
92
93         for(i = 0; i < n; i++){
94                 if((c = a[i] ^ b[i]) == 0)
95                         continue;
96                 for(i <<= 3; (c & 0x80) == 0; i++)
97                         c <<= 1;
98                 return i;
99         }
100         return i << 3;
101 }
102
103 /*
104  *  link in a new medium
105  */
106 void
107 addipmedium(Medium *med)
108 {
109         int i;
110
111         for(i = 0; i < nelem(media)-1; i++)
112                 if(media[i] == nil){
113                         media[i] = med;
114                         break;
115                 }
116 }
117
118 /*
119  *  find the medium with this name
120  */
121 Medium*
122 ipfindmedium(char *name)
123 {
124         Medium **mp;
125
126         for(mp = media; *mp != nil; mp++)
127                 if(strcmp((*mp)->name, name) == 0)
128                         break;
129         return *mp;
130 }
131
132 /*
133  *  attach a device (or pkt driver) to the interface.
134  *  called with c locked
135  */
136 static char*
137 ipifcbind(Conv *c, char **argv, int argc)
138 {
139         Ipifc *ifc;
140         Medium *m;
141
142         if(argc < 2)
143                 return Ebadarg;
144
145         ifc = (Ipifc*)c->ptcl;
146
147         /* bind the device to the interface */
148         m = ipfindmedium(argv[1]);
149         if(m == nil)
150                 return "unknown interface type";
151
152         wlock(ifc);
153         if(ifc->m != nil){
154                 wunlock(ifc);
155                 return Ebound;
156         }
157         if(waserror()){
158                 wunlock(ifc);
159                 nexterror();
160         }
161
162         /* do medium specific binding */
163         (*m->bind)(ifc, argc, argv);
164
165         /* set the bound device name */
166         if(argc > 2)
167                 strncpy(ifc->dev, argv[2], sizeof(ifc->dev));
168         else
169                 snprint(ifc->dev, sizeof ifc->dev, "%s%d", m->name, c->x);
170         ifc->dev[sizeof(ifc->dev)-1] = 0;
171
172         /* set up parameters */
173         ifc->m = m;
174         ifc->mintu = ifc->m->mintu;
175         ifc->maxtu = ifc->m->maxtu;
176         ifc->delay = 40;
177         ifc->speed = 0;
178         if(ifc->m->unbindonclose == 0)
179                 ifc->conv->inuse++;
180
181         /* default router paramters */
182         ifc->rp = c->p->f->v6p->rp;
183
184         /* any ancillary structures (like routes) no longer pertain */
185         ifc->ifcid++;
186
187         /* reopen all the queues closed by a previous unbind */
188         qreopen(c->rq);
189         qreopen(c->eq);
190         qreopen(c->sq);
191
192         wunlock(ifc);
193         poperror();
194
195         return nil;
196 }
197
198 /*
199  *  detach a device from an interface, close the interface
200  */
201 static char*
202 ipifcunbind(Ipifc *ifc)
203 {
204         Medium *m;
205
206         wlock(ifc);
207         m = ifc->m;
208         if(m == nil){
209                 wunlock(ifc);
210                 return Eunbound;
211         }
212
213         /* disassociate logical interfaces (before zeroing ifc->arg) */
214         while(ifc->lifc != nil)
215                 ipifcremlifc(ifc, &ifc->lifc);
216
217         /* disassociate device */
218         if(m->unbind != nil){
219                 extern Medium nullmedium;
220
221                 /*
222                  * unbind() might unlock the ifc, so change the medium
223                  * to the nullmedium to prevent packets from getting
224                  * sent while the medium is shutting down.
225                  */
226                 ifc->m = &nullmedium;
227
228                 if(!waserror()){
229                         (*m->unbind)(ifc);
230                         poperror();
231                 }
232         }
233
234         memset(ifc->dev, 0, sizeof(ifc->dev));
235         ifc->arg = nil;
236
237         ifc->reflect = 0;
238         ifc->reassemble = 0;
239
240         /* close queues to stop queuing of packets */
241         qclose(ifc->conv->rq);
242         qclose(ifc->conv->wq);
243         qclose(ifc->conv->sq);
244
245         /* dissociate routes */
246         ifc->ifcid++;
247         if(m->unbindonclose == 0)
248                 ifc->conv->inuse--;
249         ifc->m = nil;
250         wunlock(ifc);
251
252         return nil;
253 }
254
255 char sfixedformat[] = "device %s maxtu %d sendra %d recvra %d mflag %d oflag %d"
256 " maxraint %d minraint %d linkmtu %d reachtime %d rxmitra %d ttl %d routerlt %d"
257 " pktin %lud pktout %lud errin %lud errout %lud speed %d delay %d\n";
258
259 char slineformat[] = "  %-40I %-10M %-40I %-12lud %-12lud\n";
260
261 static int
262 ipifcstate(Conv *c, char *state, int n)
263 {
264         Ipifc *ifc;
265         Iplifc *lifc;
266         int m;
267
268         ifc = (Ipifc*)c->ptcl;
269         m = snprint(state, n, sfixedformat,
270                 ifc->dev, ifc->maxtu, ifc->sendra6, ifc->recvra6,
271                 ifc->rp.mflag, ifc->rp.oflag, ifc->rp.maxraint,
272                 ifc->rp.minraint, ifc->rp.linkmtu, ifc->rp.reachtime,
273                 ifc->rp.rxmitra, ifc->rp.ttl, ifc->rp.routerlt,
274                 ifc->in, ifc->out, ifc->inerr, ifc->outerr,
275                 ifc->speed, ifc->delay);
276
277         rlock(ifc);
278         for(lifc = ifc->lifc; lifc != nil && n > m; lifc = lifc->next)
279                 m += snprint(state+m, n - m, slineformat, lifc->local,
280                         lifc->mask, lifc->remote, lifc->validlt, lifc->preflt);
281         if(ifc->lifc == nil)
282                 m += snprint(state+m, n - m, "\n");
283         runlock(ifc);
284         return m;
285 }
286
287 static int
288 ipifclocal(Conv *c, char *state, int n)
289 {
290         Ipifc *ifc;
291         Iplifc *lifc;
292         Iplink *link;
293         int m;
294
295         ifc = (Ipifc*)c->ptcl;
296         rlock(ifc);
297         m = 0;
298         for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
299                 m += snprint(state+m, n - m, "%-40.40I ->", lifc->local);
300                 for(link = lifc->link; link != nil; link = link->lifclink)
301                         m += snprint(state+m, n - m, " %-40.40I", link->self->a);
302                 m += snprint(state+m, n - m, "\n");
303         }
304         runlock(ifc);
305         return m;
306 }
307
308 static int
309 ipifcinuse(Conv *c)
310 {
311         Ipifc *ifc;
312
313         ifc = (Ipifc*)c->ptcl;
314         return ifc->m != nil;
315 }
316
317 static void
318 ipifcadjustburst(Ipifc *ifc)
319 {
320         int burst;
321
322         burst = ((vlong)ifc->delay * ifc->speed) / 8000;
323         if(burst < ifc->maxtu)
324                 burst = ifc->maxtu;
325         ifc->burst = burst;
326 }
327
328 static void
329 ipifcsetdelay(Ipifc *ifc, int delay)
330 {
331         if(delay < 0)
332                 delay = 0;
333         else if(delay > 1000)
334                 delay = 1000;
335         ifc->delay = delay;
336         ipifcadjustburst(ifc);
337 }
338
339 static void
340 ipifcsetspeed(Ipifc *ifc, int speed)
341 {
342         if(speed < 0)
343                 speed = 0;
344         ifc->speed = speed;
345         ifc->load = 0;
346         ipifcadjustburst(ifc);
347 }
348
349 void
350 ipifcoput(Ipifc *ifc, Block *bp, int version, uchar *ip)
351 {
352         if(ifc->speed){
353                 ulong now = MACHP(0)->ticks;
354                 int dt = TK2MS(now - ifc->ticks);
355                 ifc->ticks = now;
356                 ifc->load -= ((vlong)dt * ifc->speed) / 8000;
357                 if(ifc->load < 0 || dt < 0 || dt > 1000)
358                         ifc->load = 0;
359                 else if(ifc->load > ifc->burst){
360                         freeblist(bp);
361                         return;
362                 }
363         }
364         bp = concatblock(bp);
365         ifc->load += BLEN(bp);
366         ifc->m->bwrite(ifc, bp, version, ip);
367 }
368
369
370 /*
371  *  called when a process writes to an interface's 'data'
372  */
373 static void
374 ipifckick(void *x)
375 {
376         Conv *c = x;
377         Block *bp;
378         Ipifc *ifc;
379
380         bp = qget(c->wq);
381         if(bp == nil)
382                 return;
383
384         ifc = (Ipifc*)c->ptcl;
385         rlock(ifc);
386         if(waserror()){
387                 runlock(ifc);
388                 nexterror();
389         }
390         if(ifc->m != nil && ifc->m->pktin != nil)
391                 (*ifc->m->pktin)(c->p->f, ifc, bp);
392         else
393                 freeb(bp);
394         runlock(ifc);
395         poperror();
396 }
397
398 /*
399  *  called when a new ipifc structure is created
400  */
401 static void
402 ipifccreate(Conv *c)
403 {
404         Ipifc *ifc;
405
406         c->rq = qopen(QMAX, 0, 0, 0);
407         c->wq = qopen(QMAX, Qkick, ipifckick, c);
408         c->sq = qopen(QMAX, 0, 0, 0);
409         if(c->rq == nil || c->wq == nil || c->sq == nil)
410                 error(Enomem);
411         ifc = (Ipifc*)c->ptcl;
412         ifc->conv = c;
413         ifc->m = nil;
414         ifc->reflect = 0;
415         ifc->reassemble = 0;
416 }
417
418 /*
419  *  called after last close of ipifc data or ctl
420  */
421 static void
422 ipifcclose(Conv *c)
423 {
424         Ipifc *ifc = (Ipifc*)c->ptcl;
425         Medium *m = ifc->m;
426
427         if(m != nil && m->unbindonclose)
428                 ipifcunbind(ifc);
429 }
430
431 /*
432  *  change an interface's mtu
433  */
434 static char*
435 ipifcsetmtu(Ipifc *ifc, int mtu)
436 {
437         Medium *m = ifc->m;
438
439         if(m == nil)
440                 return Eunbound;
441         if(mtu < m->mintu || mtu > m->maxtu)
442                 return Ebadarg;
443         ifc->maxtu = mtu;
444         ipifcadjustburst(ifc);
445         return nil;
446 }
447
448 /*
449  *  add an address to an interface.
450  */
451 char*
452 ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp)
453 {
454         uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen];
455         uchar bcast[IPaddrlen], net[IPaddrlen];
456         Iplifc *lifc, **l;
457         int i, type, mtu;
458         Fs *f;
459
460         mtu = 0;
461         type = Rifc;
462         memset(ip, 0, IPaddrlen);
463         memset(mask, 0, IPaddrlen);
464         memset(rem, 0, IPaddrlen);
465         switch(argc){
466         case 6:
467                 if(strcmp(argv[5], "proxy") == 0)
468                         type |= Rproxy;
469                 /* fall through */
470         case 5:
471                 mtu = strtoul(argv[4], 0, 0);
472                 /* fall through */
473         case 4:
474                 if (parseipandmask(ip, mask, argv[1], argv[2]) == -1 || parseip(rem, argv[3]) == -1)
475                         return Ebadip;
476                 maskip(rem, mask, net);
477                 break;
478         case 3:
479                 if (parseipandmask(ip, mask, argv[1], argv[2]) == -1)
480                         return Ebadip;
481                 maskip(ip, mask, rem);
482                 maskip(rem, mask, net);
483                 break;
484         case 2:
485                 if (parseip(ip, argv[1]) == -1)
486                         return Ebadip;
487                 memmove(mask, defmask(ip), IPaddrlen);
488                 maskip(ip, mask, rem);
489                 maskip(rem, mask, net);
490                 break;
491         default:
492                 return Ebadarg;
493         }
494
495         /* check for point-to-point interface */
496         if(ipcmp(ip, v6loopback) != 0) /* skip v6 loopback, it's a special address */
497         if(ipcmp(mask, IPallbits) == 0)
498                 type |= Rptpt;
499
500         if(isv4(ip) || ipcmp(ip, IPnoaddr) == 0){
501                 type |= Rv4;
502                 tentative = 0;
503         }
504
505         wlock(ifc);
506         if(ifc->m == nil){
507                 wunlock(ifc);
508                 return Eunbound;
509         }
510         f = ifc->conv->p->f;
511         if(waserror()){
512                 wunlock(ifc);
513                 return up->errstr;
514         }
515
516         if(mtu > 0)
517                 ipifcsetmtu(ifc, mtu);
518
519         /* ignore if this is already a local address for this ifc */
520         if((lifc = iplocalonifc(ifc, ip)) != nil){
521                 if(lifcp != nil) {
522                         if(!lifc->onlink && lifcp->onlink){
523                                 lifc->onlink = 1;
524                                 addroute(f, lifc->remote, lifc->mask, ip, IPallbits,
525                                         lifc->remote, lifc->type, ifc, tifc);
526                                 if(v6addrtype(ip) != linklocalv6)
527                                         addroute(f, lifc->remote, lifc->mask, ip, IPnoaddr,
528                                                 lifc->remote, lifc->type, ifc, tifc);
529                         }
530                         lifc->autoflag = lifcp->autoflag;
531                         lifc->validlt = lifcp->validlt;
532                         lifc->preflt = lifcp->preflt;
533                         lifc->origint = lifcp->origint;
534                 }
535                 if(lifc->tentative != tentative){
536                         lifc->tentative = tentative;
537                         goto done;
538                 }
539                 wunlock(ifc);
540                 poperror();
541                 return nil;
542         }
543
544         /* add the address to the list of logical ifc's for this ifc */
545         lifc = smalloc(sizeof(Iplifc));
546         ipmove(lifc->local, ip);
547         ipmove(lifc->mask, mask);
548         ipmove(lifc->remote, rem);
549         ipmove(lifc->net, net);
550         lifc->type = type;
551         lifc->tentative = tentative;
552         if(lifcp != nil) {
553                 lifc->onlink = lifcp->onlink;
554                 lifc->autoflag = lifcp->autoflag;
555                 lifc->validlt = lifcp->validlt;
556                 lifc->preflt = lifcp->preflt;
557                 lifc->origint = lifcp->origint;
558         } else {                /* default values */
559                 lifc->onlink = lifc->autoflag = 1;
560                 lifc->validlt = lifc->preflt = ~0UL;
561                 lifc->origint = NOW / 1000;
562         }
563         lifc->next = nil;
564
565         for(l = &ifc->lifc; *l != nil; l = &(*l)->next)
566                 ;
567         *l = lifc;
568
569         /* add route for this logical interface */
570         if(lifc->onlink){
571                 addroute(f, rem, mask, ip, IPallbits, rem, type, ifc, tifc);
572                 if(v6addrtype(ip) != linklocalv6)
573                         addroute(f, rem, mask, ip, IPnoaddr, rem, type, ifc, tifc);
574         }
575
576         addselfcache(f, ifc, lifc, ip, Runi);
577
578         /* register proxy */
579         if(type & Rptpt){
580                 if(type & Rproxy)
581                         ipifcregisterproxy(f, ifc, rem, 1);
582                 goto done;
583         }
584
585         if(type & Rv4) {
586                 /* add subnet directed broadcast address to the self cache */
587                 for(i = 0; i < IPaddrlen; i++)
588                         bcast[i] = (ip[i] & mask[i]) | ~mask[i];
589                 addselfcache(f, ifc, lifc, bcast, Rbcast);
590
591                 /* add subnet directed network address to the self cache */
592                 for(i = 0; i < IPaddrlen; i++)
593                         bcast[i] = (ip[i] & mask[i]) & mask[i];
594                 addselfcache(f, ifc, lifc, bcast, Rbcast);
595
596                 /* add network directed broadcast address to the self cache */
597                 memmove(mask, defmask(ip), IPaddrlen);
598                 for(i = 0; i < IPaddrlen; i++)
599                         bcast[i] = (ip[i] & mask[i]) | ~mask[i];
600                 addselfcache(f, ifc, lifc, bcast, Rbcast);
601
602                 /* add network directed network address to the self cache */
603                 memmove(mask, defmask(ip), IPaddrlen);
604                 for(i = 0; i < IPaddrlen; i++)
605                         bcast[i] = (ip[i] & mask[i]) & mask[i];
606                 addselfcache(f, ifc, lifc, bcast, Rbcast);
607
608                 addselfcache(f, ifc, lifc, IPv4bcast, Rbcast);
609         } else {
610                 if(ipcmp(ip, v6loopback) == 0) {
611                         /* add node-local mcast address */
612                         addselfcache(f, ifc, lifc, v6allnodesN, Rmulti);
613
614                         /* add route for all node multicast */
615                         addroute(f, v6allnodesN, v6allnodesNmask,
616                                 ip, IPallbits,
617                                 v6allnodesN, Rmulti, ifc, tifc);
618                 }
619
620                 /* add all nodes multicast address */
621                 addselfcache(f, ifc, lifc, v6allnodesL, Rmulti);
622
623                 /* add route for all nodes multicast */
624                 addroute(f, v6allnodesL, v6allnodesLmask,
625                         ip, IPallbits,
626                         v6allnodesL, Rmulti, ifc, tifc);
627
628                 /* add solicited-node multicast address */
629                 ipv62smcast(bcast, ip);
630                 addselfcache(f, ifc, lifc, bcast, Rmulti);
631         }
632
633 done:
634         wunlock(ifc);
635         poperror();
636
637         rlock(ifc);
638         ipifcregisteraddr(f, ifc, lifc, ip);
639         runlock(ifc);
640
641         return nil;
642 }
643
644 /*
645  *  remove a logical interface from an ifc
646  *      called with ifc wlock'd
647  */
648 static char*
649 ipifcremlifc(Ipifc *ifc, Iplifc **l)
650 {
651         Iplifc *lifc = *l;
652         Fs *f = ifc->conv->p->f;
653
654         if(lifc == nil)
655                 return "address not on this interface";
656         *l = lifc->next;
657
658         /* disassociate any addresses */
659         while(lifc->link != nil)
660                 remselfcache(f, ifc, lifc, lifc->link->self->a);
661
662         /* remove the route for this logical interface */
663         if(lifc->onlink){
664                 remroute(f, lifc->remote, lifc->mask,
665                         lifc->local, IPallbits,
666                         lifc->remote, lifc->type, ifc, tifc);
667                 if(v6addrtype(lifc->local) != linklocalv6)
668                         remroute(f, lifc->remote, lifc->mask,
669                                 lifc->local, IPnoaddr,
670                                 lifc->remote, lifc->type, ifc, tifc);
671         }
672
673         /* unregister proxy */
674         if(lifc->type & Rptpt){
675                 if(lifc->type & Rproxy)
676                         ipifcregisterproxy(f, ifc, lifc->remote, 0);
677                 goto done;
678         }
679
680         /* remove route for all nodes multicast */
681         if((lifc->type & Rv4) == 0){
682                 if(ipcmp(lifc->local, v6loopback) == 0)
683                         remroute(f, v6allnodesN, v6allnodesNmask,
684                                 lifc->local, IPallbits,
685                                 v6allnodesN, Rmulti, ifc, tifc);
686
687                 remroute(f, v6allnodesL, v6allnodesLmask,
688                         lifc->local, IPallbits,
689                         v6allnodesL, Rmulti, ifc, tifc);
690         }
691
692 done:
693         free(lifc);
694         return nil;
695 }
696
697 /*
698  *  remove an address from an interface.
699  */
700 char*
701 ipifcrem(Ipifc *ifc, char **argv, int argc)
702 {
703         uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen];
704         Iplifc *lifc, **l;
705         char *err;
706
707         if(argc < 3)
708                 return Ebadarg;
709         if(parseipandmask(ip, mask, argv[1], argv[2]) == -1)
710                 return Ebadip;
711         if(argc < 4)
712                 maskip(ip, mask, rem);
713         else if(parseip(rem, argv[3]) == -1)
714                 return Ebadip;
715
716         /*
717          *  find address on this interface and remove from chain.
718          *  for pt to pt we actually specify the remote address as the
719          *  addresss to remove.
720          */
721         wlock(ifc);
722         l = &ifc->lifc;
723         for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next) {
724                 if(ipcmp(ip, lifc->local) == 0
725                 && ipcmp(mask, lifc->mask) == 0
726                 && ipcmp(rem, lifc->remote) == 0)
727                         break;
728                 l = &lifc->next;
729         }
730         err = ipifcremlifc(ifc, l);
731         wunlock(ifc);
732         return err;
733 }
734
735 /*
736  *  associate an address with the interface.  This wipes out any previous
737  *  addresses.  This is a macro that means, remove all the old interfaces
738  *  and add a new one.
739  */
740 static char*
741 ipifcconnect(Conv* c, char **argv, int argc)
742 {
743         Ipifc *ifc = (Ipifc*)c->ptcl;
744         char *err;
745
746         wlock(ifc);
747         while(ifc->lifc != nil)
748                 ipifcremlifc(ifc, &ifc->lifc);
749         wunlock(ifc);
750
751         err = ipifcadd(ifc, argv, argc, 0, nil);
752         if(err != nil)
753                 return err;
754
755         Fsconnected(c, nil);
756         return nil;
757 }
758
759 char*
760 ipifcra6(Ipifc *ifc, char **argv, int argc)
761 {
762         int i, argsleft;
763         uchar sendra, recvra;
764         Routerparams rp;
765
766         i = 1;
767         argsleft = argc - 1;
768         if((argsleft % 2) != 0)
769                 return Ebadarg;
770
771         sendra = ifc->sendra6;
772         recvra = ifc->recvra6;
773         rp = ifc->rp;
774
775         while (argsleft > 1) {
776                 if(strcmp(argv[i], "recvra") == 0)
777                         recvra = atoi(argv[i+1]) != 0;
778                 else if(strcmp(argv[i], "sendra") == 0)
779                         sendra = atoi(argv[i+1]) != 0;
780                 else if(strcmp(argv[i], "mflag") == 0)
781                         rp.mflag = atoi(argv[i+1]) != 0;
782                 else if(strcmp(argv[i], "oflag") == 0)
783                         rp.oflag = atoi(argv[i+1]) != 0;
784                 else if(strcmp(argv[i], "maxraint") == 0)
785                         rp.maxraint = atoi(argv[i+1]);
786                 else if(strcmp(argv[i], "minraint") == 0)
787                         rp.minraint = atoi(argv[i+1]);
788                 else if(strcmp(argv[i], "linkmtu") == 0)
789                         rp.linkmtu = atoi(argv[i+1]);
790                 else if(strcmp(argv[i], "reachtime") == 0)
791                         rp.reachtime = atoi(argv[i+1]);
792                 else if(strcmp(argv[i], "rxmitra") == 0)
793                         rp.rxmitra = atoi(argv[i+1]);
794                 else if(strcmp(argv[i], "ttl") == 0)
795                         rp.ttl = atoi(argv[i+1]);
796                 else if(strcmp(argv[i], "routerlt") == 0)
797                         rp.routerlt = atoi(argv[i+1]);
798                 else
799                         return Ebadarg;
800
801                 argsleft -= 2;
802                 i += 2;
803         }
804
805         /* consistency check */
806         if(rp.maxraint < rp.minraint)
807                 return Ebadarg;
808
809         ifc->rp = rp;
810         ifc->sendra6 = sendra;
811         ifc->recvra6 = recvra;
812
813         return nil;
814 }
815
816 /*
817  *  non-standard control messages.
818  */
819 static char*
820 ipifcctl(Conv* c, char **argv, int argc)
821 {
822         Ipifc *ifc = (Ipifc*)c->ptcl;
823
824         if(strcmp(argv[0], "add") == 0)
825                 return ipifcadd(ifc, argv, argc, 0, nil);
826         else if(strcmp(argv[0], "try") == 0)
827                 return ipifcadd(ifc, argv, argc, 1, nil);
828         else if(strcmp(argv[0], "remove") == 0)
829                 return ipifcrem(ifc, argv, argc);
830         else if(strcmp(argv[0], "unbind") == 0)
831                 return ipifcunbind(ifc);
832         else if(strcmp(argv[0], "mtu") == 0)
833                 return ipifcsetmtu(ifc, argc>1? strtoul(argv[1], 0, 0): 0);
834         else if(strcmp(argv[0], "speed") == 0){
835                 ipifcsetspeed(ifc, argc>1? atoi(argv[1]): 0);
836                 return nil;
837         }
838         else if(strcmp(argv[0], "delay") == 0){
839                 ipifcsetdelay(ifc, argc>1? atoi(argv[1]): 0);
840                 return nil;
841         }
842         else if(strcmp(argv[0], "iprouting") == 0){
843                 iprouting(c->p->f, argc>1? atoi(argv[1]): 1);
844                 return nil;
845         }
846         else if(strcmp(argv[0], "reflect") == 0){
847                 ifc->reflect = argc>1? atoi(argv[1]): 1;
848                 return nil;
849         }
850         else if(strcmp(argv[0], "reassemble") == 0){
851                 ifc->reassemble = argc>1? atoi(argv[1]): 1;
852                 return nil;
853         }
854         else if(strcmp(argv[0], "add6") == 0)
855                 return ipifcadd6(ifc, argv, argc);
856         else if(strcmp(argv[0], "remove6") == 0)
857                 return ipifcremove6(ifc, argv, argc);
858         else if(strcmp(argv[0], "ra6") == 0)
859                 return ipifcra6(ifc, argv, argc);
860         return "unsupported ctl";
861 }
862
863 int
864 ipifcstats(Proto *ipifc, char *buf, int len)
865 {
866         return ipstats(ipifc->f, buf, len);
867 }
868
869 void
870 ipifcinit(Fs *f)
871 {
872         Proto *ipifc;
873
874         ipifc = smalloc(sizeof(Proto));
875         ipifc->name = "ipifc";
876         ipifc->connect = ipifcconnect;
877         ipifc->announce = nil;
878         ipifc->bind = ipifcbind;
879         ipifc->state = ipifcstate;
880         ipifc->create = ipifccreate;
881         ipifc->close = ipifcclose;
882         ipifc->rcv = nil;
883         ipifc->ctl = ipifcctl;
884         ipifc->advise = nil;
885         ipifc->stats = ipifcstats;
886         ipifc->inuse = ipifcinuse;
887         ipifc->local = ipifclocal;
888         ipifc->ipproto = -1;
889         ipifc->nc = Maxmedia;
890         ipifc->ptclsize = sizeof(Ipifc);
891
892         f->ipifc = ipifc;       /* hack for ipifcremroute, findipifc, ... */
893         f->self = smalloc(sizeof(Ipselftab));   /* hack for ipforme */
894
895         Fsproto(f, ipifc);
896 }
897
898 /*
899  *  add to self routing cache
900  */
901 static void
902 addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type)
903 {
904         Iplink *lp;
905         Ipself *p;
906         int h;
907
908         type |= (lifc->type & Rv4);
909         qlock(f->self);
910         if(waserror()){
911                 qunlock(f->self);
912                 nexterror();
913         }
914
915         /* see if the address already exists */
916         h = hashipa(a);
917         for(p = f->self->hash[h]; p != nil; p = p->next)
918                 if(ipcmp(a, p->a) == 0)
919                         break;
920
921         /* allocate a local address and add to hash chain */
922         if(p == nil){
923                 p = smalloc(sizeof(*p));
924                 ipmove(p->a, a);
925                 p->type = type;
926                 p->next = f->self->hash[h];
927                 f->self->hash[h] = p;
928
929                 /* if the null address, accept all packets */
930                 if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0)
931                         f->self->acceptall = 1;
932         }
933
934         /* look for a link for this lifc */
935         for(lp = p->link; lp != nil; lp = lp->selflink)
936                 if(lp->lifc == lifc)
937                         break;
938
939         /* allocate a lifc-to-local link and link to both */
940         if(lp == nil){
941                 lp = smalloc(sizeof(*lp));
942                 lp->ref = 1;
943                 lp->lifc = lifc;
944                 lp->self = p;
945                 lp->selflink = p->link;
946                 p->link = lp;
947                 lp->lifclink = lifc->link;
948                 lifc->link = lp;
949
950                 /* add to routing table */
951                 addroute(f, a, IPallbits,
952                         lifc->local, 
953                         ((type & (Rbcast|Rmulti)) != 0 || v6addrtype(a) == linklocalv6) ?
954                                 IPallbits : IPnoaddr,
955                         a, type, ifc, tifc);
956
957                 if((type & Rmulti) && ifc->m->addmulti != nil)
958                         (*ifc->m->addmulti)(ifc, a, lifc->local);
959         } else
960                 lp->ref++;
961
962         qunlock(f->self);
963         poperror();
964 }
965
966 /*
967  *  These structures are unlinked from their chains while
968  *  other threads may be using them.  To avoid excessive locking,
969  *  just put them aside for a while before freeing them.
970  *      called with f->self locked
971  */
972 static Iplink *freeiplink;
973 static Ipself *freeipself;
974
975 static void
976 iplinkfree(Iplink *p)
977 {
978         Iplink **l, *np;
979         ulong now = NOW;
980
981         l = &freeiplink;
982         for(np = *l; np != nil; np = *l){
983                 if((long)(now - np->expire) >= 0){
984                         *l = np->next;
985                         free(np);
986                         continue;
987                 }
988                 l = &np->next;
989         }
990         p->expire = now + 5000; /* give other threads 5 secs to get out */
991         p->next = nil;
992         *l = p;
993 }
994
995 static void
996 ipselffree(Ipself *p)
997 {
998         Ipself **l, *np;
999         ulong now = NOW;
1000
1001         l = &freeipself;
1002         for(np = *l; np != nil; np = *l){
1003                 if((long)(now - np->expire) >= 0){
1004                         *l = np->next;
1005                         free(np);
1006                         continue;
1007                 }
1008                 l = &np->next;
1009         }
1010         p->expire = now + 5000; /* give other threads 5 secs to get out */
1011         p->next = nil;
1012         *l = p;
1013 }
1014
1015 /*
1016  *  Decrement reference for this address on this link.
1017  *  Unlink from selftab if this is the last ref.
1018  */
1019 static void
1020 remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a)
1021 {
1022         Ipself *p, **l;
1023         Iplink *link, **l_self, **l_lifc;
1024
1025         qlock(f->self);
1026
1027         /* find the unique selftab entry */
1028         l = &f->self->hash[hashipa(a)];
1029         for(p = *l; p != nil; p = *l){
1030                 if(ipcmp(p->a, a) == 0)
1031                         break;
1032                 l = &p->next;
1033         }
1034
1035         if(p == nil)
1036                 goto out;
1037
1038         /*
1039          *  walk down links from an ifc looking for one
1040          *  that matches the selftab entry
1041          */
1042         l_lifc = &lifc->link;
1043         for(link = *l_lifc; link != nil; link = *l_lifc){
1044                 if(link->self == p)
1045                         break;
1046                 l_lifc = &link->lifclink;
1047         }
1048
1049         if(link == nil)
1050                 goto out;
1051
1052         /*
1053          *  walk down the links from the selftab looking for
1054          *  the one we just found
1055          */
1056         l_self = &p->link;
1057         for(link = *l_self; link != nil; link = *l_self){
1058                 if(link == *l_lifc)
1059                         break;
1060                 l_self = &link->selflink;
1061         }
1062
1063         if(link == nil)
1064                 panic("remselfcache");
1065
1066         if(--(link->ref) != 0)
1067                 goto out;
1068
1069         /* remove from routing table */
1070         remroute(f, a, IPallbits,
1071                 lifc->local, 
1072                 ((p->type & (Rbcast|Rmulti)) != 0 || v6addrtype(a) == linklocalv6) ?
1073                         IPallbits : IPnoaddr,
1074                 a, p->type, ifc, tifc);
1075
1076         if((p->type & Rmulti) && ifc->m->remmulti != nil){
1077                 if(!waserror()){
1078                         (*ifc->m->remmulti)(ifc, a, lifc->local);
1079                         poperror();
1080                 }
1081         }
1082
1083         /* ref == 0, remove from both chains and free the link */
1084         *l_lifc = link->lifclink;
1085         *l_self = link->selflink;
1086         iplinkfree(link);
1087
1088         if(p->link != nil)
1089                 goto out;
1090
1091         /* if null address, forget */
1092         if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0)
1093                 f->self->acceptall = 0;
1094
1095         /* no more links, remove from hash and free */
1096         *l = p->next;
1097         ipselffree(p);
1098
1099 out:
1100         qunlock(f->self);
1101 }
1102
1103 long
1104 ipselftabread(Fs *f, char *cp, ulong offset, int n)
1105 {
1106         int i, m, nifc, off;
1107         Ipself *p;
1108         Iplink *link;
1109         char state[8];
1110
1111         m = 0;
1112         off = offset;
1113         for(i = 0; i < NHASH && m < n; i++){
1114                 for(p = f->self->hash[i]; p != nil && m < n; p = p->next){
1115                         nifc = 0;
1116                         for(link = p->link; link != nil; link = link->selflink)
1117                                 nifc++;
1118                         routetype(p->type, state);
1119                         m += snprint(cp + m, n - m, "%-44.44I %2.2d %4.4s\n",
1120                                 p->a, nifc, state);
1121                         if(off > 0){
1122                                 off -= m;
1123                                 m = 0;
1124                         }
1125                 }
1126         }
1127         return m;
1128 }
1129
1130 /*
1131  *  returns
1132  *      0               - no match
1133  *      Runi
1134  *      Rbcast
1135  *      Rmulti
1136  */
1137 int
1138 ipforme(Fs *f, uchar *addr)
1139 {
1140         Ipself *p;
1141
1142         for(p = f->self->hash[hashipa(addr)]; p != nil; p = p->next)
1143                 if(ipcmp(addr, p->a) == 0)
1144                         return p->type & (Runi|Rbcast|Rmulti);
1145
1146         /* hack to say accept anything */
1147         if(f->self->acceptall)
1148                 return Runi;
1149
1150         return 0;
1151 }
1152
1153 /*
1154  *  find the ifc on same net as the remote system.  If none,
1155  *  return nil.
1156  */
1157 Ipifc*
1158 findipifc(Fs *f, uchar *local, uchar *remote, int type)
1159 {
1160         uchar gnet[IPaddrlen];
1161         int spec, xspec;
1162         Ipifc *ifc, *x;
1163         Iplifc *lifc;
1164         Conv **cp;
1165
1166         x = nil;
1167         xspec = 0;
1168         for(cp = f->ipifc->conv; *cp != nil; cp++){
1169                 ifc = (Ipifc*)(*cp)->ptcl;
1170                 if(!canrlock(ifc))
1171                         continue;
1172                 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1173                         if(type & Runi){
1174                                 if(ipcmp(remote, lifc->local) == 0){
1175                                 Found:
1176                                         runlock(ifc);
1177                                         return ifc;
1178                                 }
1179                         } else if(type & (Rbcast|Rmulti)) {
1180                                 if(ipcmp(local, lifc->local) == 0)
1181                                         goto Found;
1182                         }
1183                         maskip(remote, lifc->mask, gnet);
1184                         if(ipcmp(gnet, lifc->net) == 0){
1185                                 spec = comprefixlen(remote, lifc->local, IPaddrlen);
1186                                 if(spec > xspec){
1187                                         x = ifc;
1188                                         xspec = spec;
1189                                 }
1190                         }
1191                 }
1192                 runlock(ifc);
1193         }
1194         return x;
1195 }
1196
1197 Ipifc*
1198 findipifcstr(Fs *f, char *s)
1199 {
1200         uchar ip[IPaddrlen];
1201         Conv *c;
1202         char *p;
1203         long x;
1204
1205         x = strtol(s, &p, 10);
1206         if(p > s && *p == '\0'){
1207                 if(x < 0)
1208                         return nil;
1209                 if(x < f->ipifc->nc && (c = f->ipifc->conv[x]) != nil && ipifcinuse(c))
1210                         return (Ipifc*)c->ptcl;
1211         }
1212         if(parseip(ip, s) != -1)
1213                 return findipifc(f, ip, ip, Runi);
1214         return nil;
1215 }
1216
1217 /*
1218  *  find "best" (global > link local > unspecified)
1219  *  local address; address must be current.
1220  */
1221 static void
1222 findprimaryipv6(Fs *f, uchar *local)
1223 {
1224         ulong now = NOW/1000;
1225         int atype, atypel;
1226         Iplifc *lifc;
1227         Ipifc *ifc;
1228         Conv **cp;
1229
1230         ipmove(local, v6Unspecified);
1231         atype = unspecifiedv6;
1232
1233         for(cp = f->ipifc->conv; *cp != nil; cp++){
1234                 ifc = (Ipifc*)(*cp)->ptcl;
1235                 rlock(ifc);
1236                 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1237                         atypel = v6addrtype(lifc->local);
1238                         if(atypel > atype)
1239                         if(lifc->preflt == ~0UL || lifc->preflt >= now-lifc->origint) {
1240                                 ipmove(local, lifc->local);
1241                                 atype = atypel;
1242                                 if(atype == globalv6){
1243                                         runlock(ifc);
1244                                         return;
1245                                 }
1246                         }
1247                 }
1248                 runlock(ifc);
1249         }
1250 }
1251
1252 /*
1253  *  returns first v4 address configured
1254  */
1255 static void
1256 findprimaryipv4(Fs *f, uchar *local)
1257 {
1258         Iplifc *lifc;
1259         Ipifc *ifc;
1260         Conv **cp;
1261
1262         /* find first ifc local address */
1263         for(cp = f->ipifc->conv; *cp != nil; cp++){
1264                 ifc = (Ipifc*)(*cp)->ptcl;
1265                 rlock(ifc);
1266                 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1267                         if((lifc->type & Rv4) != 0){
1268                                 ipmove(local, lifc->local);
1269                                 runlock(ifc);
1270                                 return;
1271                         }
1272                 }
1273                 runlock(ifc);
1274         }
1275         ipmove(local, IPnoaddr);
1276 }
1277
1278 /*
1279  * ipv4local, ipv6local:
1280  *  return a local address associated with an interface close to remote.
1281  *  prefixlen is the number of leading bits in the local address that
1282  *  have to match an interface address to be considered. this is used
1283  *  by source specific routes to filter on the source address.
1284  *  return non-zero on success or zero when no address was found.
1285  *
1286  *  for ipv4local, all addresses are 4 byte format.
1287  */
1288 int
1289 ipv4local(Ipifc *ifc, uchar *local, int prefixlen, uchar *remote)
1290 {
1291         Iplifc *lifc;
1292         int a, b;
1293
1294         b = -1;
1295         for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1296                 if((lifc->type & Rv4) == 0 || ipcmp(lifc->local, IPnoaddr) == 0)
1297                         continue;
1298
1299                 if(prefixlen && comprefixlen(lifc->local+IPv4off, local, IPv4addrlen) < prefixlen)
1300                         continue;
1301                 
1302                 a = comprefixlen(lifc->local+IPv4off, remote, IPv4addrlen);
1303                 if(a > b){
1304                         b = a;
1305                         memmove(local, lifc->local+IPv4off, IPv4addrlen);
1306                 }
1307         }
1308         return b >= 0;
1309 }
1310
1311 int
1312 ipv6local(Ipifc *ifc, uchar *local, int prefixlen, uchar *remote)
1313 {
1314         struct {
1315                 int     atype;
1316                 int     deprecated;
1317                 int     comprefixlen;
1318         } a, b;
1319         int atype;
1320         ulong now;
1321         Iplifc *lifc;
1322
1323         if(isv4(remote)){
1324                 memmove(local, v4prefix, IPv4off);
1325                 if((prefixlen -= IPv4off*8) < 0)
1326                         prefixlen = 0;
1327                 return ipv4local(ifc, local+IPv4off, prefixlen, remote+IPv4off);
1328         }
1329
1330         atype = v6addrtype(remote);
1331         b.atype = unknownv6;
1332         b.deprecated = 1;
1333         b.comprefixlen = 0;
1334
1335         now = NOW/1000;
1336         for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1337                 if(lifc->tentative)
1338                         continue;
1339
1340                 if(prefixlen && comprefixlen(lifc->local, local, IPaddrlen) < prefixlen)
1341                         continue;
1342
1343                 a.atype = v6addrtype(lifc->local);
1344                 a.deprecated = lifc->preflt != ~0UL && lifc->preflt < now-lifc->origint;
1345                 a.comprefixlen = comprefixlen(lifc->local, remote, IPaddrlen);
1346
1347                 /* prefer appropriate scope */
1348                 if(a.atype != b.atype){
1349                         if(a.atype > b.atype && b.atype < atype ||
1350                            a.atype < b.atype && b.atype > atype)
1351                                 goto Good;
1352                         continue;
1353                 }
1354                 /* prefer non-deprecated addresses */
1355                 if(a.deprecated != b.deprecated){
1356                         if(b.deprecated)
1357                                 goto Good;
1358                         continue;
1359                 }
1360                 /* prefer longer common prefix */
1361                 if(a.comprefixlen != b.comprefixlen){
1362                         if(a.comprefixlen > b.comprefixlen)
1363                                 goto Good;
1364                         continue;
1365                 }
1366                 continue;
1367         Good:
1368                 b = a;
1369                 ipmove(local, lifc->local);
1370         }
1371
1372         return b.atype >= atype;
1373 }
1374
1375 /*
1376  *  find the local address for a remote destination
1377  */
1378 void
1379 findlocalip(Fs *f, uchar *local, uchar *remote)
1380 {
1381         if(isv4(remote)) {
1382                 memmove(local, v4prefix, IPv4off);
1383                 if(v4source(f, remote+IPv4off, local+IPv4off) == nil)
1384                         findprimaryipv4(f, local);
1385         } else {
1386                 if(v6source(f, remote, local) == nil)
1387                         findprimaryipv6(f, local);
1388         }
1389 }
1390
1391 /*
1392  *  see if this address is bound to the interface
1393  */
1394 Iplifc*
1395 iplocalonifc(Ipifc *ifc, uchar *ip)
1396 {
1397         Iplifc *lifc;
1398
1399         for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next)
1400                 if(ipcmp(ip, lifc->local) == 0)
1401                         return lifc;
1402
1403         return nil;
1404 }
1405
1406 Iplifc*
1407 ipremoteonifc(Ipifc *ifc, uchar *ip)
1408 {
1409         uchar net[IPaddrlen];
1410         Iplifc *lifc;
1411
1412         for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1413                 maskip(ip, lifc->mask, net);
1414                 if(ipcmp(net, lifc->remote) == 0)
1415                         return lifc;
1416         }
1417         return nil;
1418 }
1419
1420
1421 /*
1422  *  See if we're proxying for this address on this interface
1423  */
1424 int
1425 ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip)
1426 {
1427         Route *r;
1428
1429         /* see if this is a direct connected pt to pt address */
1430         r = v6lookup(f, ip, ip, nil);
1431         if(r == nil || (r->type & (Rifc|Rproxy)) != (Rifc|Rproxy))
1432                 return 0;
1433
1434         return ipremoteonifc(ifc, ip) != nil;
1435 }
1436
1437 /*
1438  *  return multicast version if any
1439  */
1440 int
1441 ipismulticast(uchar *ip)
1442 {
1443         if(isv4(ip)){
1444                 if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
1445                         return V4;
1446         }
1447         else if(ip[0] == 0xff)
1448                 return V6;
1449         return 0;
1450 }
1451
1452 /*
1453  *  add a multicast address to an interface.
1454  */
1455 void
1456 ipifcaddmulti(Conv *c, uchar *ma, uchar *ia)
1457 {
1458         Ipmulti *multi, **l;
1459         Iplifc *lifc;
1460         Ipifc *ifc;
1461         Fs *f;
1462
1463         if(isv4(ma) != isv4(ia))
1464                 error("incompatible multicast/interface ip address");
1465
1466         for(l = &c->multi; *l != nil; l = &(*l)->next)
1467                 if(ipcmp(ma, (*l)->ma) == 0 && ipcmp(ia, (*l)->ia) == 0)
1468                         return;         /* it's already there */
1469
1470         f = c->p->f;
1471         if((ifc = findipifc(f, ia, ma, Rmulti)) != nil){
1472                 rlock(ifc);
1473                 if(waserror()){
1474                         runlock(ifc);
1475                         nexterror();
1476                 }
1477                 if((lifc = iplocalonifc(ifc, ia)) != nil)
1478                         addselfcache(f, ifc, lifc, ma, Rmulti);
1479                 runlock(ifc);
1480                 poperror();
1481         }
1482
1483         multi = smalloc(sizeof(*multi));
1484         ipmove(multi->ma, ma);
1485         ipmove(multi->ia, ia);
1486         multi->next = nil;
1487         *l = multi;
1488 }
1489
1490
1491 /*
1492  *  remove a multicast address from an interface.
1493  */
1494 void
1495 ipifcremmulti(Conv *c, uchar *ma, uchar *ia)
1496 {
1497         Ipmulti *multi, **l;
1498         Iplifc *lifc;
1499         Ipifc *ifc;
1500         Fs *f;
1501
1502         for(l = &c->multi; *l != nil; l = &(*l)->next)
1503                 if(ipcmp(ma, (*l)->ma) == 0 && ipcmp(ia, (*l)->ia) == 0)
1504                         break;
1505
1506         multi = *l;
1507         if(multi == nil)
1508                 return;         /* we don't have it open */
1509
1510         *l = multi->next;
1511         multi->next = nil;
1512
1513         f = c->p->f;
1514         if((ifc = findipifc(f, ia, ma, Rmulti)) != nil){
1515                 rlock(ifc);
1516                 if(!waserror()){
1517                         if((lifc = iplocalonifc(ifc, ia)) != nil)
1518                                 remselfcache(f, ifc, lifc, ma);
1519                         poperror();
1520                 }
1521                 runlock(ifc);
1522         }
1523         free(multi);
1524 }
1525
1526 /* register the address on this network for address resolution */
1527 static void
1528 ipifcregisteraddr(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *ip)
1529 {
1530         if(waserror()){
1531                 print("ipifcregisteraddr %s %I %I: %s\n", ifc->dev, lifc->local, ip, up->errstr);
1532                 return;
1533         }
1534         if(ifc->m != nil && ifc->m->areg != nil)
1535                 (*ifc->m->areg)(f, ifc, lifc, ip);
1536         poperror();
1537 }
1538
1539 static void
1540 ipifcregisterproxy(Fs *f, Ipifc *ifc, uchar *ip, int add)
1541 {
1542         uchar a[IPaddrlen];
1543         Iplifc *lifc;
1544         Ipifc *nifc;
1545         Conv **cp;
1546
1547         /* register the address on any interface that will proxy for the ip */
1548         for(cp = f->ipifc->conv; *cp != nil; cp++){
1549                 nifc = (Ipifc*)(*cp)->ptcl;
1550                 if(nifc == ifc || !canrlock(nifc))
1551                         continue;
1552
1553                 if(nifc->m == nil
1554                 || (lifc = ipremoteonifc(nifc, ip)) == nil
1555                 || (lifc->type & Rptpt) != 0
1556                 || waserror()){
1557                         runlock(nifc);
1558                         continue;
1559                 }
1560                 if((lifc->type & Rv4) == 0){
1561                         /* add solicited-node multicast addr */
1562                         ipv62smcast(a, ip);
1563                         if(add)
1564                                 addselfcache(f, nifc, lifc, a, Rmulti);
1565                         else
1566                                 remselfcache(f, nifc, lifc, a);
1567                 }
1568                 if(add)
1569                         ipifcregisteraddr(f, nifc, lifc, ip);
1570                 runlock(nifc);
1571                 poperror();
1572         }
1573 }
1574
1575 char*
1576 ipifcadd6(Ipifc *ifc, char **argv, int argc)
1577 {
1578         int plen = 64;
1579         char addr[40], preflen[6];
1580         char *params[3];
1581         uchar prefix[IPaddrlen];
1582         Iplifc lifc;
1583         Medium *m;
1584
1585         lifc.onlink = 1;
1586         lifc.autoflag = 1;
1587         lifc.validlt = lifc.preflt = ~0UL;
1588         lifc.origint = NOW / 1000;
1589
1590         switch(argc) {
1591         case 7:
1592                 lifc.preflt = strtoul(argv[6], 0, 10);
1593                 /* fall through */
1594         case 6:
1595                 lifc.validlt = strtoul(argv[5], 0, 10);
1596                 /* fall through */
1597         case 5:
1598                 lifc.autoflag = atoi(argv[4]) != 0;
1599                 /* fall through */
1600         case 4:
1601                 lifc.onlink = atoi(argv[3]) != 0;
1602                 /* fall through */
1603         case 3:
1604                 plen = atoi(argv[2]);
1605                 /* fall through */
1606         case 2:
1607                 break;
1608         default:
1609                 return Ebadarg;
1610         }
1611
1612         if (parseip(prefix, argv[1]) != 6 || lifc.validlt < lifc.preflt || plen < 0 ||
1613             plen > 64 || islinklocal(prefix))
1614                 return Ebadarg;
1615
1616         /* issue "add" ctl msg for v6 link-local addr and prefix len */
1617         m = ifc->m;
1618         if(m == nil || m->pref2addr == nil)
1619                 return Eunbound;
1620         (*m->pref2addr)(prefix, ifc->mac);      /* mac → v6 link-local addr */
1621
1622         sprint(addr, "%I", prefix);
1623         sprint(preflen, "/%d", plen);
1624         params[0] = "add";
1625         params[1] = addr;
1626         params[2] = preflen;
1627
1628         return ipifcadd(ifc, params, 3, 0, &lifc);
1629 }
1630
1631 char*
1632 ipifcremove6(Ipifc *ifc, char**, int argc)
1633 {
1634         Iplifc *lifc, **l;
1635         ulong now;
1636
1637         if(argc != 1)
1638                 return Ebadarg;
1639
1640         wlock(ifc);
1641         now = NOW/1000;
1642         for(l = &ifc->lifc; (lifc = *l) != nil;) {
1643                 if((lifc->type & Rv4) == 0)
1644                 if(lifc->validlt != ~0UL && lifc->validlt < now-lifc->origint)
1645                         if(ipifcremlifc(ifc, l) == nil)
1646                                 continue;
1647                 l = &lifc->next;
1648         }
1649         wunlock(ifc);
1650
1651         return nil;
1652 }