code: plan9front

Download patch

ref: a4fa14a9d8f604d3e1eb409091b4f0d879f9cf97
parent: b76d35401c0a056911aa192d4e4e6d86a15efa50
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Apr 15 14:00:34 EDT 2023

ip: generalize Rproxy route handling, allowing non point-to-point arp proxy

Generalize the arp proxy code, so one can create
specific routes for ip ranges that we want to arp
proxy for, not just for point-to-point interface
routes.

as we have source specific routes, this also
gives some control over which requester we will
reply to.

one example for this is a vps where we booked
another ip address on the ethernet, that we want
to route into a vpn tunnel. the new ip is in
subnet of the public ethernet interface, so all
we now need todo is add a route for that ip into
the vpn tunnel and set the proxy flag "y", and
we will respond to arp for that ip on the public
ethernet interface.

--- a/sys/src/9/ip/arp.c
+++ b/sys/src/9/ip/arp.c
@@ -383,7 +383,43 @@
 	return 1;
 }
 
+/*
+ * arpforme() checks if we should respond to arp/ndp on a specific interface.
+ * 
+ * returns Runi if targ is a non-tentative local address on ifc.
+ * returns Rproxy if we have a proxy route for targ to another interface.
+ */
 int
+arpforme(Fs *fs, int version, uchar *targ, uchar *src, Ipifc *ifc)
+{
+	uchar ipv6[IPaddrlen];
+	Iplifc *lifc;
+	Route *r;
+
+	if(version == V4) {
+		v4tov6(ipv6, targ);
+		targ = ipv6;
+	}
+	lifc = iplocalonifc(ifc, targ);
+	if(lifc != nil){
+		if(lifc->tentative)
+			return 0;
+		return Runi;
+	}
+	if(ipremoteonifc(ifc, targ) == nil)
+		return 0;
+	if(version == V4){
+		targ += IPv4off;
+		r = v4lookup(fs, targ, src, nil);
+	} else {
+		r = v6lookup(fs, targ, src, nil);
+	}
+	if(r == nil || r->ifc == ifc && r->ifcid == ifc->ifcid)
+		return 0;
+	return r->type & Rproxy;
+}
+
+int
 arpwrite(Fs *fs, char *s, int len)
 {
 	int n;
@@ -552,8 +588,7 @@
 	if(a->last != nil){
 		ipmove(src, ((Ip6hdr*)a->last->rp)->src);
 		arpcontinue(f->arp, a);
-
-		if(iplocalonifc(ifc, src) != nil || ipproxyifc(f, ifc, src))
+		if(arpforme(f, V6, src, targ, ifc))
 			goto send;
 	} else {
 		arpcontinue(f->arp, a);
--- a/sys/src/9/ip/ethermedium.c
+++ b/sys/src/9/ip/ethermedium.c
@@ -508,7 +508,6 @@
 	int forme;
 	Block *ebp, *rbp;
 	Etherarp *e, *r;
-	uchar ip[IPaddrlen];
 	static uchar eprinted[4];
 	Etherrock *er = ifc->arg;
 
@@ -529,8 +528,7 @@
 			break;
 
 		/* check for machine using my ip address */
-		v4tov6(ip, e->spa);
-		if(iplocalonifc(ifc, ip) != nil || ipproxyifc(er->f, ifc, ip)){
+		if(arpforme(er->f, V4, e->spa, e->tpa, ifc)){
 			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){
 				print("arprep: 0x%E/0x%E also has ip addr %V\n",
 					e->s, e->sha, e->spa);
@@ -552,8 +550,7 @@
 			break;
 
 		/* check for machine using my ip or ether address */
-		v4tov6(ip, e->spa);
-		if(iplocalonifc(ifc, ip) != nil || ipproxyifc(er->f, ifc, ip)){
+		if(arpforme(er->f, V4, e->spa, e->tpa, ifc)){
 			if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){
 				if(memcmp(eprinted, e->spa, sizeof(e->spa)) != 0){
 					/* print only once */
@@ -576,8 +573,7 @@
 		 * enter senders address into arp table and reply, otherwise just
 		 * refresh the senders address.
 		 */
-		v4tov6(ip, e->tpa);
-		forme = iplocalonifc(ifc, ip) != nil || ipproxyifc(er->f, ifc, ip);
+		forme = arpforme(er->f, V4, e->tpa, e->spa, ifc);
 		if(arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), e->tpa, ifc, !forme) < 0 || !forme)
 			break;
 
--- a/sys/src/9/ip/icmp6.c
+++ b/sys/src/9/ip/icmp6.c
@@ -636,21 +636,6 @@
 	return 0;
 }
 
-static int
-targettype(Fs *f, Ipifc *ifc, uchar *target)
-{
-	Iplifc *lifc;
-	int t;
-
-	if((lifc = iplocalonifc(ifc, target)) != nil)
-		t = lifc->tentative? Tunitent: Tunirany;
-	else if(ipproxyifc(f, ifc, target))
-		t = Tuniproxy;
-	else
-		t = 0;
-	return t;
-}
-
 static void
 icmpiput6(Proto *icmp, Ipifc *ifc, Block *bp)
 {
@@ -745,12 +730,11 @@
 		pktflags = 0;
 		if(ifc->sendra6)
 			pktflags |= Rflag;
-		switch (targettype(icmp->f, ifc, np->target)) {
-		case Tunirany:
+		switch(arpforme(icmp->f, V6, np->target, np->src, ifc)){
+		case Runi:
 			pktflags |= Oflag;
-			/* fall through */
-
-		case Tuniproxy:
+			/* wet floor */
+		case Rproxy:
 			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;
@@ -759,12 +743,6 @@
 				ipmove(ia, np->target);
 			icmpna6(icmp->f, ia, (pktflags & Sflag)? np->src: v6allnodesL,
 				np->target, ifc->mac, pktflags);
-			break;
-		case Tunitent:
-			/*
-			 * not clear what needs to be done. send up
-			 * an icmp mesg saying don't use this address?
-			 */
 			break;
 		}
 		freeblist(bp);
--- a/sys/src/9/ip/ip.h
+++ b/sys/src/9/ip/ip.h
@@ -668,6 +668,7 @@
 extern void	arpcontinue(Arp*, Arpent *a);
 extern Block*	arpresolve(Arp*, Arpent *a, uchar *mac, Routehint *rh);
 extern int	arpenter(Fs*, int version, uchar *ip, uchar *mac, int n, uchar *ia, Ipifc *ifc, int refresh);
+extern int	arpforme(Fs*, int version, uchar *targ, uchar *src, Ipifc *ifc);
 extern void	ndpsendsol(Fs*, Arpent*);
 
 /*
@@ -725,7 +726,6 @@
 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);
 extern Ipmulti*	ipifcgetmulti(Fs *f, Ipifc *ifc, uchar *ma);
 extern void	ipifcremmulti(Conv *c, uchar *ma, uchar *ia);
 extern void	ipifcaddmulti(Conv *c, uchar *ma, uchar *ia);
--- a/sys/src/9/ip/ipifc.c
+++ b/sys/src/9/ip/ipifc.c
@@ -569,7 +569,7 @@
 
 	addselfcache(f, ifc, lifc, ip, Runi);
 
-	/* register proxy */
+	/* register point-to-point proxy */
 	if(type & Rptpt){
 		if(type & Rproxy)
 			ipifcregisterproxy(f, ifc, rem, 1);
@@ -576,6 +576,10 @@
 		goto done;
 	}
 
+	/* register local ip if proxy */
+	if(type & Rproxy)
+		ipifcregisterproxy(f, ifc, ip, 1);
+
 	if(type & Rv4) {
 		/* add subnet directed broadcast address to the self cache */
 		for(i = 0; i < IPaddrlen; i++)
@@ -667,7 +671,7 @@
 				lifc->remote, lifc->type, ifc, tifc);
 	}
 
-	/* unregister proxy */
+	/* unregister point-to-point proxy */
 	if(lifc->type & Rptpt){
 		if(lifc->type & Rproxy)
 			ipifcregisterproxy(f, ifc, lifc->remote, 0);
@@ -674,6 +678,10 @@
 		goto done;
 	}
 
+	/* unregister local ip if proxy */
+	if(lifc->type & Rproxy)
+		ipifcregisterproxy(f, ifc, lifc->local, 0);
+
 	/* remove route for all nodes multicast */
 	if((lifc->type & Rv4) == 0){
 		if(ipcmp(lifc->local, v6loopback) == 0){
@@ -902,6 +910,7 @@
 	Ipself *p;
 	int h;
 
+	if(type == Runi) type |= (lifc->type & Rproxy);
 	type |= (lifc->type & Rv4);
 
 	qlock(f->self);
@@ -1448,22 +1457,6 @@
 			return lifc;
 
 	return nil;
-}
-
-/*
- *  See if we're proxying for this address on this interface
- */
-int
-ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip)
-{
-	Route *r;
-
-	/* see if this is a direct connected pt to pt address */
-	r = v6lookup(f, ip, ip, nil);
-	if(r == nil || (r->type & (Rifc|Rproxy)) != (Rifc|Rproxy))
-		return 0;
-
-	return ipremoteonifc(ifc, ip) != nil;
 }
 
 /*
--- a/sys/src/9/ip/ipv6.h
+++ b/sys/src/9/ip/ipv6.h
@@ -94,10 +94,6 @@
 	TARG_UNI	= 2,
 	TARG_MULTI	= 3,
 
-	Tunitent	= 1,
-	Tuniproxy	= 2,
-	Tunirany	= 3,
-
 	/* Node constants */
 	MAX_MULTICAST_SOLICIT	= 3,
 	RETRANS_TIMER		= 1000,