/* * add an address to an interface. */ char* ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp) { int i, type, mtu, sendnbrdisc = 0; uchar ip[IPaddrlen], mask[IPaddrlen], rem[IPaddrlen]; uchar bcast[IPaddrlen], net[IPaddrlen]; Iplifc *lifc, **l; Fs *f; if(ifc->m == nil) return "ipifc not yet bound to device"; f = ifc->conv->p->f; type = Rifc; memset(ip, 0, IPaddrlen); memset(mask, 0, IPaddrlen); memset(rem, 0, IPaddrlen); switch(argc) { case 6: if(strcmp(argv[5], "proxy") == 0) type |= Rproxy; /* fall through */ case 5: mtu = strtoul(argv[4], 0, 0); if(mtu >= ifc->m->mintu && mtu <= ifc->m->maxtu) ifc->maxtu = mtu; /* fall through */ case 4: if (parseip(ip, argv[1]) == -1 || parseip(rem, argv[3]) == -1) return Ebadip; parseipmask(mask, argv[2]); maskip(rem, mask, net); break; case 3: if (parseip(ip, argv[1]) == -1) return Ebadip; parseipmask(mask, argv[2]); maskip(ip, mask, rem); maskip(rem, mask, net); break; case 2: if (parseip(ip, argv[1]) == -1) return Ebadip; memmove(mask, defmask(ip), IPaddrlen); maskip(ip, mask, rem); maskip(rem, mask, net); break; default: return Ebadarg; } if(isv4(ip)) tentative = 0; wlock(ifc); /* ignore if this is already a local address for this ifc */ for(lifc = ifc->lifc; lifc; lifc = lifc->next) { if(ipcmp(lifc->local, ip) == 0) { if(lifc->tentative != tentative) lifc->tentative = tentative; if(lifcp) { lifc->onlink = lifcp->onlink; lifc->autoflag = lifcp->autoflag; lifc->validlt = lifcp->validlt; lifc->preflt = lifcp->preflt; lifc->origint = lifcp->origint; } goto out; } } /* add the address to the list of logical ifc's for this ifc */ lifc = smalloc(sizeof(Iplifc)); ipmove(lifc->local, ip); ipmove(lifc->mask, mask); ipmove(lifc->remote, rem); ipmove(lifc->net, net); lifc->tentative = tentative; if(lifcp) { lifc->onlink = lifcp->onlink; lifc->autoflag = lifcp->autoflag; lifc->validlt = lifcp->validlt; lifc->preflt = lifcp->preflt; lifc->origint = lifcp->origint; } else { /* default values */ lifc->onlink = lifc->autoflag = 1; lifc->validlt = lifc->preflt = ~0L; lifc->origint = NOW / 1000; } lifc->next = nil; for(l = &ifc->lifc; *l; l = &(*l)->next) ; *l = lifc; /* check for point-to-point interface */ if(ipcmp(ip, v6loopback)) /* skip v6 loopback, it's a special address */ if(ipcmp(mask, IPallbits) == 0) type |= Rptpt; /* add local routes */ if(isv4(ip)) v4addroute(f, tifc, rem+IPv4off, mask+IPv4off, rem+IPv4off, type); else v6addroute(f, tifc, rem, mask, rem, type); addselfcache(f, ifc, lifc, ip, Runi); if((type & (Rproxy|Rptpt)) == (Rproxy|Rptpt)) { ipifcregisterproxy(f, ifc, rem); goto out; } if(isv4(ip) || ipcmp(ip, IPnoaddr) == 0) { /* add subnet directed broadcast address to the self cache */ for(i = 0; i < IPaddrlen; i++) bcast[i] = (ip[i] & mask[i]) | ~mask[i]; addselfcache(f, ifc, lifc, bcast, Rbcast); /* add subnet directed network address to the self cache */ for(i = 0; i < IPaddrlen; i++) bcast[i] = (ip[i] & mask[i]) & mask[i]; addselfcache(f, ifc, lifc, bcast, Rbcast); /* add network directed broadcast address to the self cache */ memmove(mask, defmask(ip), IPaddrlen); for(i = 0; i < IPaddrlen; i++) bcast[i] = (ip[i] & mask[i]) | ~mask[i]; addselfcache(f, ifc, lifc, bcast, Rbcast); /* add network directed network address to the self cache */ memmove(mask, defmask(ip), IPaddrlen); for(i = 0; i < IPaddrlen; i++) bcast[i] = (ip[i] & mask[i]) & mask[i]; addselfcache(f, ifc, lifc, bcast, Rbcast); addselfcache(f, ifc, lifc, IPv4bcast, Rbcast); } else { if(ipcmp(ip, v6loopback) == 0) { /* add node-local mcast address */ addselfcache(f, ifc, lifc, v6allnodesN, Rmulti); /* add route for all node multicast */ v6addroute(f, tifc, v6allnodesN, v6allnodesNmask, v6allnodesN, Rmulti); } /* add all nodes multicast address */ addselfcache(f, ifc, lifc, v6allnodesL, Rmulti); /* add route for all nodes multicast */ v6addroute(f, tifc, v6allnodesL, v6allnodesLmask, v6allnodesL, Rmulti); /* add solicited-node multicast address */ ipv62smcast(bcast, ip); addselfcache(f, ifc, lifc, bcast, Rmulti); sendnbrdisc = 1; } /* register the address on this network for address resolution */ if(isv4(ip) && ifc->m->areg != nil) (*ifc->m->areg)(ifc, ip); out: wunlock(ifc); if(tentative && sendnbrdisc) icmpns(f, 0, SRC_UNSPEC, ip, TARG_MULTI, ifc->mac); return nil; }
/* * encapsulate next IP packet on x's write queue in IP/ESP packet * and initiate output of the result. */ static void espkick(void *x) { int nexthdr, payload, pad, align; uchar *auth; Block *bp; Conv *c = x; Esp4hdr *eh4; Esp6hdr *eh6; Espcb *ecb; Esptail *et; Userhdr *uh; Versdep vers; getverslens(convipvers(c), &vers); bp = qget(c->wq); if(bp == nil) return; qlock(c); ecb = c->ptcl; if(ecb->header) { /* make sure the message has a User header */ bp = pullupblock(bp, Userhdrlen); if(bp == nil) { qunlock(c); return; } uh = (Userhdr*)bp->rp; nexthdr = uh->nexthdr; bp->rp += Userhdrlen; } else { nexthdr = 0; /* what should this be? */ } payload = BLEN(bp) + ecb->espivlen; /* Make space to fit ip header */ bp = padblock(bp, vers.hdrlen + ecb->espivlen); getpktspiaddrs(bp->rp, &vers); align = 4; if(ecb->espblklen > align) align = ecb->espblklen; if(align % ecb->ahblklen != 0) panic("espkick: ahblklen is important after all"); pad = (align-1) - (payload + Esptaillen-1)%align; /* * Make space for tail * this is done by calling padblock with a negative size * Padblock does not change bp->wp! */ bp = padblock(bp, -(pad+Esptaillen+ecb->ahlen)); bp->wp += pad+Esptaillen+ecb->ahlen; et = (Esptail*)(bp->rp + vers.hdrlen + payload + pad); /* fill in tail */ et->pad = pad; et->nexthdr = nexthdr; /* encrypt the payload */ ecb->cipher(ecb, bp->rp + vers.hdrlen, payload + pad + Esptaillen); auth = bp->rp + vers.hdrlen + payload + pad + Esptaillen; /* fill in head; construct a new IP header and an ESP header */ if (vers.version == V4) { eh4 = (Esp4hdr *)bp->rp; eh4->vihl = IP_VER4; v6tov4(eh4->espsrc, c->laddr); v6tov4(eh4->espdst, c->raddr); eh4->espproto = IP_ESPPROTO; eh4->frag[0] = 0; eh4->frag[1] = 0; hnputl(eh4->espspi, ecb->spi); hnputl(eh4->espseq, ++ecb->seq); } else { eh6 = (Esp6hdr *)bp->rp; eh6->vcf[0] = IP_VER6; ipmove(eh6->src, c->laddr); ipmove(eh6->dst, c->raddr); eh6->proto = IP_ESPPROTO; hnputl(eh6->espspi, ecb->spi); hnputl(eh6->espseq, ++ecb->seq); } /* compute secure hash */ ecb->auth(ecb, bp->rp + vers.iphdrlen, (vers.hdrlen - vers.iphdrlen) + payload + pad + Esptaillen, auth); qunlock(c); /* print("esp: pass down: %uld\n", BLEN(bp)); */ if (vers.version == V4) ipoput4(c->p->f, bp, 0, c->ttl, c->tos, c); else ipoput6(c->p->f, bp, 0, c->ttl, c->tos, c); }
/* * find the local address 'closest' to the remote system, copy it to * local and return the ifc for that address */ void findlocalip(Fs *f, uchar *local, uchar *remote) { int version, atype = unspecifiedv6, atypel = unknownv6; int atyper, deprecated; uchar gate[IPaddrlen], gnet[IPaddrlen]; Ipifc *ifc; Iplifc *lifc; Route *r; USED(atype); USED(atypel); qlock(f->ipifc); r = v6lookup(f, remote, nil); version = (memcmp(remote, v4prefix, IPv4off) == 0)? V4: V6; if(r != nil) { ifc = r->ifc; if(r->type & Rv4) v4tov6(gate, r->v4.gate); else { ipmove(gate, r->v6.gate); ipmove(local, v6Unspecified); } switch(version) { case V4: /* find ifc address closest to the gateway to use */ for(lifc = ifc->lifc; lifc; lifc = lifc->next) { maskip(gate, lifc->mask, gnet); if(ipcmp(gnet, lifc->net) == 0) { ipmove(local, lifc->local); goto out; } } break; case V6: /* find ifc address with scope matching the destination */ atyper = v6addrtype(remote); deprecated = 0; for(lifc = ifc->lifc; lifc; lifc = lifc->next) { atypel = v6addrtype(lifc->local); /* prefer appropriate scope */ if(atypel > atype && atype < atyper || atypel < atype && atype > atyper) { ipmove(local, lifc->local); deprecated = !v6addrcurr(lifc); atype = atypel; } else if(atypel == atype) { /* avoid deprecated addresses */ if(deprecated && v6addrcurr(lifc)) { ipmove(local, lifc->local); atype = atypel; deprecated = 0; } } if(atype == atyper && !deprecated) goto out; } if(atype >= atyper) goto out; break; default: panic("findlocalip: version %d", version); } } switch(version) { case V4: findprimaryipv4(f, local); break; case V6: findprimaryipv6(f, local); break; default: panic("findlocalip2: version %d", version); } out: qunlock(f->ipifc); }
/* * fill in all the requested attributes for a system. * if the system's entry doesn't have all required, * walk through successively more inclusive networks * for inherited attributes. */ Ndbtuple* ndbipinfo(Ndb *db, char *attr, char *val, char **alist, int n) { Ndbtuple *t, *nt, *f; Ndbs s; char *ipstr; uint8_t net[IPaddrlen], ip[IPaddrlen]; int prefix, smallestprefix, force; int64_t r; /* just in case */ fmtinstall('I', eipfmt); fmtinstall('M', eipfmt); /* get needed attributes */ f = mkfilter(n, alist); /* * first look for a matching entry with an ip address */ t = nil; ipstr = ndbgetvalue(db, &s, attr, val, "ip", &nt); if(ipstr == nil){ /* none found, make one up */ if(strcmp(attr, "ip") != 0) { ndbfree(f); return nil; } t = ndbnew("ip", val); t->line = t; t->entry = nil; r = parseip(net, val); if(r == -1) ndbfree(t); } else { /* found one */ while(nt != nil){ nt = ndbreorder(nt, s.t); t = ndbconcatenate(t, nt); nt = ndbsnext(&s, attr, val); } r = parseip(net, ipstr); free(ipstr); } if(r < 0){ ndbfree(f); return nil; } ipmove(ip, net); t = filter(db, t, f); /* * now go through subnets to fill in any missing attributes */ if(isv4(net)){ prefix = 127; smallestprefix = 100; force = 0; } else { /* in v6, the last 8 bytes have no structure (we hope) */ prefix = 64; smallestprefix = 2; memset(net+8, 0, 8); force = 1; } /* * to find a containing network, keep turning off * the lower bit and look for a network with * that address and a shorter mask. tedius but * complete, we may need to find a trick to speed this up. */ for(; prefix >= smallestprefix; prefix--){ if(filtercomplete(f)) break; if(!force && (net[prefix/8] & (1<<(7-(prefix%8)))) == 0) continue; force = 0; net[prefix/8] &= ~(1<<(7-(prefix%8))); t = ndbconcatenate(t, subnet(db, net, f, prefix)); } /* * if there's an unfulfilled ipmask, make one up */ nt = ndbfindattr(f, f, "ipmask"); if(nt && !(nt->ptr & Fignore)){ char x[64]; snprint(x, sizeof(x), "%M", defmask(ip)); t = ndbconcatenate(t, ndbnew("ipmask", x)); } ndbfree(f); ndbsetmalloctag(t, getcallerpc()); return t; }
/* * called with protocol locked */ Conv* Fsprotoclone(Proto *p, char *user) { Conv *c, **pp, **ep; retry: c = nil; ep = &p->conv[p->nc]; for(pp = p->conv; pp < ep; pp++) { c = *pp; if(c == nil){ c = malloc(sizeof(Conv)); if(c == nil) error(Enomem); qlock(c); c->p = p; c->x = pp - p->conv; if(p->ptclsize != 0){ c->ptcl = malloc(p->ptclsize); if(c->ptcl == nil) { free(c); error(Enomem); } } *pp = c; p->ac++; c->eq = qopen(1024, Qmsg, 0, 0); (*p->create)(c); break; } if(canqlock(c)){ /* * make sure both processes and protocol * are done with this Conv */ if(c->inuse == 0 && (p->inuse == nil || (*p->inuse)(c) == 0)) break; qunlock(c); } } if(pp >= ep) { if(p->gc) print("Fsprotoclone: garbage collecting Convs\n"); if(p->gc != nil && (*p->gc)(p)) goto retry; /* debugging: do we ever get here? */ if (cpuserver) panic("Fsprotoclone: all conversations in use"); return nil; } c->inuse = 1; kstrdup(&c->owner, user); c->perm = 0660; c->state = Idle; ipmove(c->laddr, IPnoaddr); ipmove(c->raddr, IPnoaddr); c->r = nil; c->rgen = 0; c->lport = 0; c->rport = 0; c->restricted = 0; c->maxfragsize = 0; c->ttl = MAXTTL; qreopen(c->rq); qreopen(c->wq); qreopen(c->eq); qunlock(c); return c; }
/* * find the local address 'closest' to the remote system, copy it to * local and return the ifc for that address */ void findlocalip(struct Fs *f, uint8_t * local, uint8_t * remote) { struct Ipifc *ifc; struct Iplifc *lifc; struct route *r; uint8_t gate[IPaddrlen]; uint8_t gnet[IPaddrlen]; int version; int atype = unspecifiedv6, atypel = unknownv6; qlock(&f->ipifc->qlock); r = v6lookup(f, remote, NULL); version = (memcmp(remote, v4prefix, IPv4off) == 0) ? V4 : V6; if (r != NULL) { ifc = r->rt.ifc; if (r->rt.type & Rv4) v4tov6(gate, r->v4.gate); else { ipmove(gate, r->v6.gate); ipmove(local, v6Unspecified); } /* find ifc address closest to the gateway to use */ switch (version) { case V4: for (lifc = ifc->lifc; lifc; lifc = lifc->next) { maskip(gate, lifc->mask, gnet); if (ipcmp(gnet, lifc->net) == 0) { ipmove(local, lifc->local); goto out; } } break; case V6: for (lifc = ifc->lifc; lifc; lifc = lifc->next) { atypel = v6addrtype(lifc->local); maskip(gate, lifc->mask, gnet); if (ipcmp(gnet, lifc->net) == 0) if (atypel > atype) if (v6addrcurr(lifc)) { ipmove(local, lifc->local); atype = atypel; if (atype == globalv6) break; } } if (atype > unspecifiedv6) goto out; break; default: panic("findlocalip: version %d", version); } } switch (version) { case V4: findprimaryipv4(f, local); break; case V6: findprimaryipv6(f, local); break; default: panic("findlocalip2: version %d", version); } out: qunlock(&f->ipifc->qlock); }
void dhcprecv(void) { uint8_t buf[2000]; Bootp *bp; int n, type; uint32_t lease; uint8_t mask[IPaddrlen]; qunlock(&dhcp.lk); n = read(dhcp.fd, buf, sizeof(buf)); qlock(&dhcp.lk); if(n <= 0) myfatal("dhcprecv: bad read: %r"); bp = parse(buf, n); if(bp == 0) return; if(1) { fprint(2, "recved\n"); bootpdump(buf, n); } type = optgetbyte(bp, ODtype); switch(type) { default: fprint(2, "dhcprecv: unknown type: %d\n", type); break; case Offer: if(dhcp.state != Sselecting) break; lease = optgetulong(bp, ODlease); if(lease == 0) myfatal("bad lease"); if(!optgetaddr(bp, OBmask, mask)) memset(mask, 0xff, sizeof(mask)); v4tov6(dhcp.client, bp->yiaddr); if(!optgetaddr(bp, ODserverid, dhcp.server)) { fprint(2, "dhcprecv: Offer from server with invalid serverid\n"); break; } dhcp.lease = lease; ipmove(dhcp.mask, mask); memmove(dhcp.sname, bp->sname, sizeof(dhcp.sname)); dhcp.sname[sizeof(dhcp.sname)-1] = 0; dhcpsend(Request); dhcp.state = Srequesting; dhcp.resend = 0; dhcp.timeout = 4; break; case Ack: if(dhcp.state != Srequesting) if(dhcp.state != Srenewing) if(dhcp.state != Srebinding) break; lease = optgetulong(bp, ODlease); if(lease == 0) myfatal("bad lease"); if(!optgetaddr(bp, OBmask, mask)) memset(mask, 0xff, sizeof(mask)); v4tov6(dhcp.client, bp->yiaddr); dhcp.lease = lease; ipmove(dhcp.mask, mask); dhcp.state = Sbound; break; case Nak: myfatal("recved nak"); break; } }