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 v3 24/25] pesto, conf: Parse, send and receive new rules
Date: Mon, 23 Mar 2026 18:37:31 +1100	[thread overview]
Message-ID: <20260323073732.3158468-25-david@gibson.dropbear.id.au> (raw)
In-Reply-To: <20260323073732.3158468-1-david@gibson.dropbear.id.au>

From: Stefano Brivio <sbrivio@redhat.com>

This adds parsing of options using conf_ports() in pesto, builds rules, and
sends them one by one to passt, which now receives them and stores then in
a different table.  We debug print the rules in the "pending" table but
don't yet attempt to activate them.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Message-ID: <20260322141843.4095972-3-sbrivio@redhat.com>
[dwg: Rebased on changes to my earlier patches]
[dwg: Properly preserve the rule flags when they're read in]
[dwg: Removed the non-working logic to activate the new table]
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 Makefile |  2 +-
 conf.c   | 60 ++++++++++++++++++++++++++++++++++++--
 fwd.c    |  9 +++++-
 passt.h  |  2 ++
 pesto.c  | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 pif.h    |  2 ++
 6 files changed, 157 insertions(+), 6 deletions(-)

diff --git a/Makefile b/Makefile
index b6b6a823..24235702 100644
--- a/Makefile
+++ b/Makefile
@@ -51,7 +51,7 @@ SRCS = $(PASST_SRCS) $(QRAP_SRCS) $(PASST_REPAIR_SRCS) $(PESTO_SRCS)
 MANPAGES = passt.1 pasta.1 pesto.1 qrap.1 passt-repair.1
 
 PESTO_HEADERS = common.h fwd_rule.h inany.h ip.h pesto.h serialise.h ports.h \
-	lineread.h
+	lineread.h pif.h
 PASST_HEADERS = arch.h arp.h checksum.h conf.h dhcp.h dhcpv6.h epoll_ctl.h \
 	flow.h fwd.h flow_table.h icmp.h icmp_flow.h iov.h isolation.h \
 	lineread.h log.h migrate.h ndp.h netlink.h packet.h passt.h pasta.h \
diff --git a/conf.c b/conf.c
index 7f624db6..b4c2074e 100644
--- a/conf.c
+++ b/conf.c
@@ -2046,6 +2046,50 @@ static int conf_send_rules(const struct ctx *c, int fd)
 	return 0;
 }
 
+/**
+ * conf_send_rules() - Send current forwarding rules to dynamic update client (pesto)
+ * @c:		Execution context
+ * @fd:		Socket to the client
+ *
+ * Return: 0 on success, -1 on failure
+ */
+static int conf_recv_rules(const struct ctx *c, int fd)
+{
+	while (1) {
+		struct fwd_table *fwd;
+		struct fwd_rule r;
+		uint32_t count;
+		uint8_t pif;
+		unsigned i;
+
+		if (read_u8(fd, &pif))
+			return -1;
+
+		if (pif == PIF_NONE)
+			break;
+
+		fwd = c->fwd_pending[pif];
+		if (!fwd) {
+			err("Received rules for non-existent table");
+			return -1;
+		}
+
+		/* Clear pending table */
+		memset(fwd, 0, sizeof(*fwd));
+
+		if (read_u32(fd, &count))
+			return -1;
+
+		for (i = 0; i < count; i++) {
+			fwd_rule_read(fd, &r);
+			fwd_rule_add(fwd, r.proto, r.flags, &r.addr, NULL,
+				     r.first, r.last, r.to);
+		}
+	}
+
+	return 0;
+}
+
 /**
  * conf_listen_handler() - Handle events on configuration listening socket
  * @c:		Execution context
@@ -2060,14 +2104,14 @@ void conf_listen_handler(struct ctx *c, uint32_t events)
 	union epoll_ref ref = { .type = EPOLL_TYPE_CONF };
 	struct ucred uc = { 0 };
 	socklen_t len = sizeof(uc);
-	int fd, rc;
+	int fd, rc, i;
 
 	if (events != EPOLLIN) {
 		err("Unexpected event 0x%04x on configuration socket", events);
 		return;
 	}
 
-	fd = accept4(c->fd_control_listen, NULL, NULL, SOCK_NONBLOCK);
+	fd = accept4(c->fd_control_listen, NULL, NULL, 0);
 	if (fd < 0) {
 		warn_perror("accept4() on configuration listening socket");
 		return;
@@ -2108,6 +2152,18 @@ void conf_listen_handler(struct ctx *c, uint32_t events)
 	if (conf_send_rules(c, fd) < 0)
 		goto fail;
 
+	if (conf_recv_rules(c, fd) < 0)
+		goto fail;
+
+	info("Received new forwarding table:");
+	for (i = 0; i < PIF_NUM_TYPES; i++) {
+		if (!c->fwd_pending[i])
+			continue;
+
+		info("Forwarding from %s:", pif_name(i));
+		fwd_rules_print(c->fwd_pending[i]);
+	}
+
 	return;
 
 fail:
diff --git a/fwd.c b/fwd.c
index 4592af25..d54a1f15 100644
--- a/fwd.c
+++ b/fwd.c
@@ -247,6 +247,9 @@ void fwd_neigh_table_init(const struct ctx *c)
 static struct fwd_table fwd_in;
 static struct fwd_table fwd_out;
 
+static struct fwd_table fwd_in_pending;
+static struct fwd_table fwd_out_pending;
+
 /**
  * fwd_rule_init() - Initialise forwarding tables
  * @c:		Execution context
@@ -254,8 +257,12 @@ static struct fwd_table fwd_out;
 void fwd_rule_init(struct ctx *c)
 {
 	c->fwd[PIF_HOST] = &fwd_in;
-	if (c->mode == MODE_PASTA)
+	c->fwd_pending[PIF_HOST] = &fwd_in_pending;
+
+	if (c->mode == MODE_PASTA) {
 		c->fwd[PIF_SPLICE] = &fwd_out;
+		c->fwd_pending[PIF_SPLICE] = &fwd_out_pending;
+	}
 }
 
 /**
diff --git a/passt.h b/passt.h
index b3f049de..1726965d 100644
--- a/passt.h
+++ b/passt.h
@@ -188,6 +188,7 @@ struct ip6_ctx {
  * @pasta_ifi:		Index of namespace interface for pasta
  * @pasta_conf_ns:	Configure namespace after creating it
  * @fwd:		Forwarding tables
+ * @fwd_pending:	Pending forward tables
  * @no_tcp:		Disable TCP operation
  * @tcp:		Context for TCP protocol handler
  * @no_udp:		Disable UDP operation
@@ -270,6 +271,7 @@ struct ctx {
 	int pasta_conf_ns;
 
 	struct fwd_table *fwd[PIF_NUM_TYPES];
+	struct fwd_table *fwd_pending[PIF_NUM_TYPES];
 
 	int no_tcp;
 	struct tcp_ctx tcp;
diff --git a/pesto.c b/pesto.c
index aa1d584c..5ee0352e 100644
--- a/pesto.c
+++ b/pesto.c
@@ -38,6 +38,7 @@
 #include "fwd_rule.h"
 #include "pesto.h"
 #include "log.h"
+#include "pif.h"
 
 int verbosity = 1;
 
@@ -65,6 +66,36 @@ static void usage(const char *name, FILE *f, int status)
 	FPRINTF(f, "Usage: %s [OPTION]... PATH\n", name);
 	FPRINTF(f,
 		"\n"
+		"  -t, --tcp-ports SPEC	TCP inbound port forwarding\n"
+		"    can be specified multiple times\n"
+		"    SPEC can be:\n"
+		"      'none': don't forward any ports\n"
+		"      'auto': forward all ports currently bound in namespace\n"
+		"      'all': forward all unbound, non-ephemeral ports\n"
+		"      a comma-separated list, optionally ranged with '-'\n"
+		"        and optional target ports after ':', with optional\n"
+		"        address specification suffixed by '/' and optional\n"
+		"        interface prefixed by '%%'. Examples:\n"
+		"        -t 22	Forward local port 22 to port 22 in netns\n"
+		"        -t 22:23	Forward local port 22 to port 23\n"
+		"        -t 22,25	Forward ports 22, 25 to ports 22, 25\n"
+		"        -t 22-80	Forward ports 22 to 80\n"
+		"        -t 22-80:32-90	Forward ports 22 to 80 to\n"
+		"			corresponding port numbers plus 10\n"
+		"        -t 192.0.2.1/5	Bind port 5 of 192.0.2.1\n"
+		"        -t 5-25,~10-20	Forward ports 5 to 9, and 21 to 25\n"
+		"        -t ~25		Forward all bound ports except for 25\n"
+		"    IPv6 bound ports are also forwarded for IPv4\n"
+		"  -u, --udp-ports SPEC	UDP inbound port forwarding\n"
+		"    SPEC is as described for TCP above\n"
+		"    IPv6 bound ports are also forwarded for IPv4\n"
+		"    unless specified, with '-t auto', UDP ports with numbers\n"
+		"    corresponding to forwarded TCP port numbers are\n"
+		"    forwarded too\n"
+		"  -T, --tcp-ns SPEC	TCP port forwarding to init namespace\n"
+		"    SPEC is as described above\n"
+		"  -U, --udp-ns SPEC	UDP port forwarding to init namespace\n"
+		"    SPEC is as described above\n"
 		"  -v, --verbose		Be more verbose\n"
 		"  -q, --quiet		Be less verbose\n"
 		"  -h, --help		Display this help message and exit\n"
@@ -221,7 +252,7 @@ static void show_state(const struct conf_state *state)
  *
  * Return: 0 on success, won't return on failure
  *
- * #syscalls:pesto connect write close exit_group fstat brk getrandom
+ * #syscalls:pesto connect write close exit_group fstat brk getrandom openat
  * #syscalls:pesto socket s390x:socketcall i686:socketcall
  * #syscalls:pesto recvfrom recvmsg arm:recv ppc64le:recv
  * #syscalls:pesto sendto sendmsg arm:send ppc64le:send
@@ -233,15 +264,22 @@ int main(int argc, char **argv)
 		{"verbose",	no_argument,		NULL,		'v' },
 		{"help",	no_argument,		NULL,		'h' },
 		{"version",	no_argument,		NULL,		1 },
+		{"tcp-ports",	required_argument,	NULL,		't' },
+		{"udp-ports",	required_argument,	NULL,		'u' },
+		{"tcp-ns",	required_argument,	NULL,		'T' },
+		{"udp-ns",	required_argument,	NULL,		'U' },
 		{ 0 },
 	};
+	struct fwd_table fwd_in = { .count = 0 }, fwd_out = { .count = 0 };
 	struct sockaddr_un a = { AF_UNIX, "" };
-	const char *optstring = "vh";
+	const char *optstring = "vht:u:T:U:";
+	enum fwd_mode fwd_default;
 	struct pesto_hello hello;
 	struct conf_state *state;
 	struct sock_fprog prog;
 	int optname, ret, s;
 	uint32_t s_version;
+	unsigned i;
 
 	prog.len = (unsigned short)sizeof(filter_pesto) /
 				   sizeof(filter_pesto[0]);
@@ -250,6 +288,8 @@ int main(int argc, char **argv)
 	    prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog))
 		die("Failed to apply seccomp filter");
 
+	fwd_probe_ephemeral();
+
 	do {
 		optname = getopt_long(argc, argv, optstring, options, NULL);
 
@@ -266,6 +306,18 @@ int main(int argc, char **argv)
 		case 'v':
 			verbosity++;
 			break;
+		case 't':
+		case 'u':
+			fwd_default = FWD_MODE_UNSET;
+			conf_ports(optname, optarg, &fwd_in, &fwd_default,
+				   true, true, true);
+			break;
+		case 'T':
+		case 'U':
+			fwd_default = FWD_MODE_UNSET;
+			conf_ports(optname, optarg, &fwd_out, &fwd_default,
+				   true, true, true);
+			break;
 		case 1:
 			FPRINTF(stdout, "pesto ");
 			FPRINTF(stdout, VERSION_BLOB);
@@ -291,6 +343,22 @@ int main(int argc, char **argv)
 		    a.sun_path, strerror(errno));
 	}
 
+	debug("Inbound rules from command line:");
+	for (i = 0; i < fwd_in.count; i++) {
+		const struct fwd_rule *rule = &fwd_in.rules[i].rule;
+		char rulestr[FWD_RULE_STRLEN];
+
+		debug("    %s", fwd_rule_ntop(rule, rulestr, sizeof(rulestr)));
+	}
+
+	debug("Outbound rules from command line:");
+	for (i = 0; i < fwd_out.count; i++) {
+		const struct fwd_rule *rule = &fwd_out.rules[i].rule;
+		char rulestr[FWD_RULE_STRLEN];
+
+		debug("    %s", fwd_rule_ntop(rule, rulestr, sizeof(rulestr)));
+	}
+
 	debug("Connected to passt/pasta control socket");
 
 	ret = read_all_buf(s, &hello, sizeof(hello));
@@ -324,5 +392,21 @@ int main(int argc, char **argv)
 
 	show_state(state);
 
+	if (fwd_in.count) {
+		write_u8(s, PIF_HOST);
+		write_u32(s, fwd_in.count);
+		for (i = 0; i < fwd_in.count; i++)
+			fwd_rule_write(s, &fwd_in.rules[i].rule);
+	}
+
+	if (fwd_out.count) {
+		write_u8(s, PIF_SPLICE);
+		write_u32(s, fwd_out.count);
+		for (i = 0; i < fwd_out.count; i++)
+			fwd_rule_write(s, &fwd_out.rules[i].rule);
+	}
+
+	write_u8(s, PIF_NONE);
+
 	exit(0);
 }
diff --git a/pif.h b/pif.h
index 7bb58e5c..c96a0900 100644
--- a/pif.h
+++ b/pif.h
@@ -35,6 +35,7 @@ enum pif_type {
 	PIF_NUM_TYPES,
 };
 
+#ifndef PESTO
 extern const char *pif_type_str[];
 
 static inline const char *pif_type(enum pif_type pt)
@@ -66,5 +67,6 @@ void pif_sockaddr(const struct ctx *c, union sockaddr_inany *sa,
 int pif_listen(const struct ctx *c, enum epoll_type type, uint8_t pif,
 	       const union inany_addr *addr, const char *ifname,
 	       in_port_t port, unsigned rule);
+#endif /* !PESTO */
 
 #endif /* PIF_H */
-- 
2.53.0


  parent reply	other threads:[~2026-03-23  8:33 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-23  7:37 [PATCH v3 00/25] RFC: Read-only dynamic update implementation David Gibson
2026-03-23  7:37 ` [PATCH v3 01/25] conf: runas can be const David Gibson
2026-03-23  7:37 ` [PATCH v3 02/25] vhost_user: Fix assorted minor cppcheck warnings David Gibson
2026-03-23  7:37 ` [PATCH v3 03/25] serialise: Split functions user for serialisation from util.c David Gibson
2026-03-25  0:54   ` Stefano Brivio
2026-03-25  1:50     ` David Gibson
2026-03-23  7:37 ` [PATCH v3 04/25] serialise: Add helpers for serialising unsigned integers David Gibson
2026-03-23  7:37 ` [PATCH v3 05/25] fwd: Move selecting correct scan bitmap into fwd_sync_one() David Gibson
2026-03-23  7:37 ` [PATCH v3 06/25] fwd: Look up rule index in fwd_sync_one() David Gibson
2026-03-23  7:37 ` [PATCH v3 07/25] fwd: Store forwarding tables indexed by (origin) pif David Gibson
2026-03-25  0:54   ` Stefano Brivio
2026-03-25  4:04     ` David Gibson
2026-03-23  7:37 ` [PATCH v3 08/25] fwd: Allow FWD_DUAL_STACK_ANY flag to be passed directly to fwd_rule_add() David Gibson
2026-03-25  0:54   ` Stefano Brivio
2026-03-25  4:07     ` David Gibson
2026-03-23  7:37 ` [PATCH v3 09/25] fwd, conf: Expose ephemeral ports as bitmap rather than function David Gibson
2026-03-23  7:37 ` [PATCH v3 10/25] conf: Don't bother complaining about overlapping excluded ranges David Gibson
2026-03-23  7:37 ` [PATCH v3 11/25] conf: Move check for mapping port 0 to caller David Gibson
2026-03-23  7:37 ` [PATCH v3 12/25] conf: Move check for disabled interfaces earlier David Gibson
2026-03-23  7:37 ` [PATCH v3 13/25] pesto: Introduce stub configuration interface and tool David Gibson
2026-03-25  0:54   ` Stefano Brivio
2026-03-23  7:37 ` [PATCH v3 14/25] pesto: Add command line option parsing and debug messages David Gibson
2026-03-25  0:55   ` Stefano Brivio
2026-03-25  4:27     ` David Gibson
2026-03-23  7:37 ` [PATCH v3 15/25] pesto: Expose list of pifs to pesto David Gibson
2026-03-25  0:56   ` Stefano Brivio
2026-03-25  4:34     ` David Gibson
2026-03-25  8:18       ` Stefano Brivio
2026-03-25  8:31         ` David Gibson
2026-03-23  7:37 ` [PATCH v3 16/25] ip: Prepare ip.[ch] for sharing with pesto tool David Gibson
2026-03-23  7:37 ` [PATCH v3 17/25] inany: Prepare inany.[ch] " David Gibson
2026-03-23  7:37 ` [PATCH v3 18/25] fwd: Split forwading rule specification from its implementation state David Gibson
2026-03-23  7:37 ` [PATCH v3 19/25] ip: Define a bound for the string returned by ipproto_name() David Gibson
2026-03-23  7:37 ` [PATCH v3 20/25] fwd_rule: Move forwarding rule text formatting to common code David Gibson
2026-03-25  0:56   ` Stefano Brivio
2026-03-25  4:42     ` David Gibson
2026-03-25  8:18       ` Stefano Brivio
2026-03-25 23:54         ` David Gibson
2026-03-23  7:37 ` [PATCH v3 21/25] pesto: Read current ruleset from passt/pasta and display it David Gibson
2026-03-25  0:56   ` Stefano Brivio
2026-03-25  4:43     ` David Gibson
2026-03-23  7:37 ` [PATCH v3 22/25] conf: Move port parsing functions to own file, ports.c David Gibson
2026-03-23  7:37 ` [PATCH v3 23/25] conf, fwd, ports, util: Move things around for pesto David Gibson
2026-03-23  7:37 ` David Gibson [this message]
2026-03-23  7:37 ` [PATCH v3 25/25] conf, fwd: Allow switching to new rules received from pesto David Gibson
2026-03-23  8:38 ` [PATCH v3 00/25] RFC: Read-only dynamic update implementation David Gibson
2026-03-25  0:56 ` Stefano Brivio
2026-03-25  1:00   ` Stefano Brivio
2026-03-25  4:44     ` David Gibson

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=20260323073732.3158468-25-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).