]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/ip/ipifc.c
ip: get rid of update_mtucache() and restrict_mtu() prototypes
[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  *      Rmcast
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 //      multicastv6,
1167         unspecifiedv6,
1168         linklocalv6,
1169         globalv6,
1170 };
1171
1172 int
1173 v6addrtype(uchar *addr)
1174 {
1175         if(isv4(addr) || ipcmp(addr, IPnoaddr) == 0)
1176                 return unknownv6;
1177         else if(islinklocal(addr) ||
1178             isv6mcast(addr) && (addr[1] & 0xF) <= Link_local_scop)
1179                 return linklocalv6;
1180         else
1181                 return globalv6;
1182 }
1183
1184 #define v6addrcurr(lifc) ((lifc)->preflt == ~0L || \
1185                         (lifc)->origint + (lifc)->preflt >= NOW/1000)
1186
1187 static void
1188 findprimaryipv6(Fs *f, uchar *local)
1189 {
1190         int atype, atypel;
1191         Conv **cp, **e;
1192         Ipifc *ifc;
1193         Iplifc *lifc;
1194
1195         ipmove(local, v6Unspecified);
1196         atype = unspecifiedv6;
1197
1198         /*
1199          * find "best" (global > link local > unspecified)
1200          * local address; address must be current.
1201          */
1202         e = &f->ipifc->conv[f->ipifc->nc];
1203         for(cp = f->ipifc->conv; cp < e; cp++){
1204                 if(*cp == 0)
1205                         continue;
1206                 ifc = (Ipifc*)(*cp)->ptcl;
1207                 for(lifc = ifc->lifc; lifc; lifc = lifc->next){
1208                         atypel = v6addrtype(lifc->local);
1209                         if(atypel > atype && v6addrcurr(lifc)) {
1210                                 ipmove(local, lifc->local);
1211                                 atype = atypel;
1212                                 if(atype == globalv6)
1213                                         return;
1214                         }
1215                 }
1216         }
1217 }
1218
1219 /*
1220  *  returns first ip address configured
1221  */
1222 static void
1223 findprimaryipv4(Fs *f, uchar *local)
1224 {
1225         Conv **cp, **e;
1226         Ipifc *ifc;
1227         Iplifc *lifc;
1228
1229         /* find first ifc local address */
1230         e = &f->ipifc->conv[f->ipifc->nc];
1231         for(cp = f->ipifc->conv; cp < e; cp++){
1232                 if(*cp == 0)
1233                         continue;
1234                 ifc = (Ipifc*)(*cp)->ptcl;
1235                 if((lifc = ifc->lifc) != nil){
1236                         ipmove(local, lifc->local);
1237                         return;
1238                 }
1239         }
1240 }
1241
1242 /*
1243  *  find the local address 'closest' to the remote system, copy it to
1244  *  local and return the ifc for that address
1245  */
1246 void
1247 findlocalip(Fs *f, uchar *local, uchar *remote)
1248 {
1249         int version, atype = unspecifiedv6, atypel = unknownv6;
1250         int atyper, deprecated;
1251         uchar gate[IPaddrlen], gnet[IPaddrlen];
1252         Ipifc *ifc;
1253         Iplifc *lifc;
1254         Route *r;
1255
1256         USED(atype);
1257         USED(atypel);
1258         qlock(f->ipifc);
1259         r = v6lookup(f, remote, nil);
1260         version = (memcmp(remote, v4prefix, IPv4off) == 0)? V4: V6;
1261
1262         if(r != nil){
1263                 ifc = r->ifc;
1264                 if(r->type & Rv4)
1265                         v4tov6(gate, r->v4.gate);
1266                 else {
1267                         ipmove(gate, r->v6.gate);
1268                         ipmove(local, v6Unspecified);
1269                 }
1270
1271                 switch(version) {
1272                 case V4:
1273                         /* find ifc address closest to the gateway to use */
1274                         for(lifc = ifc->lifc; lifc; lifc = lifc->next){
1275                                 maskip(gate, lifc->mask, gnet);
1276                                 if(ipcmp(gnet, lifc->net) == 0){
1277                                         ipmove(local, lifc->local);
1278                                         goto out;
1279                                 }
1280                         }
1281                         break;
1282                 case V6:
1283                         /* find ifc address with scope matching the destination */
1284                         atyper = v6addrtype(remote);
1285                         deprecated = 0;
1286                         for(lifc = ifc->lifc; lifc; lifc = lifc->next){
1287                                 atypel = v6addrtype(lifc->local);
1288                                 /* prefer appropriate scope */
1289                                 if(atypel > atype && atype < atyper ||
1290                                    atypel < atype && atype > atyper){
1291                                         ipmove(local, lifc->local);
1292                                         deprecated = !v6addrcurr(lifc);
1293                                         atype = atypel;
1294                                 } else if(atypel == atype){
1295                                         /* avoid deprecated addresses */
1296                                         if(deprecated && v6addrcurr(lifc)){
1297                                                 ipmove(local, lifc->local);
1298                                                 atype = atypel;
1299                                                 deprecated = 0;
1300                                         }
1301                                 }
1302                                 if(atype == atyper && !deprecated)
1303                                         goto out;
1304                         }
1305                         if(atype >= atyper)
1306                                 goto out;
1307                         break;
1308                 default:
1309                         panic("findlocalip: version %d", version);
1310                 }
1311         }
1312
1313         switch(version){
1314         case V4:
1315                 findprimaryipv4(f, local);
1316                 break;
1317         case V6:
1318                 findprimaryipv6(f, local);
1319                 break;
1320         default:
1321                 panic("findlocalip2: version %d", version);
1322         }
1323
1324 out:
1325         qunlock(f->ipifc);
1326 }
1327
1328 /*
1329  *  return first v4 address associated with an interface
1330  */
1331 int
1332 ipv4local(Ipifc *ifc, uchar *addr)
1333 {
1334         Iplifc *lifc;
1335
1336         for(lifc = ifc->lifc; lifc; lifc = lifc->next){
1337                 if(isv4(lifc->local)){
1338                         memmove(addr, lifc->local+IPv4off, IPv4addrlen);
1339                         return 1;
1340                 }
1341         }
1342         return 0;
1343 }
1344
1345 /*
1346  *  return first v6 address associated with an interface
1347  */
1348 int
1349 ipv6local(Ipifc *ifc, uchar *addr)
1350 {
1351         Iplifc *lifc;
1352
1353         for(lifc = ifc->lifc; lifc; lifc = lifc->next){
1354                 if(!isv4(lifc->local) && !(lifc->tentative)){
1355                         ipmove(addr, lifc->local);
1356                         return 1;
1357                 }
1358         }
1359         return 0;
1360 }
1361
1362 int
1363 ipv6anylocal(Ipifc *ifc, uchar *addr)
1364 {
1365         Iplifc *lifc;
1366
1367         for(lifc = ifc->lifc; lifc; lifc = lifc->next){
1368                 if(!isv4(lifc->local)){
1369                         ipmove(addr, lifc->local);
1370                         return SRC_UNI;
1371                 }
1372         }
1373         return SRC_UNSPEC;
1374 }
1375
1376 /*
1377  *  see if this address is bound to the interface
1378  */
1379 Iplifc*
1380 iplocalonifc(Ipifc *ifc, uchar *ip)
1381 {
1382         Iplifc *lifc;
1383
1384         for(lifc = ifc->lifc; lifc; lifc = lifc->next)
1385                 if(ipcmp(ip, lifc->local) == 0)
1386                         return lifc;
1387         return nil;
1388 }
1389
1390
1391 /*
1392  *  See if we're proxying for this address on this interface
1393  */
1394 int
1395 ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip)
1396 {
1397         Route *r;
1398         uchar net[IPaddrlen];
1399         Iplifc *lifc;
1400
1401         /* see if this is a direct connected pt to pt address */
1402         r = v6lookup(f, ip, nil);
1403         if(r == nil || (r->type & (Rifc|Rproxy)) != (Rifc|Rproxy))
1404                 return 0;
1405
1406         /* see if this is on the right interface */
1407         for(lifc = ifc->lifc; lifc; lifc = lifc->next){
1408                 maskip(ip, lifc->mask, net);
1409                 if(ipcmp(net, lifc->remote) == 0)
1410                         return 1;
1411         }
1412         return 0;
1413 }
1414
1415 /*
1416  *  return multicast version if any
1417  */
1418 int
1419 ipismulticast(uchar *ip)
1420 {
1421         if(isv4(ip)){
1422                 if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
1423                         return V4;
1424         }
1425         else if(ip[0] == 0xff)
1426                 return V6;
1427         return 0;
1428 }
1429 int
1430 ipisbm(uchar *ip)
1431 {
1432         if(isv4(ip)){
1433                 if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
1434                         return V4;
1435                 else if(ipcmp(ip, IPv4bcast) == 0)
1436                         return V4;
1437         }
1438         else if(ip[0] == 0xff)
1439                 return V6;
1440         return 0;
1441 }
1442
1443
1444 /*
1445  *  add a multicast address to an interface, called with c->car locked
1446  */
1447 void
1448 ipifcaddmulti(Conv *c, uchar *ma, uchar *ia)
1449 {
1450         Ipifc *ifc;
1451         Iplifc *lifc;
1452         Conv **p;
1453         Ipmulti *multi, **l;
1454         Fs *f;
1455
1456         f = c->p->f;
1457
1458         for(l = &c->multi; *l; l = &(*l)->next)
1459                 if(ipcmp(ma, (*l)->ma) == 0 && ipcmp(ia, (*l)->ia) == 0)
1460                         return;         /* it's already there */
1461
1462         multi = *l = smalloc(sizeof(*multi));
1463         ipmove(multi->ma, ma);
1464         ipmove(multi->ia, ia);
1465         multi->next = nil;
1466
1467         for(p = f->ipifc->conv; *p; p++){
1468                 if((*p)->inuse == 0)
1469                         continue;
1470                 ifc = (Ipifc*)(*p)->ptcl;
1471                 wlock(ifc);
1472                 if(waserror()){
1473                         wunlock(ifc);
1474                         nexterror();
1475                 }
1476                 for(lifc = ifc->lifc; lifc; lifc = lifc->next)
1477                         if(ipcmp(ia, lifc->local) == 0)
1478                                 addselfcache(f, ifc, lifc, ma, Rmulti);
1479                 wunlock(ifc);
1480                 poperror();
1481         }
1482 }
1483
1484
1485 /*
1486  *  remove a multicast address from an interface, called with c->car locked
1487  */
1488 void
1489 ipifcremmulti(Conv *c, uchar *ma, uchar *ia)
1490 {
1491         Ipmulti *multi, **l;
1492         Iplifc *lifc;
1493         Conv **p;
1494         Ipifc *ifc;
1495         Fs *f;
1496
1497         f = c->p->f;
1498
1499         for(l = &c->multi; *l; l = &(*l)->next)
1500                 if(ipcmp(ma, (*l)->ma) == 0 && ipcmp(ia, (*l)->ia) == 0)
1501                         break;
1502
1503         multi = *l;
1504         if(multi == nil)
1505                 return;         /* we don't have it open */
1506
1507         *l = multi->next;
1508
1509         for(p = f->ipifc->conv; *p; p++){
1510                 if((*p)->inuse == 0)
1511                         continue;
1512
1513                 ifc = (Ipifc*)(*p)->ptcl;
1514                 wlock(ifc);
1515                 for(lifc = ifc->lifc; lifc; lifc = lifc->next)
1516                         if(ipcmp(ia, lifc->local) == 0)
1517                                 remselfcache(f, ifc, lifc, ma);
1518                 wunlock(ifc);
1519         }
1520
1521         free(multi);
1522 }
1523
1524 /*
1525  *  make lifc's join and leave multicast groups
1526  */
1527 static char*
1528 ipifcjoinmulti(Ipifc *ifc, char **argv, int argc)
1529 {
1530         USED(ifc, argv, argc);
1531         return nil;
1532 }
1533
1534 static char*
1535 ipifcleavemulti(Ipifc *ifc, char **argv, int argc)
1536 {
1537         USED(ifc, argv, argc);
1538         return nil;
1539 }
1540
1541 static void
1542 ipifcregisterproxy(Fs *f, Ipifc *ifc, uchar *ip)
1543 {
1544         Conv **cp, **e;
1545         Ipifc *nifc;
1546         Iplifc *lifc;
1547         Medium *m;
1548         uchar net[IPaddrlen];
1549
1550         /* register the address on any network that will proxy for us */
1551         e = &f->ipifc->conv[f->ipifc->nc];
1552
1553         if(!isv4(ip)) {                         /* V6 */
1554                 for(cp = f->ipifc->conv; cp < e; cp++){
1555                         if(*cp == nil || (nifc = (Ipifc*)(*cp)->ptcl) == ifc)
1556                                 continue;
1557                         rlock(nifc);
1558                         m = nifc->m;
1559                         if(m == nil || m->addmulti == nil) {
1560                                 runlock(nifc);
1561                                 continue;
1562                         }
1563                         if(waserror()){
1564                                 runlock(nifc);
1565                                 nexterror();
1566                         }
1567                         for(lifc = nifc->lifc; lifc; lifc = lifc->next){
1568                                 maskip(ip, lifc->mask, net);
1569                                 if(ipcmp(net, lifc->remote) == 0) {
1570                                         /* add solicited-node multicast addr */
1571                                         ipv62smcast(net, ip);
1572                                         addselfcache(f, nifc, lifc, net, Rmulti);
1573                                         arpenter(f, V6, ip, nifc->mac, 6, 0);
1574                                         break;
1575                                 }
1576                         }
1577                         runlock(nifc);
1578                         poperror();
1579                 }
1580         }
1581         else {                                  /* V4 */
1582                 for(cp = f->ipifc->conv; cp < e; cp++){
1583                         if(*cp == nil || (nifc = (Ipifc*)(*cp)->ptcl) == ifc)
1584                                 continue;
1585                         rlock(nifc);
1586                         m = nifc->m;
1587                         if(m == nil || m->areg == nil){
1588                                 runlock(nifc);
1589                                 continue;
1590                         }
1591                         if(waserror()){
1592                                 runlock(nifc);
1593                                 nexterror();
1594                         }
1595                         for(lifc = nifc->lifc; lifc; lifc = lifc->next){
1596                                 maskip(ip, lifc->mask, net);
1597                                 if(ipcmp(net, lifc->remote) == 0){
1598                                         (*m->areg)(nifc, ip);
1599                                         break;
1600                                 }
1601                         }
1602                         runlock(nifc);
1603                         poperror();
1604                 }
1605         }
1606 }
1607
1608
1609 /* added for new v6 mesg types */
1610 static void
1611 adddefroute6(Fs *f, uchar *gate, int force)
1612 {
1613         Route *r;
1614
1615         r = v6lookup(f, v6Unspecified, nil);
1616         /*
1617          * route entries generated by all other means take precedence
1618          * over router announcements.
1619          */
1620         if (r && !force && strcmp(r->tag, "ra") != 0)
1621                 return;
1622
1623         v6delroute(f, v6Unspecified, v6Unspecified, 1);
1624         v6addroute(f, "ra", v6Unspecified, v6Unspecified, gate, 0);
1625 }
1626
1627 enum {
1628         Ngates = 3,
1629 };
1630
1631 char*
1632 ipifcadd6(Ipifc *ifc, char**argv, int argc)
1633 {
1634         int plen = 64;
1635         char addr[40], preflen[6];
1636         char *params[3];
1637         uchar prefix[IPaddrlen];
1638         Iplifc lifc;
1639
1640         lifc.onlink = 1;
1641         lifc.autoflag = 1;
1642         lifc.validlt = ~0L;
1643         lifc.preflt = ~0L;
1644         lifc.origint = NOW / 1000;
1645
1646         switch(argc) {
1647         case 7:
1648                 lifc.preflt = atoi(argv[6]);
1649                 /* fall through */
1650         case 6:
1651                 lifc.validlt = atoi(argv[5]);
1652                 /* fall through */
1653         case 5:
1654                 lifc.autoflag = atoi(argv[4]) != 0;
1655                 /* fall through */
1656         case 4:
1657                 lifc.onlink = atoi(argv[3]) != 0;
1658                 /* fall through */
1659         case 3:
1660                 plen = atoi(argv[2]);
1661                 /* fall through */
1662         case 2:
1663                 break;
1664         default:
1665                 return Ebadarg;
1666         }
1667
1668         if (parseip(prefix, argv[1]) != 6 || lifc.validlt < lifc.preflt || plen < 0 ||
1669             plen > 64 || islinklocal(prefix))
1670                 return Ebadarg;
1671
1672         /* issue "add" ctl msg for v6 link-local addr and prefix len */
1673         if(ifc->m->pref2addr == nil)
1674                 return Ebadarg;
1675         (*ifc->m->pref2addr)(prefix, ifc->mac); /* mac → v6 link-local addr */
1676
1677         sprint(addr, "%I", prefix);
1678         sprint(preflen, "/%d", plen);
1679         params[0] = "add";
1680         params[1] = addr;
1681         params[2] = preflen;
1682
1683         return ipifcadd(ifc, params, 3, 0, &lifc);
1684 }