From 9afd87b7334d8ea9cd7ca35b9f838a21ef093eaa Mon Sep 17 00:00:00 2001
From: Stefano Brivio <sbrivio@redhat.com>
Date: Sun, 20 Feb 2022 03:47:19 +0100
Subject: udp: Allow loopback connections from host using configured unicast
 address

Likely for testing purposes only: allow connections from host to
guest or namespace using, as connection target, the configured,
possibly global unicast address.

In this case, we have to map the destination address to a link-local
address, and for port-based tracked responses, the source address
needs to be again the unicast address: not loopback, not link-local.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
---
 udp.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/udp.c b/udp.c
index 2fc52d3..8129a89 100644
--- a/udp.c
+++ b/udp.c
@@ -125,13 +125,15 @@
  * @sock:	Socket bound to source port used as index
  * @ts:		Activity timestamp from tap, used for socket aging
  * @ts_local:	Timestamp of tap packet to gateway address, aging for local bind
- * @loopback:	Whether local bind should use loopback address as source
+ * @loopback:	Whether local bind maps to loopback address as source
+ * @gua:	Whether local bind maps to configured unicast address as source
  */
 struct udp_tap_port {
 	int sock;
 	time_t ts;
 	time_t ts_local;
 	int loopback;
+	int gua;
 };
 
 /**
@@ -701,10 +703,13 @@ void udp_sock_handler(struct ctx *c, union epoll_ref ref, uint32_t events,
 				b->ip6h.saddr = b->s_in6.sin6_addr;
 			} else if (IN6_IS_ADDR_LOOPBACK(&b->s_in6.sin6_addr) ||
 				   !memcmp(&b->s_in6.sin6_addr, &c->addr6_seen,
+					   sizeof(c->addr6)) ||
+				   !memcmp(&b->s_in6.sin6_addr, &c->addr6,
 					   sizeof(c->addr6))) {
 				in_port_t src = htons(b->s_in6.sin6_port);
 
 				b->ip6h.daddr = c->addr6_ll_seen;
+
 				if (IN6_IS_ADDR_LINKLOCAL(&c->gw6))
 					b->ip6h.saddr = c->gw6;
 				else
@@ -717,6 +722,12 @@ void udp_sock_handler(struct ctx *c, union epoll_ref ref, uint32_t events,
 				else
 					udp_tap_map[V6][src].loopback = 0;
 
+				if (!memcmp(&b->s_in6.sin6_addr, &c->addr6,
+						 sizeof(c->addr6)))
+					udp_tap_map[V6][src].gua = 1;
+				else
+					udp_tap_map[V6][src].gua = 0;
+
 				bitmap_set(udp_act[V6][UDP_ACT_TAP], src);
 			} else if (!IN6_IS_ADDR_UNSPECIFIED(&c->dns6_fwd) &&
 				   !memcmp(&b->s_in6.sin6_addr, &c->dns6_fwd,
@@ -987,6 +998,8 @@ int udp_tap_handler(struct ctx *c, int af, void *addr,
 			if (!udp_tap_map[V6][dst].ts_local ||
 			    udp_tap_map[V6][dst].loopback)
 				s_in6.sin6_addr = in6addr_loopback;
+			else if (udp_tap_map[V6][dst].gua)
+				s_in6.sin6_addr = c->addr6;
 			else
 				s_in6.sin6_addr = c->addr6_seen;
 		} else if (!memcmp(addr, &c->dns6_fwd, sizeof(c->dns6_fwd)) &&
@@ -1212,8 +1225,11 @@ static void udp_timer_one(struct ctx *c, int v6, enum udp_act_type type,
 		if (ts->tv_sec - tp->ts > UDP_CONN_TIMEOUT)
 			s = tp->sock;
 
-		if (ts->tv_sec - tp->ts_local > UDP_CONN_TIMEOUT)
+		if (ts->tv_sec - tp->ts_local > UDP_CONN_TIMEOUT) {
 			tp->ts_local = 0;
+			tp->loopback = 0;
+			tp->gua = 0;
+		}
 
 		break;
 	case UDP_ACT_INIT_CONN:
-- 
cgit v1.2.3