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