changeset 7231: | 7c0cb11e474d |
parent: | 3a5ccd543798 |
child: | 24d21d7c0868 |
author: | cinap_lenrek@felloff.net |
date: | Sat, 11 May 2019 17:22:33 +0200 |
permissions: | -rw-r--r-- |
description: | devip: reset speed and delay on bind, adjust burst on mtu change, ifc->m nil check, consistent error strings initialize the rate limits when the device gets bound, not when it is created. so that the rate limtis get reset to default when the ifc is reused. adjust the burst delay when the mtu is changed. this is to make sure that we allow at least one full sized packet burst. make a local copy of ifc->m before doing nil check as it can change under us when we do not have the ifc locked. specify Ebound[] and Eunbound[] error strings and use them consistently. |
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"8 #include "ip.h"9 #include "ipv6.h"11 #define DPRINT if(0)print13 enum {14 Maxmedia = 32,15 Nself = Maxmedia*5,16 NHASH = 1<<6,17 NCACHE = 256,18 QMAX = 192*1024-1,19 };21 Medium *media[Maxmedia] = { 0 };23 /*24 * cache of local addresses (addresses we answer to)25 */26 struct Ipself27 {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 };37 struct Ipselftab38 {39 QLock;40 int inited;41 int acceptall; /* true if an interface has the null address */42 Ipself *hash[NHASH]; /* hash chains */43 };45 /*46 * Multicast addresses are chained onto a Chan so that47 * we can remove them when the Chan is closed.48 */49 typedef struct Ipmcast Ipmcast;50 struct Ipmcast51 {52 Ipmcast *next;53 uchar ma[IPaddrlen]; /* multicast address */54 uchar ia[IPaddrlen]; /* interface address */55 };57 /* quick hash for ip addresses */58 #define hashipa(a) ( ( ((a)[IPaddrlen-2]<<8) | (a)[IPaddrlen-1] )%NHASH )60 static char tifc[] = "ifc ";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*, Iplifc*, uchar*);65 static void ipifcregisterproxy(Fs*, Ipifc*, uchar*, int);66 static char* ipifcremlifc(Ipifc*, Iplifc**);68 static char Ebound[] = "interface already bound";69 static char Eunbound[] = "interface not bound";71 enum {72 unknownv6, /* UGH */73 unspecifiedv6,74 linklocalv6,75 globalv6,76 };78 static int79 v6addrtype(uchar *addr)80 {81 if(isv4(addr) || ipcmp(addr, IPnoaddr) == 0)82 return unknownv6;83 else if(islinklocal(addr) || ipcmp(addr, v6loopback) == 0 ||84 isv6mcast(addr) && (addr[1] & 0xF) <= Link_local_scop)85 return linklocalv6;86 else87 return globalv6;88 }90 static int91 comprefixlen(uchar *a, uchar *b, int n)92 {93 int i, c;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 }105 /*106 * link in a new medium107 */108 void109 addipmedium(Medium *med)110 {111 int i;113 for(i = 0; i < nelem(media)-1; i++)114 if(media[i] == nil){115 media[i] = med;116 break;117 }118 }120 /*121 * find the medium with this name122 */123 Medium*124 ipfindmedium(char *name)125 {126 Medium **mp;128 for(mp = media; *mp != nil; mp++)129 if(strcmp((*mp)->name, name) == 0)130 break;131 return *mp;132 }134 /*135 * attach a device (or pkt driver) to the interface.136 * called with c locked137 */138 static char*139 ipifcbind(Conv *c, char **argv, int argc)140 {141 Ipifc *ifc;142 Medium *m;144 if(argc < 2)145 return Ebadarg;147 ifc = (Ipifc*)c->ptcl;149 /* bind the device to the interface */150 m = ipfindmedium(argv[1]);151 if(m == nil)152 return "unknown interface type";154 wlock(ifc);155 if(ifc->m != nil){156 wunlock(ifc);157 return Ebound;158 }159 if(waserror()){160 wunlock(ifc);161 nexterror();162 }164 /* do medium specific binding */165 (*m->bind)(ifc, argc, argv);167 /* set the bound device name */168 if(argc > 2)169 strncpy(ifc->dev, argv[2], sizeof(ifc->dev));170 else171 snprint(ifc->dev, sizeof ifc->dev, "%s%d", m->name, c->x);172 ifc->dev[sizeof(ifc->dev)-1] = 0;174 /* set up parameters */175 ifc->m = m;176 ifc->mintu = ifc->m->mintu;177 ifc->maxtu = ifc->m->maxtu;178 ifc->delay = 40;179 ifc->speed = 0;180 if(ifc->m->unbindonclose == 0)181 ifc->conv->inuse++;183 /* default router paramters */184 ifc->rp = c->p->f->v6p->rp;186 /* any ancillary structures (like routes) no longer pertain */187 ifc->ifcid++;189 /* reopen all the queues closed by a previous unbind */190 qreopen(c->rq);191 qreopen(c->eq);192 qreopen(c->sq);194 wunlock(ifc);195 poperror();197 return nil;198 }200 /*201 * detach a device from an interface, close the interface202 */203 static char*204 ipifcunbind(Ipifc *ifc)205 {206 wlock(ifc);207 if(ifc->m == nil){208 wunlock(ifc);209 return Eunbound;210 }212 /* disassociate logical interfaces (before zeroing ifc->arg) */213 while(ifc->lifc != nil)214 ipifcremlifc(ifc, &ifc->lifc);216 /* disassociate device */217 if(ifc->m->unbind != nil){218 if(!waserror()){219 (*ifc->m->unbind)(ifc);220 poperror();221 }222 }224 memset(ifc->dev, 0, sizeof(ifc->dev));225 ifc->arg = nil;227 ifc->reflect = 0;228 ifc->reassemble = 0;230 /* close queues to stop queuing of packets */231 qclose(ifc->conv->rq);232 qclose(ifc->conv->wq);233 qclose(ifc->conv->sq);235 /* dissociate routes */236 ifc->ifcid++;237 if(ifc->m->unbindonclose == 0)238 ifc->conv->inuse--;239 ifc->m = nil;240 wunlock(ifc);242 return nil;243 }245 char sfixedformat[] = "device %s maxtu %d sendra %d recvra %d mflag %d oflag %d"246 " maxraint %d minraint %d linkmtu %d reachtime %d rxmitra %d ttl %d routerlt %d"247 " pktin %lud pktout %lud errin %lud errout %lud speed %d delay %d\n";249 char slineformat[] = " %-40I %-10M %-40I %-12lud %-12lud\n";251 static int252 ipifcstate(Conv *c, char *state, int n)253 {254 Ipifc *ifc;255 Iplifc *lifc;256 int m;258 ifc = (Ipifc*)c->ptcl;259 m = snprint(state, n, sfixedformat,260 ifc->dev, ifc->maxtu, ifc->sendra6, ifc->recvra6,261 ifc->rp.mflag, ifc->rp.oflag, ifc->rp.maxraint,262 ifc->rp.minraint, ifc->rp.linkmtu, ifc->rp.reachtime,263 ifc->rp.rxmitra, ifc->rp.ttl, ifc->rp.routerlt,264 ifc->in, ifc->out, ifc->inerr, ifc->outerr,265 ifc->speed, ifc->delay);267 rlock(ifc);268 for(lifc = ifc->lifc; lifc != nil && n > m; lifc = lifc->next)269 m += snprint(state+m, n - m, slineformat, lifc->local,270 lifc->mask, lifc->remote, lifc->validlt, lifc->preflt);271 if(ifc->lifc == nil)272 m += snprint(state+m, n - m, "\n");273 runlock(ifc);274 return m;275 }277 static int278 ipifclocal(Conv *c, char *state, int n)279 {280 Ipifc *ifc;281 Iplifc *lifc;282 Iplink *link;283 int m;285 ifc = (Ipifc*)c->ptcl;286 rlock(ifc);287 m = 0;288 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){289 m += snprint(state+m, n - m, "%-40.40I ->", lifc->local);290 for(link = lifc->link; link != nil; link = link->lifclink)291 m += snprint(state+m, n - m, " %-40.40I", link->self->a);292 m += snprint(state+m, n - m, "\n");293 }294 runlock(ifc);295 return m;296 }298 static int299 ipifcinuse(Conv *c)300 {301 Ipifc *ifc;303 ifc = (Ipifc*)c->ptcl;304 return ifc->m != nil;305 }307 static void308 ipifcadjustburst(Ipifc *ifc)309 {310 int burst;312 burst = ((vlong)ifc->delay * ifc->speed) / 8000;313 if(burst < ifc->maxtu)314 burst = ifc->maxtu;315 ifc->burst = burst;316 }318 static void319 ipifcsetdelay(Ipifc *ifc, int delay)320 {321 if(delay < 0)322 delay = 0;323 else if(delay > 1000)324 delay = 1000;325 ifc->delay = delay;326 ipifcadjustburst(ifc);327 }329 static void330 ipifcsetspeed(Ipifc *ifc, int speed)331 {332 if(speed < 0)333 speed = 0;334 ifc->speed = speed;335 ifc->load = 0;336 ipifcadjustburst(ifc);337 }339 void340 ipifcoput(Ipifc *ifc, Block *bp, int version, uchar *ip)341 {342 if(ifc->speed){343 ulong now = MACHP(0)->ticks;344 int dt = TK2MS(now - ifc->ticks);345 ifc->ticks = now;346 ifc->load -= ((vlong)dt * ifc->speed) / 8000;347 if(ifc->load < 0 || dt < 0 || dt > 1000)348 ifc->load = 0;349 else if(ifc->load > ifc->burst){350 freeblist(bp);351 return;352 }353 }354 bp = concatblock(bp);355 ifc->load += BLEN(bp);356 ifc->m->bwrite(ifc, bp, version, ip);357 }360 /*361 * called when a process writes to an interface's 'data'362 */363 static void364 ipifckick(void *x)365 {366 Conv *c = x;367 Block *bp;368 Ipifc *ifc;370 bp = qget(c->wq);371 if(bp == nil)372 return;374 ifc = (Ipifc*)c->ptcl;375 if(!canrlock(ifc)){376 freeb(bp);377 return;378 }379 if(waserror()){380 runlock(ifc);381 nexterror();382 }383 if(ifc->m != nil && ifc->m->pktin != nil)384 (*ifc->m->pktin)(c->p->f, ifc, bp);385 else386 freeb(bp);387 runlock(ifc);388 poperror();389 }391 /*392 * called when a new ipifc structure is created393 */394 static void395 ipifccreate(Conv *c)396 {397 Ipifc *ifc;399 c->rq = qopen(QMAX, 0, 0, 0);400 c->wq = qopen(QMAX, Qkick, ipifckick, c);401 c->sq = qopen(QMAX, 0, 0, 0);402 if(c->rq == nil || c->wq == nil || c->sq == nil)403 error(Enomem);404 ifc = (Ipifc*)c->ptcl;405 ifc->conv = c;406 ifc->m = nil;407 ifc->reflect = 0;408 ifc->reassemble = 0;409 }411 /*412 * called after last close of ipifc data or ctl413 */414 static void415 ipifcclose(Conv *c)416 {417 Ipifc *ifc = (Ipifc*)c->ptcl;418 Medium *m = ifc->m;420 if(m != nil && m->unbindonclose)421 ipifcunbind(ifc);422 }424 /*425 * change an interface's mtu426 */427 static char*428 ipifcsetmtu(Ipifc *ifc, int mtu)429 {430 Medium *m = ifc->m;432 if(m == nil)433 return Eunbound;434 if(mtu < m->mintu || mtu > m->maxtu)435 return Ebadarg;436 ifc->maxtu = mtu;437 ipifcadjustburst(ifc);438 return nil;439 }441 /*442 * add an address to an interface.443 */444 char*445 ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp)446 {447 uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen];448 uchar bcast[IPaddrlen], net[IPaddrlen];449 Iplifc *lifc, **l;450 int i, type, mtu;451 Fs *f;453 mtu = 0;454 type = Rifc;455 memset(ip, 0, IPaddrlen);456 memset(mask, 0, IPaddrlen);457 memset(rem, 0, IPaddrlen);458 switch(argc){459 case 6:460 if(strcmp(argv[5], "proxy") == 0)461 type |= Rproxy;462 /* fall through */463 case 5:464 mtu = strtoul(argv[4], 0, 0);465 /* fall through */466 case 4:467 if (parseipandmask(ip, mask, argv[1], argv[2]) == -1 || parseip(rem, argv[3]) == -1)468 return Ebadip;469 maskip(rem, mask, net);470 break;471 case 3:472 if (parseipandmask(ip, mask, argv[1], argv[2]) == -1)473 return Ebadip;474 maskip(ip, mask, rem);475 maskip(rem, mask, net);476 break;477 case 2:478 if (parseip(ip, argv[1]) == -1)479 return Ebadip;480 memmove(mask, defmask(ip), IPaddrlen);481 maskip(ip, mask, rem);482 maskip(rem, mask, net);483 break;484 default:485 return Ebadarg;486 }488 /* check for point-to-point interface */489 if(ipcmp(ip, v6loopback) != 0) /* skip v6 loopback, it's a special address */490 if(ipcmp(mask, IPallbits) == 0)491 type |= Rptpt;493 if(isv4(ip) || ipcmp(ip, IPnoaddr) == 0){494 type |= Rv4;495 tentative = 0;496 }498 wlock(ifc);499 if(ifc->m == nil){500 wunlock(ifc);501 return Eunbound;502 }503 f = ifc->conv->p->f;504 if(waserror()){505 wunlock(ifc);506 return up->errstr;507 }509 if(mtu > 0)510 ipifcsetmtu(ifc, mtu);512 /* ignore if this is already a local address for this ifc */513 if((lifc = iplocalonifc(ifc, ip)) != nil){514 if(lifcp != nil) {515 if(!lifc->onlink && lifcp->onlink){516 lifc->onlink = 1;517 addroute(f, lifc->remote, lifc->mask, ip, IPallbits,518 lifc->remote, lifc->type, ifc, tifc);519 if(v6addrtype(ip) != linklocalv6)520 addroute(f, lifc->remote, lifc->mask, ip, IPnoaddr,521 lifc->remote, lifc->type, ifc, tifc);522 }523 lifc->autoflag = lifcp->autoflag;524 lifc->validlt = lifcp->validlt;525 lifc->preflt = lifcp->preflt;526 lifc->origint = lifcp->origint;527 }528 if(lifc->tentative != tentative){529 lifc->tentative = tentative;530 goto done;531 }532 wunlock(ifc);533 poperror();534 return nil;535 }537 /* add the address to the list of logical ifc's for this ifc */538 lifc = smalloc(sizeof(Iplifc));539 ipmove(lifc->local, ip);540 ipmove(lifc->mask, mask);541 ipmove(lifc->remote, rem);542 ipmove(lifc->net, net);543 lifc->type = type;544 lifc->tentative = tentative;545 if(lifcp != nil) {546 lifc->onlink = lifcp->onlink;547 lifc->autoflag = lifcp->autoflag;548 lifc->validlt = lifcp->validlt;549 lifc->preflt = lifcp->preflt;550 lifc->origint = lifcp->origint;551 } else { /* default values */552 lifc->onlink = lifc->autoflag = 1;553 lifc->validlt = lifc->preflt = ~0UL;554 lifc->origint = NOW / 1000;555 }556 lifc->next = nil;558 for(l = &ifc->lifc; *l != nil; l = &(*l)->next)559 ;560 *l = lifc;562 /* add route for this logical interface */563 if(lifc->onlink){564 addroute(f, rem, mask, ip, IPallbits, rem, type, ifc, tifc);565 if(v6addrtype(ip) != linklocalv6)566 addroute(f, rem, mask, ip, IPnoaddr, rem, type, ifc, tifc);567 }569 addselfcache(f, ifc, lifc, ip, Runi);571 /* register proxy */572 if(type & Rptpt){573 if(type & Rproxy)574 ipifcregisterproxy(f, ifc, rem, 1);575 goto done;576 }578 if(type & Rv4) {579 /* add subnet directed broadcast address to the self cache */580 for(i = 0; i < IPaddrlen; i++)581 bcast[i] = (ip[i] & mask[i]) | ~mask[i];582 addselfcache(f, ifc, lifc, bcast, Rbcast);584 /* add subnet directed network address to the self cache */585 for(i = 0; i < IPaddrlen; i++)586 bcast[i] = (ip[i] & mask[i]) & mask[i];587 addselfcache(f, ifc, lifc, bcast, Rbcast);589 /* add network directed broadcast address to the self cache */590 memmove(mask, defmask(ip), IPaddrlen);591 for(i = 0; i < IPaddrlen; i++)592 bcast[i] = (ip[i] & mask[i]) | ~mask[i];593 addselfcache(f, ifc, lifc, bcast, Rbcast);595 /* add network directed network address to the self cache */596 memmove(mask, defmask(ip), IPaddrlen);597 for(i = 0; i < IPaddrlen; i++)598 bcast[i] = (ip[i] & mask[i]) & mask[i];599 addselfcache(f, ifc, lifc, bcast, Rbcast);601 addselfcache(f, ifc, lifc, IPv4bcast, Rbcast);602 } else {603 if(ipcmp(ip, v6loopback) == 0) {604 /* add node-local mcast address */605 addselfcache(f, ifc, lifc, v6allnodesN, Rmulti);607 /* add route for all node multicast */608 addroute(f, v6allnodesN, v6allnodesNmask,609 ip, IPallbits,610 v6allnodesN, Rmulti, ifc, tifc);611 }613 /* add all nodes multicast address */614 addselfcache(f, ifc, lifc, v6allnodesL, Rmulti);616 /* add route for all nodes multicast */617 addroute(f, v6allnodesL, v6allnodesLmask,618 ip, IPallbits,619 v6allnodesL, Rmulti, ifc, tifc);621 /* add solicited-node multicast address */622 ipv62smcast(bcast, ip);623 addselfcache(f, ifc, lifc, bcast, Rmulti);624 }626 done:627 ipifcregisteraddr(f, ifc, lifc, ip);628 wunlock(ifc);629 poperror();631 return nil;632 }634 /*635 * remove a logical interface from an ifc636 * called with ifc wlock'd637 */638 static char*639 ipifcremlifc(Ipifc *ifc, Iplifc **l)640 {641 Iplifc *lifc = *l;642 Fs *f = ifc->conv->p->f;644 if(lifc == nil)645 return "address not on this interface";646 *l = lifc->next;648 /* disassociate any addresses */649 while(lifc->link != nil)650 remselfcache(f, ifc, lifc, lifc->link->self->a);652 /* remove the route for this logical interface */653 if(lifc->onlink){654 remroute(f, lifc->remote, lifc->mask,655 lifc->local, IPallbits,656 lifc->remote, lifc->type, ifc, tifc);657 if(v6addrtype(lifc->local) != linklocalv6)658 remroute(f, lifc->remote, lifc->mask,659 lifc->local, IPnoaddr,660 lifc->remote, lifc->type, ifc, tifc);661 }663 /* unregister proxy */664 if(lifc->type & Rptpt){665 if(lifc->type & Rproxy)666 ipifcregisterproxy(f, ifc, lifc->remote, 0);667 goto done;668 }670 /* remove route for all nodes multicast */671 if((lifc->type & Rv4) == 0){672 if(ipcmp(lifc->local, v6loopback) == 0)673 remroute(f, v6allnodesN, v6allnodesNmask,674 lifc->local, IPallbits,675 v6allnodesN, Rmulti, ifc, tifc);677 remroute(f, v6allnodesL, v6allnodesLmask,678 lifc->local, IPallbits,679 v6allnodesL, Rmulti, ifc, tifc);680 }682 done:683 free(lifc);684 return nil;685 }687 /*688 * remove an address from an interface.689 */690 char*691 ipifcrem(Ipifc *ifc, char **argv, int argc)692 {693 uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen];694 Iplifc *lifc, **l;695 char *err;697 if(argc < 3)698 return Ebadarg;699 if(parseipandmask(ip, mask, argv[1], argv[2]) == -1)700 return Ebadip;701 if(argc < 4)702 maskip(ip, mask, rem);703 else if(parseip(rem, argv[3]) == -1)704 return Ebadip;706 /*707 * find address on this interface and remove from chain.708 * for pt to pt we actually specify the remote address as the709 * addresss to remove.710 */711 wlock(ifc);712 l = &ifc->lifc;713 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next) {714 if(ipcmp(ip, lifc->local) == 0715 && ipcmp(mask, lifc->mask) == 0716 && ipcmp(rem, lifc->remote) == 0)717 break;718 l = &lifc->next;719 }720 err = ipifcremlifc(ifc, l);721 wunlock(ifc);722 return err;723 }725 /*726 * associate an address with the interface. This wipes out any previous727 * addresses. This is a macro that means, remove all the old interfaces728 * and add a new one.729 */730 static char*731 ipifcconnect(Conv* c, char **argv, int argc)732 {733 Ipifc *ifc = (Ipifc*)c->ptcl;734 char *err;736 wlock(ifc);737 while(ifc->lifc != nil)738 ipifcremlifc(ifc, &ifc->lifc);739 wunlock(ifc);741 err = ipifcadd(ifc, argv, argc, 0, nil);742 if(err != nil)743 return err;745 Fsconnected(c, nil);746 return nil;747 }749 char*750 ipifcra6(Ipifc *ifc, char **argv, int argc)751 {752 int i, argsleft;753 uchar sendra, recvra;754 Routerparams rp;756 i = 1;757 argsleft = argc - 1;758 if((argsleft % 2) != 0)759 return Ebadarg;761 sendra = ifc->sendra6;762 recvra = ifc->recvra6;763 rp = ifc->rp;765 while (argsleft > 1) {766 if(strcmp(argv[i], "recvra") == 0)767 recvra = atoi(argv[i+1]) != 0;768 else if(strcmp(argv[i], "sendra") == 0)769 sendra = atoi(argv[i+1]) != 0;770 else if(strcmp(argv[i], "mflag") == 0)771 rp.mflag = atoi(argv[i+1]) != 0;772 else if(strcmp(argv[i], "oflag") == 0)773 rp.oflag = atoi(argv[i+1]) != 0;774 else if(strcmp(argv[i], "maxraint") == 0)775 rp.maxraint = atoi(argv[i+1]);776 else if(strcmp(argv[i], "minraint") == 0)777 rp.minraint = atoi(argv[i+1]);778 else if(strcmp(argv[i], "linkmtu") == 0)779 rp.linkmtu = atoi(argv[i+1]);780 else if(strcmp(argv[i], "reachtime") == 0)781 rp.reachtime = atoi(argv[i+1]);782 else if(strcmp(argv[i], "rxmitra") == 0)783 rp.rxmitra = atoi(argv[i+1]);784 else if(strcmp(argv[i], "ttl") == 0)785 rp.ttl = atoi(argv[i+1]);786 else if(strcmp(argv[i], "routerlt") == 0)787 rp.routerlt = atoi(argv[i+1]);788 else789 return Ebadarg;791 argsleft -= 2;792 i += 2;793 }795 /* consistency check */796 if(rp.maxraint < rp.minraint)797 return Ebadarg;799 ifc->rp = rp;800 ifc->sendra6 = sendra;801 ifc->recvra6 = recvra;803 return nil;804 }806 /*807 * non-standard control messages.808 */809 static char*810 ipifcctl(Conv* c, char **argv, int argc)811 {812 Ipifc *ifc = (Ipifc*)c->ptcl;814 if(strcmp(argv[0], "add") == 0)815 return ipifcadd(ifc, argv, argc, 0, nil);816 else if(strcmp(argv[0], "try") == 0)817 return ipifcadd(ifc, argv, argc, 1, nil);818 else if(strcmp(argv[0], "remove") == 0)819 return ipifcrem(ifc, argv, argc);820 else if(strcmp(argv[0], "unbind") == 0)821 return ipifcunbind(ifc);822 else if(strcmp(argv[0], "mtu") == 0)823 return ipifcsetmtu(ifc, argc>1? strtoul(argv[1], 0, 0): 0);824 else if(strcmp(argv[0], "speed") == 0){825 ipifcsetspeed(ifc, argc>1? atoi(argv[1]): 0);826 return nil;827 }828 else if(strcmp(argv[0], "delay") == 0){829 ipifcsetdelay(ifc, argc>1? atoi(argv[1]): 0);830 return nil;831 }832 else if(strcmp(argv[0], "iprouting") == 0){833 iprouting(c->p->f, argc>1? atoi(argv[1]): 1);834 return nil;835 }836 else if(strcmp(argv[0], "reflect") == 0){837 ifc->reflect = argc>1? atoi(argv[1]): 1;838 return nil;839 }840 else if(strcmp(argv[0], "reassemble") == 0){841 ifc->reassemble = argc>1? atoi(argv[1]): 1;842 return nil;843 }844 else if(strcmp(argv[0], "add6") == 0)845 return ipifcadd6(ifc, argv, argc);846 else if(strcmp(argv[0], "remove6") == 0)847 return ipifcremove6(ifc, argv, argc);848 else if(strcmp(argv[0], "ra6") == 0)849 return ipifcra6(ifc, argv, argc);850 return "unsupported ctl";851 }853 int854 ipifcstats(Proto *ipifc, char *buf, int len)855 {856 return ipstats(ipifc->f, buf, len);857 }859 void860 ipifcinit(Fs *f)861 {862 Proto *ipifc;864 ipifc = smalloc(sizeof(Proto));865 ipifc->name = "ipifc";866 ipifc->connect = ipifcconnect;867 ipifc->announce = nil;868 ipifc->bind = ipifcbind;869 ipifc->state = ipifcstate;870 ipifc->create = ipifccreate;871 ipifc->close = ipifcclose;872 ipifc->rcv = nil;873 ipifc->ctl = ipifcctl;874 ipifc->advise = nil;875 ipifc->stats = ipifcstats;876 ipifc->inuse = ipifcinuse;877 ipifc->local = ipifclocal;878 ipifc->ipproto = -1;879 ipifc->nc = Maxmedia;880 ipifc->ptclsize = sizeof(Ipifc);882 f->ipifc = ipifc; /* hack for ipifcremroute, findipifc, ... */883 f->self = smalloc(sizeof(Ipselftab)); /* hack for ipforme */885 Fsproto(f, ipifc);886 }888 /*889 * add to self routing cache890 */891 static void892 addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type)893 {894 Iplink *lp;895 Ipself *p;896 int h;898 type |= (lifc->type & Rv4);899 qlock(f->self);900 if(waserror()){901 qunlock(f->self);902 nexterror();903 }905 /* see if the address already exists */906 h = hashipa(a);907 for(p = f->self->hash[h]; p != nil; p = p->next)908 if(ipcmp(a, p->a) == 0)909 break;911 /* allocate a local address and add to hash chain */912 if(p == nil){913 p = smalloc(sizeof(*p));914 ipmove(p->a, a);915 p->type = type;916 p->next = f->self->hash[h];917 f->self->hash[h] = p;919 /* if the null address, accept all packets */920 if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0)921 f->self->acceptall = 1;922 }924 /* look for a link for this lifc */925 for(lp = p->link; lp != nil; lp = lp->selflink)926 if(lp->lifc == lifc)927 break;929 /* allocate a lifc-to-local link and link to both */930 if(lp == nil){931 lp = smalloc(sizeof(*lp));932 lp->ref = 1;933 lp->lifc = lifc;934 lp->self = p;935 lp->selflink = p->link;936 p->link = lp;937 lp->lifclink = lifc->link;938 lifc->link = lp;940 /* add to routing table */941 addroute(f, a, IPallbits,942 lifc->local,943 ((type & (Rbcast|Rmulti)) != 0 || v6addrtype(a) == linklocalv6) ?944 IPallbits : IPnoaddr,945 a, type, ifc, tifc);947 if((type & Rmulti) && ifc->m->addmulti != nil)948 (*ifc->m->addmulti)(ifc, a, lifc->local);949 } else950 lp->ref++;952 qunlock(f->self);953 poperror();954 }956 /*957 * These structures are unlinked from their chains while958 * other threads may be using them. To avoid excessive locking,959 * just put them aside for a while before freeing them.960 * called with f->self locked961 */962 static Iplink *freeiplink;963 static Ipself *freeipself;965 static void966 iplinkfree(Iplink *p)967 {968 Iplink **l, *np;969 ulong now = NOW;971 l = &freeiplink;972 for(np = *l; np != nil; np = *l){973 if((long)(now - np->expire) >= 0){974 *l = np->next;975 free(np);976 continue;977 }978 l = &np->next;979 }980 p->expire = now + 5000; /* give other threads 5 secs to get out */981 p->next = nil;982 *l = p;983 }985 static void986 ipselffree(Ipself *p)987 {988 Ipself **l, *np;989 ulong now = NOW;991 l = &freeipself;992 for(np = *l; np != nil; np = *l){993 if((long)(now - np->expire) >= 0){994 *l = np->next;995 free(np);996 continue;997 }998 l = &np->next;999 }1000 p->expire = now + 5000; /* give other threads 5 secs to get out */1001 p->next = nil;1002 *l = p;1003 }1005 /*1006 * Decrement reference for this address on this link.1007 * Unlink from selftab if this is the last ref.1008 */1009 static void1010 remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a)1011 {1012 Ipself *p, **l;1013 Iplink *link, **l_self, **l_lifc;1015 qlock(f->self);1017 /* find the unique selftab entry */1018 l = &f->self->hash[hashipa(a)];1019 for(p = *l; p != nil; p = *l){1020 if(ipcmp(p->a, a) == 0)1021 break;1022 l = &p->next;1023 }1025 if(p == nil)1026 goto out;1028 /*1029 * walk down links from an ifc looking for one1030 * that matches the selftab entry1031 */1032 l_lifc = &lifc->link;1033 for(link = *l_lifc; link != nil; link = *l_lifc){1034 if(link->self == p)1035 break;1036 l_lifc = &link->lifclink;1037 }1039 if(link == nil)1040 goto out;1042 /*1043 * walk down the links from the selftab looking for1044 * the one we just found1045 */1046 l_self = &p->link;1047 for(link = *l_self; link != nil; link = *l_self){1048 if(link == *l_lifc)1049 break;1050 l_self = &link->selflink;1051 }1053 if(link == nil)1054 panic("remselfcache");1056 if(--(link->ref) != 0)1057 goto out;1059 /* remove from routing table */1060 remroute(f, a, IPallbits,1061 lifc->local,1062 ((p->type & (Rbcast|Rmulti)) != 0 || v6addrtype(a) == linklocalv6) ?1063 IPallbits : IPnoaddr,1064 a, p->type, ifc, tifc);1066 if((p->type & Rmulti) && ifc->m->remmulti != nil){1067 if(!waserror()){1068 (*ifc->m->remmulti)(ifc, a, lifc->local);1069 poperror();1070 }1071 }1073 /* ref == 0, remove from both chains and free the link */1074 *l_lifc = link->lifclink;1075 *l_self = link->selflink;1076 iplinkfree(link);1078 if(p->link != nil)1079 goto out;1081 /* if null address, forget */1082 if(ipcmp(a, v4prefix) == 0 || ipcmp(a, IPnoaddr) == 0)1083 f->self->acceptall = 0;1085 /* no more links, remove from hash and free */1086 *l = p->next;1087 ipselffree(p);1089 out:1090 qunlock(f->self);1091 }1093 long1094 ipselftabread(Fs *f, char *cp, ulong offset, int n)1095 {1096 int i, m, nifc, off;1097 Ipself *p;1098 Iplink *link;1099 char state[8];1101 m = 0;1102 off = offset;1103 qlock(f->self);1104 for(i = 0; i < NHASH && m < n; i++){1105 for(p = f->self->hash[i]; p != nil && m < n; p = p->next){1106 nifc = 0;1107 for(link = p->link; link != nil; link = link->selflink)1108 nifc++;1109 routetype(p->type, state);1110 m += snprint(cp + m, n - m, "%-44.44I %2.2d %4.4s\n",1111 p->a, nifc, state);1112 if(off > 0){1113 off -= m;1114 m = 0;1115 }1116 }1117 }1118 qunlock(f->self);1119 return m;1120 }1122 /*1123 * returns1124 * 0 - no match1125 * Runi1126 * Rbcast1127 * Rmulti1128 */1129 int1130 ipforme(Fs *f, uchar *addr)1131 {1132 Ipself *p;1134 for(p = f->self->hash[hashipa(addr)]; p != nil; p = p->next)1135 if(ipcmp(addr, p->a) == 0)1136 return p->type & (Runi|Rbcast|Rmulti);1138 /* hack to say accept anything */1139 if(f->self->acceptall)1140 return Runi;1142 return 0;1143 }1145 /*1146 * find the ifc on same net as the remote system. If none,1147 * return nil.1148 */1149 Ipifc*1150 findipifc(Fs *f, uchar *local, uchar *remote, int type)1151 {1152 uchar gnet[IPaddrlen];1153 int spec, xspec;1154 Ipifc *ifc, *x;1155 Iplifc *lifc;1156 Conv **cp;1158 x = nil;1159 xspec = 0;1160 for(cp = f->ipifc->conv; *cp != nil; cp++){1161 ifc = (Ipifc*)(*cp)->ptcl;1162 rlock(ifc);1163 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){1164 if(type & Runi){1165 if(ipcmp(remote, lifc->local) == 0){1166 Found:1167 runlock(ifc);1168 return ifc;1169 }1170 } else if(type & (Rbcast|Rmulti)) {1171 if(ipcmp(local, lifc->local) == 0)1172 goto Found;1173 }1174 maskip(remote, lifc->mask, gnet);1175 if(ipcmp(gnet, lifc->net) == 0){1176 spec = comprefixlen(remote, lifc->local, IPaddrlen);1177 if(spec > xspec){1178 x = ifc;1179 xspec = spec;1180 }1181 }1182 }1183 runlock(ifc);1184 }1185 return x;1186 }1188 Ipifc*1189 findipifcstr(Fs *f, char *s)1190 {1191 uchar ip[IPaddrlen];1192 Conv *c;1193 char *p;1194 long x;1196 x = strtol(s, &p, 10);1197 if(p > s && *p == '\0'){1198 if(x < 0)1199 return nil;1200 if(x < f->ipifc->nc && (c = f->ipifc->conv[x]) != nil && ipifcinuse(c))1201 return (Ipifc*)c->ptcl;1202 }1203 if(parseip(ip, s) != -1)1204 return findipifc(f, ip, ip, Runi);1205 return nil;1206 }1208 /*1209 * find "best" (global > link local > unspecified)1210 * local address; address must be current.1211 */1212 static void1213 findprimaryipv6(Fs *f, uchar *local)1214 {1215 ulong now = NOW/1000;1216 int atype, atypel;1217 Iplifc *lifc;1218 Ipifc *ifc;1219 Conv **cp;1221 ipmove(local, v6Unspecified);1222 atype = unspecifiedv6;1224 for(cp = f->ipifc->conv; *cp != nil; cp++){1225 ifc = (Ipifc*)(*cp)->ptcl;1226 rlock(ifc);1227 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){1228 atypel = v6addrtype(lifc->local);1229 if(atypel > atype)1230 if(lifc->preflt == ~0UL || lifc->preflt >= now-lifc->origint) {1231 ipmove(local, lifc->local);1232 atype = atypel;1233 if(atype == globalv6){1234 runlock(ifc);1235 return;1236 }1237 }1238 }1239 runlock(ifc);1240 }1241 }1243 /*1244 * returns first v4 address configured1245 */1246 static void1247 findprimaryipv4(Fs *f, uchar *local)1248 {1249 Iplifc *lifc;1250 Ipifc *ifc;1251 Conv **cp;1253 /* find first ifc local address */1254 for(cp = f->ipifc->conv; *cp != nil; cp++){1255 ifc = (Ipifc*)(*cp)->ptcl;1256 rlock(ifc);1257 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){1258 if((lifc->type & Rv4) != 0){1259 ipmove(local, lifc->local);1260 runlock(ifc);1261 return;1262 }1263 }1264 runlock(ifc);1265 }1266 ipmove(local, IPnoaddr);1267 }1269 /*1270 * return v4 address associated with an interface close to remote1271 */1272 int1273 ipv4local(Ipifc *ifc, uchar *local, uchar *remote)1274 {1275 Iplifc *lifc;1276 int a, b;1278 b = -1;1279 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){1280 if((lifc->type & Rv4) == 0 || ipcmp(lifc->local, IPnoaddr) == 0)1281 continue;1282 a = comprefixlen(lifc->local+IPv4off, remote, IPv4addrlen);1283 if(a > b){1284 b = a;1285 memmove(local, lifc->local+IPv4off, IPv4addrlen);1286 }1287 }1288 return b >= 0;1289 }1291 /*1292 * return v6 address associated with an interface close to remote1293 */1294 int1295 ipv6local(Ipifc *ifc, uchar *local, uchar *remote)1296 {1297 struct {1298 int atype;1299 int deprecated;1300 int comprefixlen;1301 } a, b;1302 int atype;1303 ulong now;1304 Iplifc *lifc;1306 if(isv4(remote)){1307 ipmove(local, v4prefix);1308 return ipv4local(ifc, local+IPv4off, remote+IPv4off);1309 }1311 atype = v6addrtype(remote);1312 ipmove(local, v6Unspecified);1313 b.atype = unknownv6;1314 b.deprecated = 1;1315 b.comprefixlen = 0;1317 now = NOW/1000;1318 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){1319 if(lifc->tentative)1320 continue;1322 a.atype = v6addrtype(lifc->local);1323 a.deprecated = lifc->preflt != ~0UL && lifc->preflt < now-lifc->origint;1324 a.comprefixlen = comprefixlen(lifc->local, remote, IPaddrlen);1326 /* prefer appropriate scope */1327 if(a.atype != b.atype){1328 if(a.atype > b.atype && b.atype < atype ||1329 a.atype < b.atype && b.atype > atype)1330 goto Good;1331 continue;1332 }1333 /* prefer non-deprecated addresses */1334 if(a.deprecated != b.deprecated){1335 if(b.deprecated)1336 goto Good;1337 continue;1338 }1339 /* prefer longer common prefix */1340 if(a.comprefixlen != b.comprefixlen){1341 if(a.comprefixlen > b.comprefixlen)1342 goto Good;1343 continue;1344 }1345 continue;1346 Good:1347 b = a;1348 ipmove(local, lifc->local);1349 }1351 return b.atype >= atype;1352 }1354 void1355 findlocalip(Fs *f, uchar *local, uchar *remote)1356 {1357 Route *r;1358 Iplifc *lifc;1359 Ipifc *ifc, *nifc;1360 Conv **cp;1362 for(cp = f->ipifc->conv; *cp != nil; cp++){1363 ifc = (Ipifc*)(*cp)->ptcl;1364 rlock(ifc);1365 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){1366 if(lifc->tentative)1367 continue;1369 r = v6lookup(f, remote, lifc->local, nil);1370 if(r == nil || (nifc = r->ifc) == nil)1371 continue;1372 if(r->type & Runi){1373 ipmove(local, remote);1374 runlock(ifc);1375 return;1376 }1377 if(nifc != ifc) rlock(nifc);1378 if((r->type & (Rifc|Rbcast|Rmulti|Rv4)) == Rv4){1379 ipmove(local, v4prefix);1380 if(ipv4local(nifc, local+IPv4off, r->v4.gate)){1381 if(nifc != ifc) runlock(nifc);1382 runlock(ifc);1383 return;1384 }1385 }1386 if(ipv6local(nifc, local, remote)){1387 if(nifc != ifc) runlock(nifc);1388 runlock(ifc);1389 return;1390 }1391 if(nifc != ifc) runlock(nifc);1392 }1393 runlock(ifc);1394 }1395 if(isv4(remote))1396 findprimaryipv4(f, local);1397 else1398 findprimaryipv6(f, local);1399 }1402 /*1403 * see if this address is bound to the interface1404 */1405 Iplifc*1406 iplocalonifc(Ipifc *ifc, uchar *ip)1407 {1408 Iplifc *lifc;1410 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next)1411 if(ipcmp(ip, lifc->local) == 0)1412 return lifc;1414 return nil;1415 }1417 Iplifc*1418 ipremoteonifc(Ipifc *ifc, uchar *ip)1419 {1420 uchar net[IPaddrlen];1421 Iplifc *lifc;1423 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){1424 maskip(ip, lifc->mask, net);1425 if(ipcmp(net, lifc->remote) == 0)1426 return lifc;1427 }1428 return nil;1429 }1432 /*1433 * See if we're proxying for this address on this interface1434 */1435 int1436 ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip)1437 {1438 Route *r;1440 /* see if this is a direct connected pt to pt address */1441 r = v6lookup(f, ip, ip, nil);1442 if(r == nil || (r->type & (Rifc|Rproxy)) != (Rifc|Rproxy))1443 return 0;1445 return ipremoteonifc(ifc, ip) != nil;1446 }1448 /*1449 * return multicast version if any1450 */1451 int1452 ipismulticast(uchar *ip)1453 {1454 if(isv4(ip)){1455 if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)1456 return V4;1457 }1458 else if(ip[0] == 0xff)1459 return V6;1460 return 0;1461 }1463 /*1464 * add a multicast address to an interface.1465 */1466 void1467 ipifcaddmulti(Conv *c, uchar *ma, uchar *ia)1468 {1469 Ipmulti *multi, **l;1470 Iplifc *lifc;1471 Ipifc *ifc;1472 Fs *f;1474 if(isv4(ma) != isv4(ia))1475 error("incompatible multicast/interface ip address");1477 for(l = &c->multi; *l != nil; l = &(*l)->next)1478 if(ipcmp(ma, (*l)->ma) == 0 && ipcmp(ia, (*l)->ia) == 0)1479 return; /* it's already there */1481 f = c->p->f;1482 if((ifc = findipifc(f, ia, ma, Rmulti)) != nil){1483 rlock(ifc);1484 if(waserror()){1485 runlock(ifc);1486 nexterror();1487 }1488 if((lifc = iplocalonifc(ifc, ia)) != nil)1489 addselfcache(f, ifc, lifc, ma, Rmulti);1490 runlock(ifc);1491 poperror();1492 }1494 multi = smalloc(sizeof(*multi));1495 ipmove(multi->ma, ma);1496 ipmove(multi->ia, ia);1497 multi->next = nil;1498 *l = multi;1499 }1502 /*1503 * remove a multicast address from an interface.1504 */1505 void1506 ipifcremmulti(Conv *c, uchar *ma, uchar *ia)1507 {1508 Ipmulti *multi, **l;1509 Iplifc *lifc;1510 Ipifc *ifc;1511 Fs *f;1513 for(l = &c->multi; *l != nil; l = &(*l)->next)1514 if(ipcmp(ma, (*l)->ma) == 0 && ipcmp(ia, (*l)->ia) == 0)1515 break;1517 multi = *l;1518 if(multi == nil)1519 return; /* we don't have it open */1521 *l = multi->next;1522 multi->next = nil;1524 f = c->p->f;1525 if((ifc = findipifc(f, ia, ma, Rmulti)) != nil){1526 rlock(ifc);1527 if(!waserror()){1528 if((lifc = iplocalonifc(ifc, ia)) != nil)1529 remselfcache(f, ifc, lifc, ma);1530 poperror();1531 }1532 runlock(ifc);1533 }1534 free(multi);1535 }1537 /* register the address on this network for address resolution */1538 static void1539 ipifcregisteraddr(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *ip)1540 {1541 if(waserror()){1542 print("ipifcregisteraddr %s %I %I: %s\n", ifc->dev, lifc->local, ip, up->errstr);1543 return;1544 }1545 if(ifc->m != nil && ifc->m->areg != nil)1546 (*ifc->m->areg)(f, ifc, lifc, ip);1547 poperror();1548 }1550 static void1551 ipifcregisterproxy(Fs *f, Ipifc *ifc, uchar *ip, int add)1552 {1553 uchar a[IPaddrlen];1554 Iplifc *lifc;1555 Ipifc *nifc;1556 Conv **cp;1558 /* register the address on any interface that will proxy for the ip */1559 for(cp = f->ipifc->conv; *cp != nil; cp++){1560 nifc = (Ipifc*)(*cp)->ptcl;1561 if(nifc == ifc || !canrlock(nifc))1562 continue;1564 if(nifc->m == nil1565 || (lifc = ipremoteonifc(nifc, ip)) == nil1566 || (lifc->type & Rptpt) != 01567 || waserror()){1568 runlock(nifc);1569 continue;1570 }1571 if((lifc->type & Rv4) == 0){1572 /* add solicited-node multicast addr */1573 ipv62smcast(a, ip);1574 if(add)1575 addselfcache(f, nifc, lifc, a, Rmulti);1576 else1577 remselfcache(f, nifc, lifc, a);1578 }1579 if(add)1580 ipifcregisteraddr(f, nifc, lifc, ip);1581 runlock(nifc);1582 poperror();1583 }1584 }1586 char*1587 ipifcadd6(Ipifc *ifc, char **argv, int argc)1588 {1589 int plen = 64;1590 char addr[40], preflen[6];1591 char *params[3];1592 uchar prefix[IPaddrlen];1593 Iplifc lifc;1594 Medium *m;1596 lifc.onlink = 1;1597 lifc.autoflag = 1;1598 lifc.validlt = lifc.preflt = ~0UL;1599 lifc.origint = NOW / 1000;1601 switch(argc) {1602 case 7:1603 lifc.preflt = strtoul(argv[6], 0, 10);1604 /* fall through */1605 case 6:1606 lifc.validlt = strtoul(argv[5], 0, 10);1607 /* fall through */1608 case 5:1609 lifc.autoflag = atoi(argv[4]) != 0;1610 /* fall through */1611 case 4:1612 lifc.onlink = atoi(argv[3]) != 0;1613 /* fall through */1614 case 3:1615 plen = atoi(argv[2]);1616 /* fall through */1617 case 2:1618 break;1619 default:1620 return Ebadarg;1621 }1623 if (parseip(prefix, argv[1]) != 6 || lifc.validlt < lifc.preflt || plen < 0 ||1624 plen > 64 || islinklocal(prefix))1625 return Ebadarg;1627 /* issue "add" ctl msg for v6 link-local addr and prefix len */1628 m = ifc->m;1629 if(m == nil || m->pref2addr == nil)1630 return Eunbound;1631 (*m->pref2addr)(prefix, ifc->mac); /* mac → v6 link-local addr */1633 sprint(addr, "%I", prefix);1634 sprint(preflen, "/%d", plen);1635 params[0] = "add";1636 params[1] = addr;1637 params[2] = preflen;1639 return ipifcadd(ifc, params, 3, 0, &lifc);1640 }1642 char*1643 ipifcremove6(Ipifc *ifc, char**, int argc)1644 {1645 Iplifc *lifc, **l;1646 ulong now;1648 if(argc != 1)1649 return Ebadarg;1651 wlock(ifc);1652 now = NOW/1000;1653 for(l = &ifc->lifc; (lifc = *l) != nil;) {1654 if((lifc->type & Rv4) == 0)1655 if(lifc->validlt != ~0UL && lifc->validlt < now-lifc->origint)1656 if(ipifcremlifc(ifc, l) == nil)1657 continue;1658 l = &lifc->next;1659 }1660 wunlock(ifc);1662 return nil;1663 }