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