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