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