public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: David Gibson <david@gibson.dropbear.id.au>
To: Stefano Brivio <sbrivio@redhat.com>, passt-dev@passt.top
Cc: David Gibson <david@gibson.dropbear.id.au>
Subject: [PATCH 3/3] udp,pasta: Periodically scan for ports to automatically forward
Date: Wed, 15 Nov 2023 16:25:34 +1100	[thread overview]
Message-ID: <20231115052534.1826261-4-david@gibson.dropbear.id.au> (raw)
In-Reply-To: <20231115052534.1826261-1-david@gibson.dropbear.id.au>

pasta supports automatic port forwarding, where we look for listening
sockets in /proc/net (in both namespace and outside) and establish port
forwarding to match.

For TCP we do this scan both at initial startup, then periodically
thereafter.  For UDP however, we currently only scan at start.  So unlike
TCP we won't update forwarding to handle services that start after pasta
has begun.

There's no particular reason for that, other than that we didn't implement
it.  So, remove that difference, by scanning for new UDP forwards
periodically too.  The logic is basically identical to that for TCP, but it
needs some changes to handle the mildly different data structures in the
UDP case.

Link: https://bugs.passt.top/show_bug.cgi?id=45

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 passt.1 |  3 +--
 udp.c   | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 75 insertions(+), 2 deletions(-)

diff --git a/passt.1 b/passt.1
index 1ad4276..efd6bb7 100644
--- a/passt.1
+++ b/passt.1
@@ -496,8 +496,7 @@ Default is \fBauto\fR.
 .BR \-u ", " \-\-udp-ports " " \fIspec
 Configure UDP port forwarding to namespace. \fIspec\fR is as described for TCP
 above, and the list of ports is derived from listening sockets reported by
-\fI/proc/net/udp\fR and \fI/proc/net/udp6\fR, see \fBproc\fR(5),
-when \fBpasta\fR starts (not periodically).
+\fI/proc/net/udp\fR and \fI/proc/net/udp6\fR, see \fBproc\fR(5).
 
 Note: unless overridden, UDP ports with numbers corresponding to forwarded TCP
 port numbers are forwarded too, without, however, any port translation. 
diff --git a/udp.c b/udp.c
index a13efd2..cc1ea9c 100644
--- a/udp.c
+++ b/udp.c
@@ -1186,6 +1186,66 @@ static void udp_timer_one(struct ctx *c, int v6, enum udp_act_type type,
 	}
 }
 
+/**
+ * udp_port_rebind() - Rebind ports to match forward maps
+ * @c:		Execution context
+ * @outbound:	True to remap outbound forwards, otherwise inbound
+ *
+ * Must be called in namespace context if @outbound is true.
+ */
+static void udp_port_rebind(struct ctx *c, bool outbound)
+{
+	const uint8_t *fmap
+		= outbound ? c->udp.fwd_out.f.map : c->udp.fwd_in.f.map;
+	const uint8_t *rmap
+		= outbound ? c->udp.fwd_in.f.map : c->udp.fwd_out.f.map;
+	struct udp_splice_port (*socks)[NUM_PORTS]
+		= outbound ? udp_splice_ns : udp_splice_init;
+	unsigned port;
+
+	for (port = 0; port < NUM_PORTS; port++) {
+		if (!bitmap_isset(fmap, port)) {
+			if (socks[V4][port].sock >= 0) {
+				close(socks[V4][port].sock);
+				socks[V4][port].sock = -1;
+			}
+
+			if (socks[V6][port].sock >= 0) {
+				close(socks[V6][port].sock);
+				socks[V6][port].sock = -1;
+			}
+
+			continue;
+		}
+
+		/* Don't loop back our own ports */
+		if (bitmap_isset(rmap, port))
+			continue;
+
+		if ((c->ifi4 && socks[V4][port].sock == -1) ||
+		    (c->ifi6 && socks[V6][port].sock == -1))
+			udp_sock_init(c, outbound, AF_UNSPEC, NULL, NULL, port);
+	}
+}
+
+/**
+ * udp_port_rebind_outbound() - Rebind ports in namespace
+ * @arg:	Execution context
+ *
+ * Called with NS_CALL()
+ *
+ * Return: 0
+ */
+static int udp_port_rebind_outbound(void *arg)
+{
+	struct ctx *c = (struct ctx *)arg;
+
+	ns_enter(c);
+	udp_port_rebind(c, true);
+
+	return 0;
+}
+
 /**
  * udp_timer() - Scan activity bitmaps for ports with associated timed events
  * @c:		Execution context
@@ -1197,6 +1257,20 @@ void udp_timer(struct ctx *c, const struct timespec *ts)
 	unsigned int i;
 	long *word, tmp;
 
+	if (c->mode == MODE_PASTA) {
+		if (c->udp.fwd_out.f.mode == FWD_AUTO) {
+			port_fwd_scan_udp(&c->udp.fwd_out.f, &c->udp.fwd_in.f,
+					  &c->tcp.fwd_out);
+			NS_CALL(udp_port_rebind_outbound, c);
+		}
+
+		if (c->udp.fwd_in.f.mode == FWD_AUTO) {
+			port_fwd_scan_udp(&c->udp.fwd_in.f, &c->udp.fwd_out.f,
+					  &c->tcp.fwd_in);
+			udp_port_rebind(c, false);
+		}
+	}
+
 	if (!c->ifi4)
 		v6 = 1;
 v6:
-- 
@@ -1186,6 +1186,66 @@ static void udp_timer_one(struct ctx *c, int v6, enum udp_act_type type,
 	}
 }
 
+/**
+ * udp_port_rebind() - Rebind ports to match forward maps
+ * @c:		Execution context
+ * @outbound:	True to remap outbound forwards, otherwise inbound
+ *
+ * Must be called in namespace context if @outbound is true.
+ */
+static void udp_port_rebind(struct ctx *c, bool outbound)
+{
+	const uint8_t *fmap
+		= outbound ? c->udp.fwd_out.f.map : c->udp.fwd_in.f.map;
+	const uint8_t *rmap
+		= outbound ? c->udp.fwd_in.f.map : c->udp.fwd_out.f.map;
+	struct udp_splice_port (*socks)[NUM_PORTS]
+		= outbound ? udp_splice_ns : udp_splice_init;
+	unsigned port;
+
+	for (port = 0; port < NUM_PORTS; port++) {
+		if (!bitmap_isset(fmap, port)) {
+			if (socks[V4][port].sock >= 0) {
+				close(socks[V4][port].sock);
+				socks[V4][port].sock = -1;
+			}
+
+			if (socks[V6][port].sock >= 0) {
+				close(socks[V6][port].sock);
+				socks[V6][port].sock = -1;
+			}
+
+			continue;
+		}
+
+		/* Don't loop back our own ports */
+		if (bitmap_isset(rmap, port))
+			continue;
+
+		if ((c->ifi4 && socks[V4][port].sock == -1) ||
+		    (c->ifi6 && socks[V6][port].sock == -1))
+			udp_sock_init(c, outbound, AF_UNSPEC, NULL, NULL, port);
+	}
+}
+
+/**
+ * udp_port_rebind_outbound() - Rebind ports in namespace
+ * @arg:	Execution context
+ *
+ * Called with NS_CALL()
+ *
+ * Return: 0
+ */
+static int udp_port_rebind_outbound(void *arg)
+{
+	struct ctx *c = (struct ctx *)arg;
+
+	ns_enter(c);
+	udp_port_rebind(c, true);
+
+	return 0;
+}
+
 /**
  * udp_timer() - Scan activity bitmaps for ports with associated timed events
  * @c:		Execution context
@@ -1197,6 +1257,20 @@ void udp_timer(struct ctx *c, const struct timespec *ts)
 	unsigned int i;
 	long *word, tmp;
 
+	if (c->mode == MODE_PASTA) {
+		if (c->udp.fwd_out.f.mode == FWD_AUTO) {
+			port_fwd_scan_udp(&c->udp.fwd_out.f, &c->udp.fwd_in.f,
+					  &c->tcp.fwd_out);
+			NS_CALL(udp_port_rebind_outbound, c);
+		}
+
+		if (c->udp.fwd_in.f.mode == FWD_AUTO) {
+			port_fwd_scan_udp(&c->udp.fwd_in.f, &c->udp.fwd_out.f,
+					  &c->tcp.fwd_in);
+			udp_port_rebind(c, false);
+		}
+	}
+
 	if (!c->ifi4)
 		v6 = 1;
 v6:
-- 
2.41.0


  parent reply	other threads:[~2023-11-15  5:25 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-11-15  5:25 [PATCH 0/3] pasta: Implement runtime auto-forwarding for UDP David Gibson
2023-11-15  5:25 ` [PATCH 1/3] tcp: Use common helper for rebinding inbound and outbound ports David Gibson
2023-11-15  5:25 ` [PATCH 2/3] tcp: Simplify away tcp_port_rebind() David Gibson
2023-11-15  5:25 ` David Gibson [this message]
2023-11-19 20:10 ` [PATCH 0/3] pasta: Implement runtime auto-forwarding for UDP Stefano Brivio

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231115052534.1826261-4-david@gibson.dropbear.id.au \
    --to=david@gibson.dropbear.id.au \
    --cc=passt-dev@passt.top \
    --cc=sbrivio@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).