public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
* [PATCH 0/3] RFC: Target address mapping
@ 2026-07-01  7:08 David Gibson
  2026-07-01  7:08 ` [PATCH 1/3] fwd_rule: Parse target adddresses for forwarding rules David Gibson
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: David Gibson @ 2026-07-01  7:08 UTC (permalink / raw)
  To: passt-dev, Stefano Brivio; +Cc: David Gibson

Here's a next draft for target address remapping.  I'm posting this as
RFC because I have done minimal testing so far.  I've tested the
address parsing, and that we pass the existing tests, but I've done
only a tiny bit of checking of the new funcionality.  Posting so that
it can be reviewed in parallel with that continued testing.

Based on v2 of my parsing rework series.

David Gibson (2):
  fwd_rule: Parse target adddresses for forwarding rules
  fwd: Clarify semantics of --host-lo-to-ns-lo

Stefano Brivio (1):
  fwd, fwd_rule: Implement configurable target address mapping

 conf.c     |  2 ++
 fwd.c      | 30 ++++++++++++++++--------------
 fwd_rule.c | 31 ++++++++++++++++++++++++-------
 fwd_rule.h |  2 ++
 passt.1    |  9 +++++----
 pesto.h    |  6 +++++-
 6 files changed, 54 insertions(+), 26 deletions(-)

-- 
2.54.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/3] fwd_rule: Parse target adddresses for forwarding rules
  2026-07-01  7:08 [PATCH 0/3] RFC: Target address mapping David Gibson
@ 2026-07-01  7:08 ` David Gibson
  2026-07-01  7:08 ` [PATCH 2/3] fwd: Clarify semantics of --host-lo-to-ns-lo David Gibson
  2026-07-01  7:08 ` [PATCH 3/3] fwd, fwd_rule: Implement configurable target address mapping David Gibson
  2 siblings, 0 replies; 4+ messages in thread
From: David Gibson @ 2026-07-01  7:08 UTC (permalink / raw)
  To: passt-dev, Stefano Brivio; +Cc: David Gibson

Extend the parsing of forwarding rules (-[tu]) to allow the destination
address on the target side to be specified.  For now just parse them, and
give an error if we try to create rules with a specified target address.
We'll implement the actual forwarding logic in another patch.

Format (for either command line or pesto):
      -t 2222:192.0.2.1/2222

This should work along with all the other bits, that is, say:
      -t 192.0.2.1%eth0/2222-2225:192.0.2.2/22-25

FIXME: Ban for -[TU] for now
FIXME: Check interaction with splice handling

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
[dwg: Syntax from Stefano's earlier draft, largely rewritten on top of new
 parsing helpers]
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 fwd_rule.c | 38 +++++++++++++++++++++++++++++++-------
 1 file changed, 31 insertions(+), 7 deletions(-)

diff --git a/fwd_rule.c b/fwd_rule.c
index ca409eaf..e8abc884 100644
--- a/fwd_rule.c
+++ b/fwd_rule.c
@@ -378,14 +378,17 @@ int fwd_rule_add(struct fwd_table *fwd, const struct fwd_rule *new)
  * @first:	First port to forward
  * @last:	Last port to forward
  * @exclude:	Bitmap of ports to exclude (may be NULL)
- * @to:		Port to translate @first to when forwarding
+ * @tgt_addr:	Destination address on the target side
+ * @tgt_first:	Destination port to use for @first on the traget side
  * @flags:	Flags for forwarding entries
  */
 static void fwd_rule_range_except(struct fwd_table *fwd, bool del,
 				  uint8_t proto, const union inany_addr *addr,
 				  const char *ifname,
 				  uint16_t first, uint16_t last,
-				  const uint8_t *exclude, uint16_t to,
+				  const uint8_t *exclude,
+				  const union inany_addr *tgt_addr,
+				  uint16_t tgt_first,
 				  uint8_t flags)
 {
 	struct fwd_rule rule = {
@@ -395,9 +398,17 @@ static void fwd_rule_range_except(struct fwd_table *fwd, bool del,
 		.flags = flags,
 	};
 	char rulestr[FWD_RULE_STRLEN];
-	unsigned delta = to - first;
+	unsigned delta = tgt_first - first;
 	unsigned base, i;
 
+	if (tgt_addr && !inany_is_unspecified(tgt_addr)) {
+		char astr[INANY_ADDRSTRLEN];
+
+		info("Target address: %s",
+		     inany_ntop(tgt_addr, astr, sizeof(astr)));
+		die("Target address remapping not yet implemented");
+	}
+
 	if (!addr)
 		rule.flags |= FWD_DUAL_STACK_ANY;
 	if (ifname) {
@@ -458,14 +469,17 @@ enum fwd_port_chunk_kind {
  * @cursor:	Parsing point (see parse.c)
  * @kindp:	Updated with kind of chunk we parsed
  * @lrange:	Updated with listening port range (for INCLUDE & EXCLUDE)
+ * @taddr:	Updated with target address (for INCLUDE)
  * @trange:	Updated with target port range (for INCLUDE)
  */
 static bool parse_port_chunk(const char **cursor,
 			     enum fwd_port_chunk_kind *kindp,
 			     struct port_range *lrange,
+			     union inany_addr *taddr,
 			     struct port_range *trange)
 {
 	struct port_range lr = { 0 }, tr = { 0 };
+	union inany_addr taddr_tmp = inany_any6;
 	enum fwd_port_chunk_kind kind;
 	const char *p = *cursor;
 
@@ -481,6 +495,12 @@ static bool parse_port_chunk(const char **cursor,
 		kind = CHUNK_INCLUDE;
 
 		if (parse_literal(&p, ":")) {
+			const char *tgtspec = p;
+
+			if (!parse_inany(&p, &taddr_tmp)	||
+			    !parse_literal(&p, "/"))
+				p = tgtspec; /* No target address, backtrack */
+
 			if (!parse_port_range(&p, &tr))
 				return false;
 		} else {
@@ -492,6 +512,8 @@ static bool parse_port_chunk(const char **cursor,
 
 	*kindp = kind;
 	*lrange = lr;
+	if (taddr)
+		*taddr = taddr_tmp;
 	if (trange)
 		*trange = tr;
 	*cursor = p;
@@ -561,7 +583,7 @@ static void fwd_rule_parse_ports(struct fwd_table *fwd, bool del, uint8_t proto,
 	/* Consider excluded ranges and "auto" in the first pass */
 	p = spec;
 	do {
-		if (!parse_port_chunk(&p, &kind, &lrange, NULL))
+		if (!parse_port_chunk(&p, &kind, &lrange, NULL, NULL))
 			goto bad;
 
 		switch (kind) {
@@ -586,8 +608,9 @@ static void fwd_rule_parse_ports(struct fwd_table *fwd, bool del, uint8_t proto,
 	p = spec;
 	do {
 		struct port_range trange;
+		union inany_addr taddr;
 
-		if (!parse_port_chunk(&p, &kind, &lrange, &trange))
+		if (!parse_port_chunk(&p, &kind, &lrange, &taddr, &trange))
 			goto bad;
 
 		switch (kind) {
@@ -604,7 +627,8 @@ static void fwd_rule_parse_ports(struct fwd_table *fwd, bool del, uint8_t proto,
 
 			fwd_rule_range_except(fwd, del, proto, addr, ifname,
 					      lrange.first, lrange.last,
-					      exclude, trange.first, flags);
+					      exclude, &taddr, trange.first,
+					      flags);
 			break;
 		default:
 			goto bad;
@@ -620,7 +644,7 @@ static void fwd_rule_parse_ports(struct fwd_table *fwd, bool del, uint8_t proto,
 
 		fwd_rule_range_except(fwd, del, proto, addr, ifname,
 				      1, NUM_PORTS - 1, exclude,
-				      1, flags | FWD_WEAK);
+				      NULL, 1, flags | FWD_WEAK);
 	}
 	return;
 bad:
-- 
2.54.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 2/3] fwd: Clarify semantics of --host-lo-to-ns-lo
  2026-07-01  7:08 [PATCH 0/3] RFC: Target address mapping David Gibson
  2026-07-01  7:08 ` [PATCH 1/3] fwd_rule: Parse target adddresses for forwarding rules David Gibson
@ 2026-07-01  7:08 ` David Gibson
  2026-07-01  7:08 ` [PATCH 3/3] fwd, fwd_rule: Implement configurable target address mapping David Gibson
  2 siblings, 0 replies; 4+ messages in thread
From: David Gibson @ 2026-07-01  7:08 UTC (permalink / raw)
  To: passt-dev, Stefano Brivio; +Cc: David Gibson

The semantics of --host-lo-to-ns-lo as described in the man page don't
quite make sense: It says without the option forwarded packets will appear
to come _from_ the guest's public address, which is not usually true.
Instead the packets will arrive *to* the guest's public address.  The exact
semantics are also a bit confusing in general.

Rewrite both the man page and code to clarify this.  The new rule is that
it redirects connections addressed to a host loopback address to the same
loopback address in the guest.  This is notionally different from what we
had in two ways:
  * We can now deliver to nonstandard loopback addresses within the guest,
    not just the default one.  This is technically a behavioural change,
    but I think will be less surprising behaviour.
  * The decision is now made on the original _destination_ address, rather
    than source address.  That's different theoreically, but not in
    practice, since loopback packets must have loopback addresses for both
    source and destination.

We make it explicitly incompatible with --no-splice - previously it
was allowed, but would have no effect in that case.

As well as being more precise right now, these semantics will intersect
better with upcoming remapping of target address by forwarding rules.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 conf.c  |  2 ++
 fwd.c   | 22 ++++++++++------------
 passt.1 |  9 +++++----
 3 files changed, 17 insertions(+), 16 deletions(-)

diff --git a/conf.c b/conf.c
index c4a36dee..912543fa 100644
--- a/conf.c
+++ b/conf.c
@@ -1792,6 +1792,8 @@ void conf(struct ctx *c, int argc, char **argv)
 		if (c->splice_only)
 			die("--splice-only is for pasta mode only");
 	}
+	if (c->no_splice && c->host_lo_to_ns_lo)
+		die("--host-lo-to-ns-lo is incompatible with --no-splice");
 
 	if (c->mode == MODE_PASTA && !c->pasta_conf_ns) {
 		if (copy_routes_opt)
diff --git a/fwd.c b/fwd.c
index 042158cf..659f8d9f 100644
--- a/fwd.c
+++ b/fwd.c
@@ -1036,21 +1036,19 @@ uint8_t fwd_nat_from_host(const struct ctx *c,
 		 * In either case, let the kernel pick the source address to
 		 * match.
 		 */
-		if (inany_v4(&ini->eaddr)) {
-			if (c->host_lo_to_ns_lo)
-				tgt->eaddr = inany_loopback4;
-			else
-				tgt->eaddr = inany_from_v4(c->ip4.addr_seen);
+		if (c->host_lo_to_ns_lo && inany_is_loopback(&ini->oaddr))
+			tgt->eaddr = ini->oaddr;
+		else if (inany_v4(&ini->eaddr))
+			tgt->eaddr = inany_from_v4(c->ip4.addr_seen);
+		else
+			tgt->eaddr.a6 = c->ip6.addr_seen;
+
+		/* Let the kernel pick source address and port */
+		if (inany_v4(&tgt->eaddr))
 			tgt->oaddr = inany_any4;
-		} else {
-			if (c->host_lo_to_ns_lo)
-				tgt->eaddr = inany_loopback6;
-			else
-				tgt->eaddr.a6 = c->ip6.addr_seen;
+		else
 			tgt->oaddr = inany_any6;
-		}
 
-		/* Let the kernel pick source port */
 		tgt->oport = 0;
 		if (proto == IPPROTO_UDP)
 			/* But for UDP preserve the source port */
diff --git a/passt.1 b/passt.1
index 908fd4a4..d057aebc 100644
--- a/passt.1
+++ b/passt.1
@@ -650,10 +650,11 @@ Default is \fBauto\fR.
 
 .TP
 .BR \-\-host-lo-to-ns-lo
-If specified, connections forwarded with \fB\-t\fR and \fB\-u\fR from
-the host's loopback address will appear on the loopback address in the
-guest as well.  Without this option such forwarded packets will appear
-to come from the guest's public address.
+If specified, connections to a host loopback address forwarded with
+\fB\-t\fR or \fB\-u\fR will be delivered to the same loopback address
+on the guest.  Without this option such connections are forwarded to
+the guest's public address.  This option is incompatible with
+\fB--no-splice\fR.
 
 .TP
 .BR \-\-userns " " \fIspec
-- 
2.54.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 3/3] fwd, fwd_rule: Implement configurable target address mapping
  2026-07-01  7:08 [PATCH 0/3] RFC: Target address mapping David Gibson
  2026-07-01  7:08 ` [PATCH 1/3] fwd_rule: Parse target adddresses for forwarding rules David Gibson
  2026-07-01  7:08 ` [PATCH 2/3] fwd: Clarify semantics of --host-lo-to-ns-lo David Gibson
@ 2026-07-01  7:08 ` David Gibson
  2 siblings, 0 replies; 4+ messages in thread
From: David Gibson @ 2026-07-01  7:08 UTC (permalink / raw)
  To: passt-dev, Stefano Brivio; +Cc: David Gibson

From: Stefano Brivio <sbrivio@redhat.com>

Add a 'taddr' field to forwarding rules, which controls the destination
address on the target side.  Since changing the structure alters the pesto
update protocol, bump the protocol version number

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
[dwg: Split from option parsing code, added protocol version bump,
 explicitly exclude splicing with target address for now]
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 fwd.c      | 8 ++++++--
 fwd_rule.c | 9 +--------
 fwd_rule.h | 2 ++
 pesto.h    | 6 +++++-
 4 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/fwd.c b/fwd.c
index 659f8d9f..84400948 100644
--- a/fwd.c
+++ b/fwd.c
@@ -1023,7 +1023,9 @@ uint8_t fwd_nat_from_host(const struct ctx *c,
 	/* Common for spliced and non-spliced cases */
 	tgt->eport = rule->to + (ini->oport - rule->first);
 
-	if (!c->no_splice && inany_is_loopback(&ini->eaddr) &&
+	/* TODO: Allow splicing with specified target address */
+	if (!c->no_splice && inany_is_unspecified(&rule->taddr) &&
+	    inany_is_loopback(&ini->eaddr) &&
 	    (proto == IPPROTO_TCP || proto == IPPROTO_UDP)) {
 		/* spliceable */
 
@@ -1072,7 +1074,9 @@ uint8_t fwd_nat_from_host(const struct ctx *c,
 	}
 	tgt->oport = ini->eport;
 
-	if (inany_v4(&tgt->oaddr)) {
+	if (!inany_is_unspecified(&rule->taddr)) {
+		tgt->eaddr = rule->taddr;
+	} else if (inany_v4(&tgt->oaddr)) {
 		tgt->eaddr = inany_from_v4(c->ip4.addr_seen);
 	} else {
 		if (inany_is_linklocal6(&tgt->oaddr))
diff --git a/fwd_rule.c b/fwd_rule.c
index e8abc884..494d3fc3 100644
--- a/fwd_rule.c
+++ b/fwd_rule.c
@@ -393,6 +393,7 @@ static void fwd_rule_range_except(struct fwd_table *fwd, bool del,
 {
 	struct fwd_rule rule = {
 		.addr = addr ? *addr : inany_any6,
+		.taddr = tgt_addr ? *tgt_addr : inany_any6,
 		.ifname = { 0 },
 		.proto = proto,
 		.flags = flags,
@@ -401,14 +402,6 @@ static void fwd_rule_range_except(struct fwd_table *fwd, bool del,
 	unsigned delta = tgt_first - first;
 	unsigned base, i;
 
-	if (tgt_addr && !inany_is_unspecified(tgt_addr)) {
-		char astr[INANY_ADDRSTRLEN];
-
-		info("Target address: %s",
-		     inany_ntop(tgt_addr, astr, sizeof(astr)));
-		die("Target address remapping not yet implemented");
-	}
-
 	if (!addr)
 		rule.flags |= FWD_DUAL_STACK_ANY;
 	if (ifname) {
diff --git a/fwd_rule.h b/fwd_rule.h
index 435be5bd..c782f9d4 100644
--- a/fwd_rule.h
+++ b/fwd_rule.h
@@ -33,6 +33,7 @@
 /**
  * struct fwd_rule - Forwarding rule governing a range of ports
  * @addr:	Address to forward from
+ * @taddr:	Target side destination address
  * @ifname:	Interface to forward from
  * @first:	First port number to forward
  * @last:	Last port number to forward
@@ -45,6 +46,7 @@
  */
 struct fwd_rule {
 	union inany_addr addr;
+	union inany_addr taddr;
 	char ifname[IFNAMSIZ];
 	in_port_t first;
 	in_port_t last;
diff --git a/pesto.h b/pesto.h
index 980cc17d..8db701b4 100644
--- a/pesto.h
+++ b/pesto.h
@@ -15,7 +15,11 @@
 #define PESTO_SERVER_MAGIC	"basil:s"
 
 /* Version 0 is reserved for unreleased / unsupported experimental versions */
-#define PESTO_PROTOCOL_VERSION	1
+/* Version 1 had no target address field in struct fwd_rule.  It was released,
+ * but was little enough used that we decided not to implement backwards
+ * compatiblity code (i.e. a v2 pesto will not work with a v1 pasta)
+ */
+#define PESTO_PROTOCOL_VERSION	2
 
 /* Maximum size of a pif name, including \0 */
 #define	PIF_NAME_SIZE	(128)
-- 
2.54.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-07-01  7:08 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-01  7:08 [PATCH 0/3] RFC: Target address mapping David Gibson
2026-07-01  7:08 ` [PATCH 1/3] fwd_rule: Parse target adddresses for forwarding rules David Gibson
2026-07-01  7:08 ` [PATCH 2/3] fwd: Clarify semantics of --host-lo-to-ns-lo David Gibson
2026-07-01  7:08 ` [PATCH 3/3] fwd, fwd_rule: Implement configurable target address mapping David Gibson

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).