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