]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/ip/ipifc.c
devip: fix use after free in ipifcremmulti()
[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                 return "ipifc not yet bound to device";
503
504         f = ifc->conv->p->f;
505         if(waserror()){
506                 wunlock(ifc);
507                 return up->errstr;
508         }
509
510         if(mtu > 0 && mtu >= ifc->m->mintu && mtu <= ifc->m->maxtu)
511                 ifc->maxtu = mtu;
512
513         /* ignore if this is already a local address for this ifc */
514         if((lifc = iplocalonifc(ifc, ip)) != nil){
515                 if(lifcp != nil) {
516                         lifc->onlink = lifcp->onlink;
517                         lifc->autoflag = lifcp->autoflag;
518                         lifc->validlt = lifcp->validlt;
519                         lifc->preflt = lifcp->preflt;
520                         lifc->origint = lifcp->origint;
521                 }
522                 if(lifc->tentative != tentative){
523                         lifc->tentative = tentative;
524                         goto done;
525                 }
526                 wunlock(ifc);
527                 poperror();
528                 return nil;
529         }
530
531         /* add the address to the list of logical ifc's for this ifc */
532         lifc = smalloc(sizeof(Iplifc));
533         ipmove(lifc->local, ip);
534         ipmove(lifc->mask, mask);
535         ipmove(lifc->remote, rem);
536         ipmove(lifc->net, net);
537         lifc->type = type;
538         lifc->tentative = tentative;
539         if(lifcp != nil) {
540                 lifc->onlink = lifcp->onlink;
541                 lifc->autoflag = lifcp->autoflag;
542                 lifc->validlt = lifcp->validlt;
543                 lifc->preflt = lifcp->preflt;
544                 lifc->origint = lifcp->origint;
545         } else {                /* default values */
546                 lifc->onlink = lifc->autoflag = 1;
547                 lifc->validlt = lifc->preflt = ~0L;
548                 lifc->origint = NOW / 1000;
549         }
550         lifc->next = nil;
551
552         for(l = &ifc->lifc; *l != nil; l = &(*l)->next)
553                 ;
554         *l = lifc;
555
556         /* add route for this logical interface */
557         addroute(f, rem, mask, ip, IPallbits, rem, type, ifc, tifc);
558         if(v6addrtype(ip) != linklocalv6)
559                 addroute(f, rem, mask, ip, IPnoaddr, rem, type, ifc, tifc);
560
561         addselfcache(f, ifc, lifc, ip, Runi);
562
563         /* register proxy */
564         if(type & Rptpt){
565                 if(type & Rproxy)
566                         ipifcregisterproxy(f, ifc, rem, 1);
567                 goto done;
568         }
569
570         if(type & Rv4) {
571                 /* add subnet directed broadcast address to the self cache */
572                 for(i = 0; i < IPaddrlen; i++)
573                         bcast[i] = (ip[i] & mask[i]) | ~mask[i];
574                 addselfcache(f, ifc, lifc, bcast, Rbcast);
575
576                 /* add subnet directed network address to the self cache */
577                 for(i = 0; i < IPaddrlen; i++)
578                         bcast[i] = (ip[i] & mask[i]) & mask[i];
579                 addselfcache(f, ifc, lifc, bcast, Rbcast);
580
581                 /* add network directed broadcast address to the self cache */
582                 memmove(mask, defmask(ip), IPaddrlen);
583                 for(i = 0; i < IPaddrlen; i++)
584                         bcast[i] = (ip[i] & mask[i]) | ~mask[i];
585                 addselfcache(f, ifc, lifc, bcast, Rbcast);
586
587                 /* add network directed network address to the self cache */
588                 memmove(mask, defmask(ip), IPaddrlen);
589                 for(i = 0; i < IPaddrlen; i++)
590                         bcast[i] = (ip[i] & mask[i]) & mask[i];
591                 addselfcache(f, ifc, lifc, bcast, Rbcast);
592
593                 addselfcache(f, ifc, lifc, IPv4bcast, Rbcast);
594         } else {
595                 if(ipcmp(ip, v6loopback) == 0) {
596                         /* add node-local mcast address */
597                         addselfcache(f, ifc, lifc, v6allnodesN, Rmulti);
598
599                         /* add route for all node multicast */
600                         addroute(f, v6allnodesN, v6allnodesNmask,
601                                 ip, IPallbits,
602                                 v6allnodesN, Rmulti, ifc, tifc);
603                 }
604
605                 /* add all nodes multicast address */
606                 addselfcache(f, ifc, lifc, v6allnodesL, Rmulti);
607
608                 /* add route for all nodes multicast */
609                 addroute(f, v6allnodesL, v6allnodesLmask,
610                         ip, IPallbits,
611                         v6allnodesL, Rmulti, ifc, tifc);
612
613                 /* add solicited-node multicast address */
614                 ipv62smcast(bcast, ip);
615                 addselfcache(f, ifc, lifc, bcast, Rmulti);
616         }
617
618 done:
619         wunlock(ifc);
620         poperror();
621
622         ipifcregisteraddr(f, ifc, ip, ip);
623
624         return nil;
625 }
626
627 /*
628  *  remove a logical interface from an ifc
629  *  always called with ifc wlock'd
630  */
631 static char*
632 ipifcremlifc(Ipifc *ifc, Iplifc **l)
633 {
634         Iplifc *lifc = *l;
635         Fs *f = ifc->conv->p->f;
636
637         if(lifc == nil)
638                 return "address not on this interface";
639         *l = lifc->next;
640
641         /* disassociate any addresses */
642         while(lifc->link != nil)
643                 remselfcache(f, ifc, lifc, lifc->link->self->a);
644
645         /* remove the route for this logical interface */
646         remroute(f, lifc->remote, lifc->mask,
647                 lifc->local, IPallbits,
648                 lifc->remote, lifc->type, ifc, tifc);
649         if(v6addrtype(lifc->local) != linklocalv6)
650                 remroute(f, lifc->remote, lifc->mask,
651                         lifc->local, IPnoaddr,
652                         lifc->remote, lifc->type, ifc, tifc);
653
654         /* unregister proxy */
655         if(lifc->type & Rptpt){
656                 if(lifc->type & Rproxy)
657                         ipifcregisterproxy(f, ifc, lifc->remote, 0);
658                 goto done;
659         }
660
661         /* remove route for all nodes multicast */
662         if((lifc->type & Rv4) == 0){
663                 if(ipcmp(lifc->local, v6loopback) == 0)
664                         remroute(f, v6allnodesN, v6allnodesNmask,
665                                 lifc->local, IPallbits,
666                                 v6allnodesN, Rmulti, ifc, tifc);
667
668                 remroute(f, v6allnodesL, v6allnodesLmask,
669                         lifc->local, IPallbits,
670                         v6allnodesL, Rmulti, ifc, tifc);
671         }
672
673 done:
674         free(lifc);
675         return nil;
676 }
677
678 /*
679  *  remove an address from an interface.
680  *  called with c->car locked
681  */
682 char*
683 ipifcrem(Ipifc *ifc, char **argv, int argc)
684 {
685         char *rv;
686         uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen];
687         Iplifc *lifc, **l;
688
689         if(argc < 3)
690                 return Ebadarg;
691         if(parseip(ip, argv[1]) == -1)
692                 return Ebadip;
693         parseipmask(mask, argv[2]);
694         if(argc < 4)
695                 maskip(ip, mask, rem);
696         else if(parseip(rem, argv[3]) == -1)
697                 return Ebadip;
698
699         /*
700          *  find address on this interface and remove from chain.
701          *  for pt to pt we actually specify the remote address as the
702          *  addresss to remove.
703          */
704         wlock(ifc);
705         l = &ifc->lifc;
706         for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next) {
707                 if(ipcmp(ip, lifc->local) == 0
708                 && ipcmp(mask, lifc->mask) == 0
709                 && ipcmp(rem, lifc->remote) == 0)
710                         break;
711                 l = &lifc->next;
712         }
713         rv = ipifcremlifc(ifc, l);
714         wunlock(ifc);
715         return rv;
716 }
717
718 /*
719  *  associate an address with the interface.  This wipes out any previous
720  *  addresses.  This is a macro that means, remove all the old interfaces
721  *  and add a new one.
722  */
723 static char*
724 ipifcconnect(Conv* c, char **argv, int argc)
725 {
726         char *err;
727         Ipifc *ifc;
728
729         ifc = (Ipifc*)c->ptcl;
730
731         if(ifc->m == nil)
732                  return "ipifc not yet bound to device";
733
734         wlock(ifc);
735         while(ifc->lifc != nil){
736                 err = ipifcremlifc(ifc, &ifc->lifc);
737                 if(err != nil){
738                         wunlock(ifc);
739                         return err;
740                 }
741         }
742         wunlock(ifc);
743
744         err = ipifcadd(ifc, argv, argc, 0, nil);
745         if(err != nil)
746                 return err;
747
748         Fsconnected(c, nil);
749         return nil;
750 }
751
752 char*
753 ipifcra6(Ipifc *ifc, char **argv, int argc)
754 {
755         int i, argsleft, vmax = ifc->rp.maxraint, vmin = ifc->rp.minraint;
756
757         argsleft = argc - 1;
758         i = 1;
759
760         if(argsleft % 2 != 0)
761                 return Ebadarg;
762
763         while (argsleft > 1) {
764                 if(strcmp(argv[i], "recvra") == 0)
765                         ifc->recvra6 = (atoi(argv[i+1]) != 0);
766                 else if(strcmp(argv[i], "sendra") == 0)
767                         ifc->sendra6 = (atoi(argv[i+1]) != 0);
768                 else if(strcmp(argv[i], "mflag") == 0)
769                         ifc->rp.mflag = (atoi(argv[i+1]) != 0);
770                 else if(strcmp(argv[i], "oflag") == 0)
771                         ifc->rp.oflag = (atoi(argv[i+1]) != 0);
772                 else if(strcmp(argv[i], "maxraint") == 0)
773                         ifc->rp.maxraint = atoi(argv[i+1]);
774                 else if(strcmp(argv[i], "minraint") == 0)
775                         ifc->rp.minraint = atoi(argv[i+1]);
776                 else if(strcmp(argv[i], "linkmtu") == 0)
777                         ifc->rp.linkmtu = atoi(argv[i+1]);
778                 else if(strcmp(argv[i], "reachtime") == 0)
779                         ifc->rp.reachtime = atoi(argv[i+1]);
780                 else if(strcmp(argv[i], "rxmitra") == 0)
781                         ifc->rp.rxmitra = atoi(argv[i+1]);
782                 else if(strcmp(argv[i], "ttl") == 0)
783                         ifc->rp.ttl = atoi(argv[i+1]);
784                 else if(strcmp(argv[i], "routerlt") == 0)
785                         ifc->rp.routerlt = atoi(argv[i+1]);
786                 else
787                         return Ebadarg;
788
789                 argsleft -= 2;
790                 i += 2;
791         }
792
793         /* consistency check */
794         if(ifc->rp.maxraint < ifc->rp.minraint) {
795                 ifc->rp.maxraint = vmax;
796                 ifc->rp.minraint = vmin;
797                 return Ebadarg;
798         }
799         return nil;
800 }
801
802 /*
803  *  non-standard control messages.
804  *  called with c->car locked.
805  */
806 static char*
807 ipifcctl(Conv* c, char **argv, int argc)
808 {
809         Ipifc *ifc;
810
811         ifc = (Ipifc*)c->ptcl;
812         if(strcmp(argv[0], "add") == 0)
813                 return ipifcadd(ifc, argv, argc, 0, nil);
814         else if(strcmp(argv[0], "try") == 0)
815                 return ipifcadd(ifc, argv, argc, 1, nil);
816         else if(strcmp(argv[0], "remove") == 0)
817                 return ipifcrem(ifc, argv, argc);
818         else if(strcmp(argv[0], "unbind") == 0)
819                 return ipifcunbind(ifc);
820         else if(strcmp(argv[0], "mtu") == 0)
821                 return ipifcsetmtu(ifc, argv, argc);
822         else if(strcmp(argv[0], "speed") == 0){
823                 ipifcsetspeed(ifc, argc>1? atoi(argv[1]): 0);
824                 return nil;
825         }
826         else if(strcmp(argv[0], "delay") == 0){
827                 ipifcsetdelay(ifc, argc>1? atoi(argv[1]): 0);
828                 return nil;
829         }
830         else if(strcmp(argv[0], "iprouting") == 0){
831                 iprouting(c->p->f, argc>1? atoi(argv[1]): 1);
832                 return nil;
833         }
834         else if(strcmp(argv[0], "reflect") == 0){
835                 ifc->reflect = argc>1? atoi(argv[1]): 1;
836                 return nil;
837         }
838         else if(strcmp(argv[0], "reassemble") == 0){
839                 ifc->reassemble = argc>1? atoi(argv[1]): 1;
840                 return nil;
841         }
842         else if(strcmp(argv[0], "add6") == 0)
843                 return ipifcadd6(ifc, argv, argc);
844         else if(strcmp(argv[0], "ra6") == 0)
845                 return ipifcra6(ifc, argv, argc);
846         return "unsupported ctl";
847 }
848
849 int
850 ipifcstats(Proto *ipifc, char *buf, int len)
851 {
852         return ipstats(ipifc->f, buf, len);
853 }
854
855 void
856 ipifcinit(Fs *f)
857 {
858         Proto *ipifc;
859
860         ipifc = smalloc(sizeof(Proto));
861         ipifc->name = "ipifc";
862         ipifc->connect = ipifcconnect;
863         ipifc->announce = nil;
864         ipifc->bind = ipifcbind;
865         ipifc->state = ipifcstate;
866         ipifc->create = ipifccreate;
867         ipifc->close = ipifcclose;
868         ipifc->rcv = nil;
869         ipifc->ctl = ipifcctl;
870         ipifc->advise = nil;
871         ipifc->stats = ipifcstats;
872         ipifc->inuse = ipifcinuse;
873         ipifc->local = ipifclocal;
874         ipifc->ipproto = -1;
875         ipifc->nc = Maxmedia;
876         ipifc->ptclsize = sizeof(Ipifc);
877
878         f->ipifc = ipifc;       /* hack for ipifcremroute, findipifc, ... */
879         f->self = smalloc(sizeof(Ipselftab));   /* hack for ipforme */
880
881         Fsproto(f, ipifc);
882 }
883
884 /*
885  *  add to self routing cache
886  *      called with c->car locked
887  */
888 static void
889 addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type)
890 {
891         Iplink *lp;
892         Ipself *p;
893         int h;
894
895         type |= (lifc->type & Rv4);
896
897         qlock(f->self);
898         if(waserror()){
899                 qunlock(f->self);
900                 nexterror();
901         }
902
903         /* see if the address already exists */
904         h = hashipa(a);
905         for(p = f->self->hash[h]; p != nil; p = p->next)
906                 if(ipcmp(a, p->a) == 0)
907                         break;
908
909         /* allocate a local address and add to hash chain */
910         if(p == nil){
911                 p = smalloc(sizeof(*p));
912                 ipmove(p->a, a);
913                 p->type = type;
914                 p->next = f->self->hash[h];
915                 f->self->hash[h] = p;
916
917                 /* if the null address, accept all packets */
918                 if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0)
919                         f->self->acceptall = 1;
920         }
921
922         /* look for a link for this lifc */
923         for(lp = p->link; lp != nil; lp = lp->selflink)
924                 if(lp->lifc == lifc)
925                         break;
926
927         /* allocate a lifc-to-local link and link to both */
928         if(lp == nil){
929                 lp = smalloc(sizeof(*lp));
930                 lp->ref = 1;
931                 lp->lifc = lifc;
932                 lp->self = p;
933                 lp->selflink = p->link;
934                 p->link = lp;
935                 lp->lifclink = lifc->link;
936                 lifc->link = lp;
937
938                 /* add to routing table */
939                 addroute(f, a, IPallbits,
940                         lifc->local, 
941                         ((type & (Rbcast|Rmulti)) != 0 || v6addrtype(a) == linklocalv6) ?
942                                 IPallbits : IPnoaddr,
943                         a, type, ifc, tifc);
944
945                 if((type & Rmulti) && ifc->m->addmulti != nil)
946                         (*ifc->m->addmulti)(ifc, a, lifc->local);
947         } else
948                 lp->ref++;
949
950         qunlock(f->self);
951         poperror();
952 }
953
954 /*
955  *  These structures are unlinked from their chains while
956  *  other threads may be using them.  To avoid excessive locking,
957  *  just put them aside for a while before freeing them.
958  *      called with f->self locked
959  */
960 static Iplink *freeiplink;
961 static Ipself *freeipself;
962
963 static void
964 iplinkfree(Iplink *p)
965 {
966         Iplink **l, *np;
967         ulong now = NOW;
968
969         l = &freeiplink;
970         for(np = *l; np != nil; np = *l){
971                 if((long)(now - np->expire) >= 0){
972                         *l = np->next;
973                         free(np);
974                         continue;
975                 }
976                 l = &np->next;
977         }
978         p->expire = now + 5000; /* give other threads 5 secs to get out */
979         p->next = nil;
980         *l = p;
981 }
982
983 static void
984 ipselffree(Ipself *p)
985 {
986         Ipself **l, *np;
987         ulong now = NOW;
988
989         l = &freeipself;
990         for(np = *l; np != nil; np = *l){
991                 if((long)(now - np->expire) >= 0){
992                         *l = np->next;
993                         free(np);
994                         continue;
995                 }
996                 l = &np->next;
997         }
998         p->expire = now + 5000; /* give other threads 5 secs to get out */
999         p->next = nil;
1000         *l = p;
1001 }
1002
1003 /*
1004  *  Decrement reference for this address on this link.
1005  *  Unlink from selftab if this is the last ref.
1006  *      called with c->car locked
1007  */
1008 static void
1009 remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a)
1010 {
1011         Ipself *p, **l;
1012         Iplink *link, **l_self, **l_lifc;
1013
1014         qlock(f->self);
1015
1016         /* find the unique selftab entry */
1017         l = &f->self->hash[hashipa(a)];
1018         for(p = *l; p != nil; p = *l){
1019                 if(ipcmp(p->a, a) == 0)
1020                         break;
1021                 l = &p->next;
1022         }
1023
1024         if(p == nil)
1025                 goto out;
1026
1027         /*
1028          *  walk down links from an ifc looking for one
1029          *  that matches the selftab entry
1030          */
1031         l_lifc = &lifc->link;
1032         for(link = *l_lifc; link != nil; link = *l_lifc){
1033                 if(link->self == p)
1034                         break;
1035                 l_lifc = &link->lifclink;
1036         }
1037
1038         if(link == nil)
1039                 goto out;
1040
1041         /*
1042          *  walk down the links from the selftab looking for
1043          *  the one we just found
1044          */
1045         l_self = &p->link;
1046         for(link = *l_self; link != nil; link = *l_self){
1047                 if(link == *l_lifc)
1048                         break;
1049                 l_self = &link->selflink;
1050         }
1051
1052         if(link == nil)
1053                 panic("remselfcache");
1054
1055         if(--(link->ref) != 0)
1056                 goto out;
1057
1058         /* remove from routing table */
1059         remroute(f, a, IPallbits,
1060                 lifc->local, 
1061                 ((p->type & (Rbcast|Rmulti)) != 0 || v6addrtype(a) == linklocalv6) ?
1062                         IPallbits : IPnoaddr,
1063                 a, p->type, ifc, tifc);
1064
1065         if((p->type & Rmulti) && ifc->m->remmulti != nil){
1066                 if(!waserror()){
1067                         (*ifc->m->remmulti)(ifc, a, lifc->local);
1068                         poperror();
1069                 }
1070         }
1071
1072         /* ref == 0, remove from both chains and free the link */
1073         *l_lifc = link->lifclink;
1074         *l_self = link->selflink;
1075         iplinkfree(link);
1076
1077         if(p->link != nil)
1078                 goto out;
1079
1080         /* if null address, forget */
1081         if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0)
1082                 f->self->acceptall = 0;
1083
1084         /* no more links, remove from hash and free */
1085         *l = p->next;
1086         ipselffree(p);
1087
1088 out:
1089         qunlock(f->self);
1090 }
1091
1092 long
1093 ipselftabread(Fs *f, char *cp, ulong offset, int n)
1094 {
1095         int i, m, nifc, off;
1096         Ipself *p;
1097         Iplink *link;
1098         char state[8];
1099
1100         m = 0;
1101         off = offset;
1102         qlock(f->self);
1103         for(i = 0; i < NHASH && m < n; i++){
1104                 for(p = f->self->hash[i]; p != nil && m < n; p = p->next){
1105                         nifc = 0;
1106                         for(link = p->link; link != nil; link = link->selflink)
1107                                 nifc++;
1108                         routetype(p->type, state);
1109                         m += snprint(cp + m, n - m, "%-44.44I %2.2d %4.4s\n",
1110                                 p->a, nifc, state);
1111                         if(off > 0){
1112                                 off -= m;
1113                                 m = 0;
1114                         }
1115                 }
1116         }
1117         qunlock(f->self);
1118         return m;
1119 }
1120
1121 /*
1122  *  returns
1123  *      0               - no match
1124  *      Runi
1125  *      Rbcast
1126  *      Rmulti
1127  */
1128 int
1129 ipforme(Fs *f, uchar *addr)
1130 {
1131         Ipself *p;
1132
1133         for(p = f->self->hash[hashipa(addr)]; p != nil; p = p->next)
1134                 if(ipcmp(addr, p->a) == 0)
1135                         return p->type & (Runi|Rbcast|Rmulti);
1136
1137         /* hack to say accept anything */
1138         if(f->self->acceptall)
1139                 return Runi;
1140
1141         return 0;
1142 }
1143
1144 /*
1145  *  find the ifc on same net as the remote system.  If none,
1146  *  return nil.
1147  */
1148 Ipifc*
1149 findipifc(Fs *f, uchar *local, uchar *remote, int type)
1150 {
1151         uchar gnet[IPaddrlen];
1152         int spec, xspec;
1153         Ipifc *ifc, *x;
1154         Iplifc *lifc;
1155         Conv **cp;
1156
1157         x = nil;
1158         xspec = 0;
1159         for(cp = f->ipifc->conv; *cp != nil; cp++){
1160                 ifc = (Ipifc*)(*cp)->ptcl;
1161                 rlock(ifc);
1162                 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1163                         if(type & Runi){
1164                                 if(ipcmp(remote, lifc->local) == 0){
1165                                 Found:
1166                                         runlock(ifc);
1167                                         return ifc;
1168                                 }
1169                         } else if(type & (Rbcast|Rmulti)) {
1170                                 if(ipcmp(local, lifc->local) == 0)
1171                                         goto Found;
1172                         }
1173                         maskip(remote, lifc->mask, gnet);
1174                         if(ipcmp(gnet, lifc->net) == 0){
1175                                 spec = comprefixlen(remote, lifc->local, IPaddrlen);
1176                                 if(spec > xspec){
1177                                         x = ifc;
1178                                         xspec = spec;
1179                                 }
1180                         }
1181                 }
1182                 runlock(ifc);
1183         }
1184         return x;
1185 }
1186
1187 Ipifc*
1188 findipifcstr(Fs *f, char *s)
1189 {
1190         uchar ip[IPaddrlen];
1191         Conv *c;
1192         char *p;
1193         long x;
1194
1195         x = strtol(s, &p, 10);
1196         if(p > s && *p == '\0'){
1197                 if(x < 0)
1198                         return nil;
1199                 if(x < f->ipifc->nc && (c = f->ipifc->conv[x]) != nil && ipifcinuse(c))
1200                         return (Ipifc*)c->ptcl;
1201         }
1202         if(parseip(ip, s) != -1)
1203                 return findipifc(f, ip, ip, Runi);
1204         return nil;
1205 }
1206
1207 /*
1208  *  find "best" (global > link local > unspecified)
1209  *  local address; address must be current.
1210  */
1211 static void
1212 findprimaryipv6(Fs *f, uchar *local)
1213 {
1214         int atype, atypel;
1215         Iplifc *lifc;
1216         Ipifc *ifc;
1217         Conv **cp;
1218
1219         ipmove(local, v6Unspecified);
1220         atype = unspecifiedv6;
1221
1222         for(cp = f->ipifc->conv; *cp != nil; cp++){
1223                 ifc = (Ipifc*)(*cp)->ptcl;
1224                 rlock(ifc);
1225                 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1226                         atypel = v6addrtype(lifc->local);
1227                         if(atypel > atype && v6addrcurr(lifc)) {
1228                                 ipmove(local, lifc->local);
1229                                 atype = atypel;
1230                                 if(atype == globalv6){
1231                                         runlock(ifc);
1232                                         return;
1233                                 }
1234                         }
1235                 }
1236                 runlock(ifc);
1237         }
1238 }
1239
1240 /*
1241  *  returns first v4 address configured
1242  */
1243 static void
1244 findprimaryipv4(Fs *f, uchar *local)
1245 {
1246         Iplifc *lifc;
1247         Ipifc *ifc;
1248         Conv **cp;
1249
1250         /* find first ifc local address */
1251         for(cp = f->ipifc->conv; *cp != nil; cp++){
1252                 ifc = (Ipifc*)(*cp)->ptcl;
1253                 rlock(ifc);
1254                 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1255                         if((lifc->type & Rv4) != 0){
1256                                 ipmove(local, lifc->local);
1257                                 runlock(ifc);
1258                                 return;
1259                         }
1260                 }
1261                 runlock(ifc);
1262         }
1263         ipmove(local, IPnoaddr);
1264 }
1265
1266 /*
1267  *  return v4 address associated with an interface close to remote
1268  */
1269 int
1270 ipv4local(Ipifc *ifc, uchar *local, uchar *remote)
1271 {
1272         Iplifc *lifc;
1273         int a, b;
1274
1275         b = -1;
1276         for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1277                 if((lifc->type & Rv4) == 0)
1278                         continue;
1279                 a = comprefixlen(lifc->local+IPv4off, remote, IPv4addrlen);
1280                 if(a > b){
1281                         b = a;
1282                         memmove(local, lifc->local+IPv4off, IPv4addrlen);
1283                 }
1284         }
1285         return b >= 0;
1286 }
1287
1288 /*
1289  *  return v6 address associated with an interface close to remote
1290  */
1291 int
1292 ipv6local(Ipifc *ifc, uchar *local, uchar *remote)
1293 {
1294         struct {
1295                 int     atype;
1296                 int     deprecated;
1297                 int     comprefixlen;
1298         } a, b;
1299         int atype;
1300         Iplifc *lifc;
1301
1302         if(isv4(remote)){
1303                 ipmove(local, v4prefix);
1304                 return ipv4local(ifc, local+IPv4off, remote+IPv4off);
1305         }
1306
1307         atype = v6addrtype(remote);
1308         ipmove(local, v6Unspecified);
1309         b.atype = unknownv6;
1310         b.deprecated = 1;
1311         b.comprefixlen = 0;
1312
1313         for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1314                 if(lifc->tentative)
1315                         continue;
1316
1317                 a.atype = v6addrtype(lifc->local);
1318                 a.deprecated = !v6addrcurr(lifc);
1319                 a.comprefixlen = comprefixlen(lifc->local, remote, IPaddrlen);
1320
1321                 /* prefer appropriate scope */
1322                 if(a.atype != b.atype){
1323                         if(a.atype > b.atype && b.atype < atype ||
1324                            a.atype < b.atype && b.atype > atype)
1325                                 goto Good;
1326                         continue;
1327                 }
1328                 /* prefer non-deprecated addresses */
1329                 if(a.deprecated != b.deprecated){
1330                         if(b.deprecated)
1331                                 goto Good;
1332                         continue;
1333                 }
1334                 /* prefer longer common prefix */
1335                 if(a.comprefixlen != b.comprefixlen){
1336                         if(a.comprefixlen > b.comprefixlen)
1337                                 goto Good;
1338                         continue;
1339                 }
1340                 continue;
1341         Good:
1342                 b = a;
1343                 ipmove(local, lifc->local);
1344         }
1345
1346         return b.atype >= atype;
1347 }
1348
1349 void
1350 findlocalip(Fs *f, uchar *local, uchar *remote)
1351 {
1352         Route *r;
1353         Iplifc *lifc;
1354         Ipifc *ifc, *nifc;
1355         Conv **cp;
1356
1357         for(cp = f->ipifc->conv; *cp != nil; cp++){
1358                 ifc = (Ipifc*)(*cp)->ptcl;
1359                 rlock(ifc);
1360                 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1361                         if(lifc->tentative)
1362                                 continue;
1363
1364                         r = v6lookup(f, remote, lifc->local, nil);
1365                         if(r == nil || (nifc = r->ifc) == nil)
1366                                 continue;
1367                         if(r->type & Runi){
1368                                 ipmove(local, remote);
1369                                 runlock(ifc);
1370                                 return;
1371                         }
1372                         if(nifc != ifc) rlock(nifc);
1373                         if((r->type & (Rifc|Rbcast|Rmulti|Rv4)) == Rv4){
1374                                 ipmove(local, v4prefix);
1375                                 if(ipv4local(nifc, local+IPv4off, r->v4.gate)){
1376                                         if(nifc != ifc) runlock(nifc);
1377                                         runlock(ifc);
1378                                         return;
1379                                 }
1380                         }
1381                         if(ipv6local(nifc, local, remote)){
1382                                 if(nifc != ifc) runlock(nifc);
1383                                 runlock(ifc);
1384                                 return;
1385                         }
1386                         if(nifc != ifc) runlock(nifc);
1387                 }
1388                 runlock(ifc);
1389         }
1390         if(isv4(remote))
1391                 findprimaryipv4(f, local);
1392         else
1393                 findprimaryipv6(f, local);
1394 }
1395
1396
1397 /*
1398  *  see if this address is bound to the interface
1399  */
1400 Iplifc*
1401 iplocalonifc(Ipifc *ifc, uchar *ip)
1402 {
1403         Iplifc *lifc;
1404
1405         for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next)
1406                 if(ipcmp(ip, lifc->local) == 0)
1407                         return lifc;
1408
1409         return nil;
1410 }
1411
1412 Iplifc*
1413 ipremoteonifc(Ipifc *ifc, uchar *ip)
1414 {
1415         uchar net[IPaddrlen];
1416         Iplifc *lifc;
1417
1418         for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1419                 maskip(ip, lifc->mask, net);
1420                 if(ipcmp(net, lifc->remote) == 0)
1421                         return lifc;
1422         }
1423         return nil;
1424 }
1425
1426
1427 /*
1428  *  See if we're proxying for this address on this interface
1429  */
1430 int
1431 ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip)
1432 {
1433         Route *r;
1434
1435         /* see if this is a direct connected pt to pt address */
1436         r = v6lookup(f, ip, ip, nil);
1437         if(r == nil || (r->type & (Rifc|Rproxy)) != (Rifc|Rproxy))
1438                 return 0;
1439
1440         return ipremoteonifc(ifc, ip) != nil;
1441 }
1442
1443 /*
1444  *  return multicast version if any
1445  */
1446 int
1447 ipismulticast(uchar *ip)
1448 {
1449         if(isv4(ip)){
1450                 if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
1451                         return V4;
1452         }
1453         else if(ip[0] == 0xff)
1454                 return V6;
1455         return 0;
1456 }
1457
1458 /*
1459  *  add a multicast address to an interface, called with c->car locked
1460  */
1461 void
1462 ipifcaddmulti(Conv *c, uchar *ma, uchar *ia)
1463 {
1464         Ipmulti *multi, **l;
1465         Iplifc *lifc;
1466         Ipifc *ifc;
1467         Fs *f;
1468
1469         for(l = &c->multi; *l != nil; l = &(*l)->next)
1470                 if(ipcmp(ma, (*l)->ma) == 0 && ipcmp(ia, (*l)->ia) == 0)
1471                         return;         /* it's already there */
1472
1473         multi = *l = smalloc(sizeof(*multi));
1474         ipmove(multi->ma, ma);
1475         ipmove(multi->ia, ia);
1476         multi->next = nil;
1477
1478         f = c->p->f;
1479         if((ifc = findipifc(f, ia, ma, Rmulti)) != nil){
1480                 wlock(ifc);
1481                 if(waserror()){
1482                         wunlock(ifc);
1483                         nexterror();
1484                 }
1485                 if((lifc = iplocalonifc(ifc, ia)) != nil)
1486                         addselfcache(f, ifc, lifc, ma, Rmulti);
1487                 wunlock(ifc);
1488                 poperror();
1489         }
1490 }
1491
1492
1493 /*
1494  *  remove a multicast address from an interface, called with c->car locked
1495  */
1496 void
1497 ipifcremmulti(Conv *c, uchar *ma, uchar *ia)
1498 {
1499         Ipmulti *multi, **l;
1500         Iplifc *lifc;
1501         Ipifc *ifc;
1502         Fs *f;
1503
1504         for(l = &c->multi; *l != nil; l = &(*l)->next)
1505                 if(ipcmp(ma, (*l)->ma) == 0 && ipcmp(ia, (*l)->ia) == 0)
1506                         break;
1507
1508         multi = *l;
1509         if(multi == nil)
1510                 return;         /* we don't have it open */
1511
1512         *l = multi->next;
1513         multi->next = nil;
1514
1515         f = c->p->f;
1516         if((ifc = findipifc(f, ia, ma, Rmulti)) != nil){
1517                 wlock(ifc);
1518                 if(!waserror()){
1519                         if((lifc = iplocalonifc(ifc, ia)) != nil)
1520                                 remselfcache(f, ifc, lifc, ma);
1521                         poperror();
1522                 }
1523                 wunlock(ifc);
1524         }
1525         free(multi);
1526 }
1527
1528 /* register the address on this network for address resolution */
1529 static void
1530 ipifcregisteraddr(Fs *f, Ipifc *ifc, uchar *ia, uchar *ip)
1531 {
1532         Iplifc *lifc;
1533
1534         rlock(ifc);
1535         if(waserror()){
1536                 runlock(ifc);
1537                 print("ipifcregisteraddr %s %I %I: %s\n", ifc->dev, ia, ip, up->errstr);
1538                 return;
1539         }
1540         lifc = iplocalonifc(ifc, ia);
1541         if(lifc != nil && ifc->m != nil && ifc->m->areg != nil)
1542                 (*ifc->m->areg)(f, ifc, lifc, ip);
1543         runlock(ifc);
1544         poperror();
1545 }
1546
1547 static void
1548 ipifcregisterproxy(Fs *f, Ipifc *ifc, uchar *ip, int add)
1549 {
1550         uchar a[IPaddrlen];
1551         Iplifc *lifc;
1552         Ipifc *nifc;
1553         Conv **cp;
1554
1555         /* register the address on any interface that will proxy for the ip */
1556         for(cp = f->ipifc->conv; *cp != nil; cp++){
1557                 nifc = (Ipifc*)(*cp)->ptcl;
1558                 if(nifc == ifc)
1559                         continue;
1560
1561                 wlock(nifc);
1562                 if(nifc->m == nil
1563                 || (lifc = ipremoteonifc(nifc, ip)) == nil
1564                 || (lifc->type & Rptpt) != 0
1565                 || waserror()){
1566                         wunlock(nifc);
1567                         continue;
1568                 }
1569                 if((lifc->type & Rv4) == 0){
1570                         /* add solicited-node multicast addr */
1571                         ipv62smcast(a, ip);
1572                         if(add)
1573                                 addselfcache(f, nifc, lifc, a, Rmulti);
1574                         else
1575                                 remselfcache(f, nifc, lifc, a);
1576                 }
1577                 ipmove(a, lifc->local);
1578                 wunlock(nifc);
1579                 poperror();
1580
1581                 if(add)
1582                         ipifcregisteraddr(f, nifc, a, ip);
1583         }
1584 }
1585
1586 char*
1587 ipifcadd6(Ipifc *ifc, char**argv, int argc)
1588 {
1589         int plen = 64;
1590         char addr[40], preflen[6];
1591         char *params[3];
1592         uchar prefix[IPaddrlen];
1593         Iplifc lifc;
1594
1595         lifc.onlink = 1;
1596         lifc.autoflag = 1;
1597         lifc.validlt = ~0L;
1598         lifc.preflt = ~0L;
1599         lifc.origint = NOW / 1000;
1600
1601         switch(argc) {
1602         case 7:
1603                 lifc.preflt = atoi(argv[6]);
1604                 /* fall through */
1605         case 6:
1606                 lifc.validlt = atoi(argv[5]);
1607                 /* fall through */
1608         case 5:
1609                 lifc.autoflag = atoi(argv[4]) != 0;
1610                 /* fall through */
1611         case 4:
1612                 lifc.onlink = atoi(argv[3]) != 0;
1613                 /* fall through */
1614         case 3:
1615                 plen = atoi(argv[2]);
1616                 /* fall through */
1617         case 2:
1618                 break;
1619         default:
1620                 return Ebadarg;
1621         }
1622
1623         if (parseip(prefix, argv[1]) != 6 || lifc.validlt < lifc.preflt || plen < 0 ||
1624             plen > 64 || islinklocal(prefix))
1625                 return Ebadarg;
1626
1627         /* issue "add" ctl msg for v6 link-local addr and prefix len */
1628         if(ifc->m->pref2addr == nil)
1629                 return Ebadarg;
1630         (*ifc->m->pref2addr)(prefix, ifc->mac); /* mac → v6 link-local addr */
1631
1632         sprint(addr, "%I", prefix);
1633         sprint(preflen, "/%d", plen);
1634         params[0] = "add";
1635         params[1] = addr;
1636         params[2] = preflen;
1637
1638         return ipifcadd(ifc, params, 3, 0, &lifc);
1639 }