diff --git a/sys/src/9/ip/arp.c b/sys/src/9/ip/arp.c index f71b0bad6..9c1a0cfc7 100644 --- a/sys/src/9/ip/arp.c +++ b/sys/src/9/ip/arp.c @@ -401,7 +401,7 @@ arpwrite(Fs *fs, char *s, int len) if((ifc = findipifc(fs, ia, ia, Runi)) == nil) error("no interface"); rlock(ifc); - if(!ipv6local(ifc, ia, ip) || arpenter(fs, V6, ip, mac, n, ia, ifc, 0) < 0){ + if(!ipv6local(ifc, ia, 0, ip) || arpenter(fs, V6, ip, mac, n, ia, ifc, 0) < 0){ runlock(ifc); error("destination unreachable"); } @@ -450,7 +450,7 @@ arpread(Arp *arp, char *s, ulong offset, int len) qlock(arp); state = arpstate[a->state]; ipmove(ip, a->ip); - if(ifc->m == nil || a->ifcid != ifc->ifcid || !ipv6local(ifc, ia, ip)){ + if(ifc->m == nil || a->ifcid != ifc->ifcid || !ipv6local(ifc, ia, 0, ip)){ qunlock(arp); runlock(ifc); continue; @@ -507,7 +507,7 @@ ndpsendsol(Fs *f, Ipifc *ifc, Arpent *a) } else { arprelease(f->arp, a); } - if(!ipv6local(ifc, src, targ)) + if(!ipv6local(ifc, src, 0, targ)) return; send: if(!waserror()){ diff --git a/sys/src/9/ip/ethermedium.c b/sys/src/9/ip/ethermedium.c index 41928e89f..0f0975fc2 100644 --- a/sys/src/9/ip/ethermedium.c +++ b/sys/src/9/ip/ethermedium.c @@ -462,7 +462,7 @@ sendarp(Ipifc *ifc, Arpent *a) memmove(targ, a->ip+IPv4off, IPv4addrlen); arprelease(er->f->arp, a); - if(!ipv4local(ifc, src, targ)) + if(!ipv4local(ifc, src, 0, targ)) return; n = sizeof(Etherarp); diff --git a/sys/src/9/ip/icmp.c b/sys/src/9/ip/icmp.c index 5a2a810e8..93f0cd173 100644 --- a/sys/src/9/ip/icmp.c +++ b/sys/src/9/ip/icmp.c @@ -218,7 +218,7 @@ icmpttlexceeded(Fs *f, Ipifc *ifc, Block *bp) uchar ia[IPv4addrlen]; p = (Icmp *)bp->rp; - if(!ip4reply(f, p->src) || !ipv4local(ifc, ia, p->src)) + if(!ip4reply(f, p->src) || !ipv4local(ifc, ia, 0, p->src)) return; netlog(f, Logicmp, "sending icmpttlexceeded %V -> src %V dst %V\n", diff --git a/sys/src/9/ip/icmp6.c b/sys/src/9/ip/icmp6.c index 4f483a79d..a4b4b50c8 100644 --- a/sys/src/9/ip/icmp6.c +++ b/sys/src/9/ip/icmp6.c @@ -332,7 +332,7 @@ mkechoreply6(Block *bp, Ipifc *ifc) ipmove(addr, p->src); if(!isv6mcast(p->dst)) ipmove(p->src, p->dst); - else if (!ipv6local(ifc, p->src, addr)) + else if (!ipv6local(ifc, p->src, 0, addr)) return nil; ipmove(p->dst, addr); p->type = EchoReplyV6; @@ -434,7 +434,7 @@ icmphostunr6(Fs *f, Ipifc *ifc, Block *bp, int code, int tome) uchar ia[IPaddrlen]; p = (Ip6hdr *)bp->rp; - if(isv6mcast(p->dst) || isv6mcast(p->src) || !ipv6local(ifc, ia, p->src)) + if(isv6mcast(p->dst) || isv6mcast(p->src) || !ipv6local(ifc, ia, 0, p->src)) return; netlog(f, Logicmp, "send icmphostunr %I -> src %I dst %I\n", @@ -471,7 +471,7 @@ icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp) uchar ia[IPaddrlen]; p = (Ip6hdr *)bp->rp; - if(isv6mcast(p->dst) || isv6mcast(p->src) || !ipv6local(ifc, ia, p->src)) + if(isv6mcast(p->dst) || isv6mcast(p->src) || !ipv6local(ifc, ia, 0, p->src)) return; netlog(f, Logicmp, "send icmpttlexceeded6 %I -> src %I dst %I\n", @@ -504,7 +504,7 @@ icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp) uchar ia[IPaddrlen]; p = (Ip6hdr *)bp->rp; - if(isv6mcast(p->dst) || isv6mcast(p->src) || !ipv6local(ifc, ia, p->src)) + if(isv6mcast(p->dst) || isv6mcast(p->src) || !ipv6local(ifc, ia, 0, p->src)) return; netlog(f, Logicmp, "send icmppkttoobig6 %I -> src %I dst %I\n", @@ -769,7 +769,7 @@ icmpiput6(Proto *icmp, Ipifc *ifc, Block *bp) /* fall through */ case Tuniproxy: - if(ipv6local(ifc, ia, np->src)) { + if(ipv6local(ifc, ia, 0, np->src)) { if(arpenter(icmp->f, V6, np->src, np->lnaddr, 8*np->olen-2, ia, ifc, 0) < 0) break; pktflags |= Sflag; @@ -801,7 +801,7 @@ icmpiput6(Proto *icmp, Ipifc *ifc, Block *bp) lifc = iplocalonifc(ifc, np->target); if(lifc != nil && lifc->tentative) arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2, np->target, ifc, 0); - else if(ipv6local(ifc, ia, np->target)) + else if(ipv6local(ifc, ia, 0, np->target)) arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2, ia, ifc, 1); freeblist(bp); break; diff --git a/sys/src/9/ip/ip.h b/sys/src/9/ip/ip.h index 676c09fc4..9a444466b 100644 --- a/sys/src/9/ip/ip.h +++ b/sys/src/9/ip/ip.h @@ -567,6 +567,8 @@ extern void addroute(Fs *f, uchar *a, uchar *mask, uchar *s, uchar *smask, uchar extern void remroute(Fs *f, uchar *a, uchar *mask, uchar *s, uchar *smask, uchar *gate, int type, Ipifc *ifc, char *tag); extern Route* v4lookup(Fs *f, uchar *a, uchar *s, Routehint *h); extern Route* v6lookup(Fs *f, uchar *a, uchar *s, Routehint *h); +extern Route* v4source(Fs *f, uchar *a, uchar *s); +extern Route* v6source(Fs *f, uchar *a, uchar *s); extern long routeread(Fs *f, char*, ulong, int); extern long routewrite(Fs *f, Chan*, char*, int); extern void routetype(int type, char p[8]); @@ -664,8 +666,8 @@ extern int ipismulticast(uchar *ip); extern Ipifc* findipifc(Fs*, uchar *local, uchar *remote, int type); extern Ipifc* findipifcstr(Fs *f, char *s); extern void findlocalip(Fs*, uchar *local, uchar *remote); -extern int ipv4local(Ipifc *ifc, uchar *local, uchar *remote); -extern int ipv6local(Ipifc *ifc, uchar *local, uchar *remote); +extern int ipv4local(Ipifc *ifc, uchar *local, int prefixlen, uchar *remote); +extern int ipv6local(Ipifc *ifc, uchar *local, int prefixlen, uchar *remote); extern Iplifc* iplocalonifc(Ipifc *ifc, uchar *ip); extern Iplifc* ipremoteonifc(Ipifc *ifc, uchar *ip); extern int ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip); diff --git a/sys/src/9/ip/ipifc.c b/sys/src/9/ip/ipifc.c index 45187583b..3a4380877 100644 --- a/sys/src/9/ip/ipifc.c +++ b/sys/src/9/ip/ipifc.c @@ -1263,10 +1263,17 @@ findprimaryipv4(Fs *f, uchar *local) } /* - * return v4 address associated with an interface close to remote + * ipv4local, ipv6local: + * return a local address associated with an interface close to remote. + * prefixlen is the number of leading bits in the local address that + * have to match an interface address to be considered. this is used + * by source specific routes to filter on the source address. + * return non-zero on success or zero when no address was found. + * + * for ipv4local, all addresses are 4 byte format. */ int -ipv4local(Ipifc *ifc, uchar *local, uchar *remote) +ipv4local(Ipifc *ifc, uchar *local, int prefixlen, uchar *remote) { Iplifc *lifc; int a, b; @@ -1275,6 +1282,10 @@ ipv4local(Ipifc *ifc, uchar *local, uchar *remote) for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){ if((lifc->type & Rv4) == 0 || ipcmp(lifc->local, IPnoaddr) == 0) continue; + + if(prefixlen && comprefixlen(lifc->local+IPv4off, local, IPv4addrlen) < prefixlen) + continue; + a = comprefixlen(lifc->local+IPv4off, remote, IPv4addrlen); if(a > b){ b = a; @@ -1284,11 +1295,8 @@ ipv4local(Ipifc *ifc, uchar *local, uchar *remote) return b >= 0; } -/* - * return v6 address associated with an interface close to remote - */ int -ipv6local(Ipifc *ifc, uchar *local, uchar *remote) +ipv6local(Ipifc *ifc, uchar *local, int prefixlen, uchar *remote) { struct { int atype; @@ -1300,12 +1308,13 @@ ipv6local(Ipifc *ifc, uchar *local, uchar *remote) Iplifc *lifc; if(isv4(remote)){ - ipmove(local, v4prefix); - return ipv4local(ifc, local+IPv4off, remote+IPv4off); + memmove(local, v4prefix, IPv4off); + if((prefixlen -= IPv4off*8) < 0) + prefixlen = 0; + return ipv4local(ifc, local+IPv4off, prefixlen, remote+IPv4off); } atype = v6addrtype(remote); - ipmove(local, v6Unspecified); b.atype = unknownv6; b.deprecated = 1; b.comprefixlen = 0; @@ -1315,6 +1324,9 @@ ipv6local(Ipifc *ifc, uchar *local, uchar *remote) if(lifc->tentative) continue; + if(prefixlen && comprefixlen(lifc->local, local, IPaddrlen) < prefixlen) + continue; + a.atype = v6addrtype(lifc->local); a.deprecated = lifc->preflt != ~0UL && lifc->preflt < now-lifc->origint; a.comprefixlen = comprefixlen(lifc->local, remote, IPaddrlen); @@ -1347,54 +1359,22 @@ ipv6local(Ipifc *ifc, uchar *local, uchar *remote) return b.atype >= atype; } +/* + * find the local address for a remote destination + */ void findlocalip(Fs *f, uchar *local, uchar *remote) { - Route *r; - Iplifc *lifc; - Ipifc *ifc, *nifc; - Conv **cp; - - for(cp = f->ipifc->conv; *cp != nil; cp++){ - ifc = (Ipifc*)(*cp)->ptcl; - rlock(ifc); - for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){ - if(lifc->tentative) - continue; - - r = v6lookup(f, remote, lifc->local, nil); - if(r == nil || (nifc = r->ifc) == nil) - continue; - if(r->type & Runi){ - ipmove(local, remote); - runlock(ifc); - return; - } - if(nifc != ifc) rlock(nifc); - if((r->type & (Rifc|Rbcast|Rmulti|Rv4)) == Rv4){ - ipmove(local, v4prefix); - if(ipv4local(nifc, local+IPv4off, r->v4.gate)){ - if(nifc != ifc) runlock(nifc); - runlock(ifc); - return; - } - } - if(ipv6local(nifc, local, remote)){ - if(nifc != ifc) runlock(nifc); - runlock(ifc); - return; - } - if(nifc != ifc) runlock(nifc); - } - runlock(ifc); + if(isv4(remote)) { + memmove(local, v4prefix, IPv4off); + if(v4source(f, remote+IPv4off, local+IPv4off) == nil) + findprimaryipv4(f, local); + } else { + if(v6source(f, remote, local) == nil) + findprimaryipv6(f, local); } - if(isv4(remote)) - findprimaryipv4(f, local); - else - findprimaryipv6(f, local); } - /* * see if this address is bound to the interface */ diff --git a/sys/src/9/ip/iproute.c b/sys/src/9/ip/iproute.c index 65ea63ff2..e90f61d44 100644 --- a/sys/src/9/ip/iproute.c +++ b/sys/src/9/ip/iproute.c @@ -535,10 +535,64 @@ remroute(Fs *f, uchar *a, uchar *mask, uchar *s, uchar *smask, uchar *gate, int wunlock(&routelock); } +/* get the outgoing interface for route r */ +static Ipifc* +routefindipifc(Route *r, Fs *f) +{ + uchar local[IPaddrlen], gate[IPaddrlen]; + Ipifc *ifc; + int i; + + ifc = r->ifc; + if(ifc != nil && ifc->ifcid == r->ifcid) + return ifc; + + if(r->type & Rsrc) { + if(r->type & Rv4) { + hnputl(local+IPv4off, r->v4.source); + memmove(local, v4prefix, IPv4off); + } else { + for(i = 0; i < IPllen; i++) + hnputl(local+4*i, r->v6.source[i]); + } + } else { + ipmove(local, IPnoaddr); + } + + if(r->type & Rifc) { + if(r->type & Rv4) { + hnputl(gate+IPv4off, r->v4.address); + memmove(gate, v4prefix, IPv4off); + } else { + for(i = 0; i < IPllen; i++) + hnputl(gate+4*i, r->v6.address[i]); + } + } else { + if(r->type & Rv4) + v4tov6(gate, r->v4.gate); + else + ipmove(gate, r->v6.gate); + } + + if((ifc = findipifc(f, local, gate, r->type)) == nil) + return nil; + + r->ifc = ifc; + r->ifcid = ifc->ifcid; + return ifc; +} + +/* + * v4lookup, v6lookup: + * lookup a route to destination address a from source address s + * and return the route. returns nil if no route was found. + * an optional Routehint can be passed in rh to cache the lookup. + * + * for v4lookup, addresses are in 4 byte format. + */ Route* v4lookup(Fs *f, uchar *a, uchar *s, Routehint *rh) { - uchar local[IPaddrlen], gate[IPaddrlen]; ulong la, ls; Route *p, *q; Ipifc *ifc; @@ -577,23 +631,9 @@ v4lookup(Fs *f, uchar *a, uchar *s, Routehint *rh) p = p->mid; } - if(q == nil || q->ref == 0) + if(q == nil || q->ref == 0 || routefindipifc(q, f) == nil) return nil; - if(q->ifc == nil || q->ifcid != q->ifc->ifcid){ - if(q->type & Rifc) { - hnputl(gate+IPv4off, q->v4.address); - memmove(gate, v4prefix, IPv4off); - } else - v4tov6(gate, q->v4.gate); - v4tov6(local, s); - ifc = findipifc(f, local, gate, q->type); - if(ifc == nil) - return nil; - q->ifc = ifc; - q->ifcid = ifc->ifcid; - } - if(rh != nil){ rh->r = q; rh->rgen = v4routegeneration; @@ -605,7 +645,6 @@ v4lookup(Fs *f, uchar *a, uchar *s, Routehint *rh) Route* v6lookup(Fs *f, uchar *a, uchar *s, Routehint *rh) { - uchar gate[IPaddrlen]; ulong la[IPllen], ls[IPllen]; ulong x, y; Route *p, *q; @@ -684,22 +723,9 @@ v6lookup(Fs *f, uchar *a, uchar *s, Routehint *rh) next: ; } - if(q == nil || q->ref == 0) + if(q == nil || q->ref == 0 || routefindipifc(q, f) == nil) return nil; - if(q->ifc == nil || q->ifcid != q->ifc->ifcid){ - if(q->type & Rifc) { - for(h = 0; h < IPllen; h++) - hnputl(gate+4*h, q->v6.address[h]); - ifc = findipifc(f, s, gate, q->type); - } else - ifc = findipifc(f, s, q->v6.gate, q->type); - if(ifc == nil) - return nil; - q->ifc = ifc; - q->ifcid = ifc->ifcid; - } - if(rh != nil){ rh->r = q; rh->rgen = v6routegeneration; @@ -708,6 +734,119 @@ next: ; return q; } +/* + * v4source, v6source: + * lookup a route to destination address a and also find + * a suitable source address s on the outgoing interface. + * return the route on success or nil when no route + * was found. + * + * for v4source, addresses are in 4 byte format. + */ +Route* +v4source(Fs *f, uchar *a, uchar *s) +{ + uchar src[IPv4addrlen]; + int splen; + ulong x, la; + Route *p, *q; + Ipifc *ifc; + + q = nil; + la = nhgetl(a); + rlock(&routelock); + for(p = f->v4root[V4H(la)]; p != nil;){ + if(la < p->v4.address){ + p = p->left; + continue; + } + if(la > p->v4.endaddress){ + p = p->right; + continue; + } + splen = 0; + if(p->type & Rsrc){ + /* calculate local prefix length for source specific routes */ + for(x = ~(p->v4.endsource ^ p->v4.source); x & 0x80000000UL; x <<= 1) + splen++; + hnputl(src, p->v4.source); + } + if((ifc = routefindipifc(p, f)) == nil + || !ipv4local(ifc, src, splen, (p->type & (Rifc|Rbcast|Rmulti|Rv4))==Rv4? p->v4.gate: a)){ + p = p->mid; + continue; + } + memmove(s, src, IPv4addrlen); + q = p; + p = p->mid; + } + runlock(&routelock); + return q; +} + +Route* +v6source(Fs *f, uchar *a, uchar *s) +{ + uchar src[IPaddrlen]; + int splen, h; + ulong x, y, la[IPllen]; + Route *p, *q; + Ipifc *ifc; + + q = nil; + for(h = 0; h < IPllen; h++) + la[h] = nhgetl(a+4*h); + rlock(&routelock); + for(p = f->v6root[V6H(la)]; p != nil;){ + for(h = 0; h < IPllen; h++){ + x = la[h]; + y = p->v6.address[h]; + if(x == y) + continue; + if(x < y){ + p = p->left; + goto next; + } + break; + } + for(h = 0; h < IPllen; h++){ + x = la[h]; + y = p->v6.endaddress[h]; + if(x == y) + continue; + if(x > y){ + p = p->right; + goto next; + } + break; + } + splen = 0; + if(p->type & Rsrc){ + /* calculate local prefix length for source specific routes */ + for(h = 0; h < IPllen; h++){ + hnputl(src+4*h, p->v6.source[h]); + if((x = ~(p->v6.endsource[h] ^ p->v6.source[h])) != ~0UL){ + for(; x & 0x80000000UL; x <<= 1) + splen++; + break; + } + splen += 32; + } + } + if((ifc = routefindipifc(p, f)) == nil + || !ipv6local(ifc, src, splen, a)){ + p = p->mid; + continue; + } + ipmove(s, src); + q = p; + p = p->mid; +next: ; + } + runlock(&routelock); + return q; +} + static int parseroutetype(char *p) { diff --git a/sys/src/9/ip/rudp.c b/sys/src/9/ip/rudp.c index 3631b57d5..9b6c96438 100644 --- a/sys/src/9/ip/rudp.c +++ b/sys/src/9/ip/rudp.c @@ -559,15 +559,12 @@ rudpiput(Proto *rudp, Ipifc *ifc, Block *bp) default: /* connection oriented rudp */ if(ipcmp(c->raddr, IPnoaddr) == 0){ - /* save the src address in the conversation */ - ipmove(c->raddr, raddr); - c->rport = rport; - /* reply with the same ip address (if not broadcast) */ if(ipforme(f, laddr) != Runi) - ipv6local(ifc, c->laddr, c->raddr); - else - ipmove(c->laddr, laddr); + ipv6local(ifc, laddr, 0, raddr); + ipmove(c->laddr, laddr); + ipmove(c->raddr, raddr); + c->rport = rport; } break; } diff --git a/sys/src/9/ip/udp.c b/sys/src/9/ip/udp.c index 6a2e03dcd..86205caec 100644 --- a/sys/src/9/ip/udp.c +++ b/sys/src/9/ip/udp.c @@ -423,7 +423,7 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp) if(ucb->headers == 0){ /* create a new conversation */ if(ipforme(f, laddr) != Runi) - ipv6local(ifc, laddr, raddr); + ipv6local(ifc, laddr, 0, raddr); c = Fsnewcall(c, raddr, rport, laddr, lport, version); if(c == nil){ qunlock(udp);