summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/src/9/ip/arp.c6
-rw-r--r--sys/src/9/ip/ethermedium.c2
-rw-r--r--sys/src/9/ip/icmp.c2
-rw-r--r--sys/src/9/ip/icmp6.c12
-rw-r--r--sys/src/9/ip/ip.h6
-rw-r--r--sys/src/9/ip/ipifc.c82
-rw-r--r--sys/src/9/ip/iproute.c201
-rw-r--r--sys/src/9/ip/rudp.c11
-rw-r--r--sys/src/9/ip/udp.c2
9 files changed, 221 insertions, 103 deletions
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);