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