* [PATCH 0/6] Unify TCP and UDP forwarding tables
@ 2026-03-10 4:15 David Gibson
2026-03-10 4:16 ` [PATCH 1/6] conf, fwd: Make overall forwarding mode local to conf path David Gibson
` (5 more replies)
0 siblings, 6 replies; 11+ messages in thread
From: David Gibson @ 2026-03-10 4:15 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
This will make the dynamic update protocol and future forwarding
extensions easier. Along the way I spotted a number of other minor
faults, and have fixed those too.
David Gibson (6):
conf, fwd: Make overall forwarding mode local to conf path
tcp: Remove stale description of port_to_tap field
fwd: Don't initialise unused port bitmaps
Fix misnamed field in struct ctx comments
fwd: Split forwarding table from port scanning state
fwd: Unify TCP and UDP forwarding tables
conf.c | 129 ++++++++++++++++-------------
flow.c | 23 ++----
fwd.c | 247 ++++++++++++++++++++++++++++++++++----------------------
fwd.h | 58 ++++++-------
passt.c | 3 +
passt.h | 7 +-
tcp.c | 8 +-
tcp.h | 11 +--
udp.c | 9 +--
udp.h | 8 +-
10 files changed, 282 insertions(+), 221 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/6] conf, fwd: Make overall forwarding mode local to conf path
2026-03-10 4:15 [PATCH 0/6] Unify TCP and UDP forwarding tables David Gibson
@ 2026-03-10 4:16 ` David Gibson
2026-03-10 19:33 ` Stefano Brivio
2026-03-10 4:16 ` [PATCH 2/6] tcp: Remove stale description of port_to_tap field David Gibson
` (4 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: David Gibson @ 2026-03-10 4:16 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
The 'mode' field of struct fwd_ports records the overall forwarding mode.
Now that runtime forwarding decisions are made based on the forwarding
table, this is almost unused outside conf().
The only exception is the auto-port scanning code, which uses it to
determine if a port scan is necessary. We can instead derive that from the
forwarding table itself by checking if there are any entries with the
FWD_SCAN flag.
Once that's done, make the mode purely local to conf(). While we're there
rename the constants to FWD_MODE_* to avoid confusion with the forwarding
rule flag bits, which are also prefixed with FWD_.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
conf.c | 82 ++++++++++++++++++++++++++++++++++++----------------------
fwd.c | 27 ++++++++++++++-----
fwd.h | 10 -------
3 files changed, 72 insertions(+), 47 deletions(-)
diff --git a/conf.c b/conf.c
index 11d84536..c436b88e 100644
--- a/conf.c
+++ b/conf.c
@@ -199,15 +199,27 @@ static void conf_ports_range_except(const struct ctx *c, char optname,
}
}
+/**
+ * enum fwd_mode - Overall forwarding mode for a direction and protocol
+ */
+enum fwd_mode {
+ FWD_MODE_UNSET = 0,
+ FWD_MODE_SPEC,
+ FWD_MODE_NONE,
+ FWD_MODE_AUTO,
+ FWD_MODE_ALL,
+};
+
/**
* conf_ports() - Parse port configuration options, initialise UDP/TCP sockets
* @c: Execution context
* @optname: Short option name, t, T, u, or U
* @optarg: Option argument (port specification)
* @fwd: Pointer to @fwd_ports to be updated
+ * @mode: Overall port forwarding mode (updated)
*/
static void conf_ports(const struct ctx *c, char optname, const char *optarg,
- struct fwd_ports *fwd)
+ struct fwd_ports *fwd, enum fwd_mode *mode)
{
union inany_addr addr_buf = inany_any6, *addr = &addr_buf;
char buf[BUFSIZ], *spec, *ifname = NULL, *p;
@@ -216,10 +228,10 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
unsigned i;
if (!strcmp(optarg, "none")) {
- if (fwd->mode)
+ if (*mode)
goto mode_conflict;
- fwd->mode = FWD_NONE;
+ *mode = FWD_MODE_NONE;
return;
}
@@ -229,7 +241,7 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
die("UDP port forwarding requested but UDP is disabled");
if (!strcmp(optarg, "auto")) {
- if (fwd->mode)
+ if (*mode)
goto mode_conflict;
if (c->mode != MODE_PASTA)
@@ -241,18 +253,18 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
warn(
"Forwarding from addresses other than 127.0.0.1 will not work");
}
- fwd->mode = FWD_AUTO;
+ *mode = FWD_MODE_AUTO;
return;
}
if (!strcmp(optarg, "all")) {
- if (fwd->mode)
+ if (*mode)
goto mode_conflict;
if (c->mode == MODE_PASTA)
die("'all' port forwarding is only allowed for passt");
- fwd->mode = FWD_ALL;
+ *mode = FWD_MODE_ALL;
/* Exclude ephemeral ports */
for (i = 0; i < NUM_PORTS; i++)
@@ -266,10 +278,10 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
return;
}
- if (fwd->mode > FWD_SPEC)
+ if (*mode > FWD_MODE_SPEC)
die("Specific ports cannot be specified together with all/none/auto");
- fwd->mode = FWD_SPEC;
+ *mode = FWD_MODE_SPEC;
strncpy(buf, optarg, sizeof(buf) - 1);
@@ -1525,7 +1537,11 @@ void conf(struct ctx *c, int argc, char **argv)
const char *logname = (c->mode == MODE_PASTA) ? "pasta" : "passt";
char userns[PATH_MAX] = { 0 }, netns[PATH_MAX] = { 0 };
bool copy_addrs_opt = false, copy_routes_opt = false;
- enum fwd_ports_mode fwd_default = FWD_NONE;
+ enum fwd_mode tcp_out_mode = FWD_MODE_UNSET;
+ enum fwd_mode udp_out_mode = FWD_MODE_UNSET;
+ enum fwd_mode tcp_in_mode = FWD_MODE_UNSET;
+ enum fwd_mode udp_in_mode = FWD_MODE_UNSET;
+ enum fwd_mode fwd_default = FWD_MODE_NONE;
bool v4_only = false, v6_only = false;
unsigned dns4_idx = 0, dns6_idx = 0;
unsigned long max_mtu = IP_MAX_MTU;
@@ -1540,17 +1556,16 @@ void conf(struct ctx *c, int argc, char **argv)
int name, ret;
uid_t uid;
gid_t gid;
+
if (c->mode == MODE_PASTA) {
c->no_dhcp_dns = c->no_dhcp_dns_search = 1;
- fwd_default = FWD_AUTO;
+ fwd_default = FWD_MODE_AUTO;
}
if (tap_l2_max_len(c) - ETH_HLEN < max_mtu)
max_mtu = tap_l2_max_len(c) - ETH_HLEN;
c->mtu = ROUND_DOWN(max_mtu, sizeof(uint32_t));
- c->tcp.fwd_in.mode = c->tcp.fwd_out.mode = FWD_UNSET;
- c->udp.fwd_in.mode = c->udp.fwd_out.mode = FWD_UNSET;
memcpy(c->our_tap_mac, MAC_OUR_LAA, ETH_ALEN);
optind = 0;
@@ -2100,9 +2115,11 @@ void conf(struct ctx *c, int argc, char **argv)
name = getopt_long(argc, argv, optstring, options, NULL);
if (name == 't') {
- conf_ports(c, name, optarg, &c->tcp.fwd_in);
+ conf_ports(c, name, optarg,
+ &c->tcp.fwd_in, &tcp_in_mode);
} else if (name == 'u') {
- conf_ports(c, name, optarg, &c->udp.fwd_in);
+ conf_ports(c, name, optarg,
+ &c->udp.fwd_in, &udp_in_mode);
} else if (name == 'D') {
struct in6_addr dns6_tmp;
struct in_addr dns4_tmp;
@@ -2172,10 +2189,13 @@ void conf(struct ctx *c, int argc, char **argv)
do {
name = getopt_long(argc, argv, optstring, options, NULL);
- if (name == 'T')
- conf_ports(c, name, optarg, &c->tcp.fwd_out);
- else if (name == 'U')
- conf_ports(c, name, optarg, &c->udp.fwd_out);
+ if (name == 'T') {
+ conf_ports(c, name, optarg,
+ &c->tcp.fwd_out, &tcp_out_mode);
+ } else if (name == 'U') {
+ conf_ports(c, name, optarg,
+ &c->udp.fwd_out, &udp_out_mode);
+ }
} while (name != -1);
if (!c->ifi4)
@@ -2197,31 +2217,31 @@ void conf(struct ctx *c, int argc, char **argv)
if_indextoname(c->ifi6, c->pasta_ifn);
}
- if (!c->tcp.fwd_in.mode)
- c->tcp.fwd_in.mode = fwd_default;
- if (!c->tcp.fwd_out.mode)
- c->tcp.fwd_out.mode = fwd_default;
- if (!c->udp.fwd_in.mode)
- c->udp.fwd_in.mode = fwd_default;
- if (!c->udp.fwd_out.mode)
- c->udp.fwd_out.mode = fwd_default;
+ if (!tcp_in_mode)
+ tcp_in_mode = fwd_default;
+ if (!tcp_out_mode)
+ tcp_out_mode = fwd_default;
+ if (!udp_in_mode)
+ udp_in_mode = fwd_default;
+ if (!udp_out_mode)
+ udp_out_mode = fwd_default;
- if (c->tcp.fwd_in.mode == FWD_AUTO) {
+ if (tcp_in_mode == FWD_MODE_AUTO) {
conf_ports_range_except(c, 't', "auto", &c->tcp.fwd_in,
NULL, NULL, 1, NUM_PORTS - 1,
NULL, 1, FWD_SCAN);
}
- if (c->tcp.fwd_out.mode == FWD_AUTO) {
+ if (tcp_out_mode == FWD_MODE_AUTO) {
conf_ports_range_except(c, 'T', "auto", &c->tcp.fwd_out,
NULL, "lo", 1, NUM_PORTS - 1,
NULL, 1, FWD_SCAN);
}
- if (c->udp.fwd_in.mode == FWD_AUTO) {
+ if (udp_in_mode == FWD_MODE_AUTO) {
conf_ports_range_except(c, 'u', "auto", &c->udp.fwd_in,
NULL, NULL, 1, NUM_PORTS - 1,
NULL, 1, FWD_SCAN);
}
- if (c->udp.fwd_out.mode == FWD_AUTO) {
+ if (udp_out_mode == FWD_MODE_AUTO) {
conf_ports_range_except(c, 'U', "auto", &c->udp.fwd_out,
NULL, "lo", 1, NUM_PORTS - 1,
NULL, 1, FWD_SCAN);
diff --git a/fwd.c b/fwd.c
index 4052b797..c5090fb1 100644
--- a/fwd.c
+++ b/fwd.c
@@ -717,6 +717,21 @@ static void procfs_scan_listen(int fd, unsigned int lstate, uint8_t *map)
}
}
+/**
+ * has_scan_rules() - Does the given table have any FWD_SCAN rules?
+ * @fwd: Forwarding table
+ */
+static bool has_scan_rules(const struct fwd_ports *fwd)
+{
+ unsigned i;
+
+ for (i = 0; i < fwd->count; i++) {
+ if (fwd->rules[i].flags & FWD_SCAN)
+ return true;
+ }
+ return false;
+}
+
/**
* fwd_scan_ports_tcp() - Scan /proc to update TCP forwarding map
* @fwd: Forwarding information to update
@@ -724,7 +739,7 @@ static void procfs_scan_listen(int fd, unsigned int lstate, uint8_t *map)
*/
static void fwd_scan_ports_tcp(struct fwd_ports *fwd, const uint8_t *exclude)
{
- if (fwd->mode != FWD_AUTO)
+ if (!has_scan_rules(fwd))
return;
memset(fwd->map, 0, PORT_BITMAP_SIZE);
@@ -743,7 +758,7 @@ static void fwd_scan_ports_udp(struct fwd_ports *fwd,
const struct fwd_ports *tcp_fwd,
const uint8_t *exclude)
{
- if (fwd->mode != FWD_AUTO)
+ if (!has_scan_rules(fwd))
return;
memset(fwd->map, 0, PORT_BITMAP_SIZE);
@@ -816,19 +831,19 @@ void fwd_scan_ports_init(struct ctx *c)
c->udp.fwd_in.scan4 = c->udp.fwd_in.scan6 = -1;
c->udp.fwd_out.scan4 = c->udp.fwd_out.scan6 = -1;
- if (c->tcp.fwd_in.mode == FWD_AUTO) {
+ if (has_scan_rules(&c->tcp.fwd_in)) {
c->tcp.fwd_in.scan4 = open_in_ns(c, "/proc/net/tcp", flags);
c->tcp.fwd_in.scan6 = open_in_ns(c, "/proc/net/tcp6", flags);
}
- if (c->udp.fwd_in.mode == FWD_AUTO) {
+ if (has_scan_rules(&c->udp.fwd_in)) {
c->udp.fwd_in.scan4 = open_in_ns(c, "/proc/net/udp", flags);
c->udp.fwd_in.scan6 = open_in_ns(c, "/proc/net/udp6", flags);
}
- if (c->tcp.fwd_out.mode == FWD_AUTO) {
+ if (has_scan_rules(&c->tcp.fwd_out)) {
c->tcp.fwd_out.scan4 = open("/proc/net/tcp", flags);
c->tcp.fwd_out.scan6 = open("/proc/net/tcp6", flags);
}
- if (c->udp.fwd_out.mode == FWD_AUTO) {
+ if (has_scan_rules(&c->udp.fwd_out)) {
c->udp.fwd_out.scan4 = open("/proc/net/udp", flags);
c->udp.fwd_out.scan6 = open("/proc/net/udp6", flags);
}
diff --git a/fwd.h b/fwd.h
index 7f52f76e..6d657ddc 100644
--- a/fwd.h
+++ b/fwd.h
@@ -68,14 +68,6 @@ struct fwd_listen_ref {
unsigned rule :FWD_RULE_BITS;
};
-enum fwd_ports_mode {
- FWD_UNSET = 0,
- FWD_SPEC = 1,
- FWD_NONE,
- FWD_AUTO,
- FWD_ALL,
-};
-
#define PORT_BITMAP_SIZE DIV_ROUND_UP(NUM_PORTS, 8)
/* Maximum number of listening sockets (per pif & protocol)
@@ -87,7 +79,6 @@ enum fwd_ports_mode {
/**
* fwd_ports() - Describes port forwarding for one protocol and direction
- * @mode: Overall mode (all, none, auto, specific ports)
* @scan4: /proc/net fd to scan for IPv4 ports when in AUTO mode
* @scan6: /proc/net fd to scan for IPv6 ports when in AUTO mode
* @count: Number of forwarding rules
@@ -97,7 +88,6 @@ enum fwd_ports_mode {
* @socks: Listening sockets for forwarding
*/
struct fwd_ports {
- enum fwd_ports_mode mode;
int scan4;
int scan6;
unsigned count;
--
2.53.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/6] tcp: Remove stale description of port_to_tap field
2026-03-10 4:15 [PATCH 0/6] Unify TCP and UDP forwarding tables David Gibson
2026-03-10 4:16 ` [PATCH 1/6] conf, fwd: Make overall forwarding mode local to conf path David Gibson
@ 2026-03-10 4:16 ` David Gibson
2026-03-10 4:16 ` [PATCH 3/6] fwd: Don't initialise unused port bitmaps David Gibson
` (3 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: David Gibson @ 2026-03-10 4:16 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
This field was removed in 163dc5f18899 ("Consolidate port forwarding
configuration into a common structure"), but the corresponding comment
describing it was not. Fix the oversight.
Fixes: 163dc5f18899 ("Consolidate port forwarding configuration into a common structure")
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
tcp.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/tcp.h b/tcp.h
index 384974d1..c9455e85 100644
--- a/tcp.h
+++ b/tcp.h
@@ -38,7 +38,6 @@ extern bool peek_offset_cap;
/**
* struct tcp_ctx - Execution context for TCP routines
- * @port_to_tap: Ports bound host-side, packets to tap or spliced
* @fwd_in: Port forwarding configuration for inbound packets
* @fwd_out: Port forwarding configuration for outbound packets
* @timer_run: Timestamp of most recent timer run
--
2.53.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 3/6] fwd: Don't initialise unused port bitmaps
2026-03-10 4:15 [PATCH 0/6] Unify TCP and UDP forwarding tables David Gibson
2026-03-10 4:16 ` [PATCH 1/6] conf, fwd: Make overall forwarding mode local to conf path David Gibson
2026-03-10 4:16 ` [PATCH 2/6] tcp: Remove stale description of port_to_tap field David Gibson
@ 2026-03-10 4:16 ` David Gibson
2026-03-10 4:16 ` [PATCH 4/6] Fix misnamed field in struct ctx comments David Gibson
` (2 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: David Gibson @ 2026-03-10 4:16 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
Since b223bec48213 ("fwd, tcp, udp: Set up listening sockets based on
forward table"), the 'map' field of struct fwd_ports has only been used
for forwarding rules with the FWD_SCAN bit, where it is initialised from a
port scan. However, we still pointlessly initialise it for rules with
!FWD_SCAN. Remove the unneeded code.
Fixes: b223bec48213 ("fwd, tcp, udp: Set up listening sockets based on forward table")
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
fwd.c | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/fwd.c b/fwd.c
index c5090fb1..9e455094 100644
--- a/fwd.c
+++ b/fwd.c
@@ -405,13 +405,8 @@ void fwd_rule_add(struct fwd_ports *fwd, uint8_t flags,
new->socks = &fwd->socks[fwd->sock_count];
fwd->sock_count += num;
- for (port = new->first; port <= new->last; port++) {
+ for (port = new->first; port <= new->last; port++)
new->socks[port - new->first] = -1;
-
- /* Fill in the legacy forwarding data structures to match the table */
- if (!(new->flags & FWD_SCAN))
- bitmap_set(fwd->map, port);
- }
}
/**
--
2.53.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 4/6] Fix misnamed field in struct ctx comments
2026-03-10 4:15 [PATCH 0/6] Unify TCP and UDP forwarding tables David Gibson
` (2 preceding siblings ...)
2026-03-10 4:16 ` [PATCH 3/6] fwd: Don't initialise unused port bitmaps David Gibson
@ 2026-03-10 4:16 ` David Gibson
2026-03-10 4:16 ` [PATCH 5/6] fwd: Split forwarding table from port scanning state David Gibson
2026-03-10 4:16 ` [PATCH 6/6] fwd: Unify TCP and UDP forwarding tables David Gibson
5 siblings, 0 replies; 11+ messages in thread
From: David Gibson @ 2026-03-10 4:16 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
@no_udp wasn't listed, but @no_tcp was listed twice.
Fixes: 1e49d194d017 ("passt, pasta: Introduce command-line options and port re-mapping")
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
passt.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/passt.h b/passt.h
index 299185ba..0548dacd 100644
--- a/passt.h
+++ b/passt.h
@@ -186,7 +186,7 @@ struct ip6_ctx {
* @pasta_conf_ns: Configure namespace after creating it
* @no_tcp: Disable TCP operation
* @tcp: Context for TCP protocol handler
- * @no_tcp: Disable UDP operation
+ * @no_udp: Disable UDP operation
* @udp: Context for UDP protocol handler
* @no_icmp: Disable ICMP operation
* @icmp: Context for ICMP protocol handler
--
2.53.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 5/6] fwd: Split forwarding table from port scanning state
2026-03-10 4:15 [PATCH 0/6] Unify TCP and UDP forwarding tables David Gibson
` (3 preceding siblings ...)
2026-03-10 4:16 ` [PATCH 4/6] Fix misnamed field in struct ctx comments David Gibson
@ 2026-03-10 4:16 ` David Gibson
2026-03-10 4:16 ` [PATCH 6/6] fwd: Unify TCP and UDP forwarding tables David Gibson
5 siblings, 0 replies; 11+ messages in thread
From: David Gibson @ 2026-03-10 4:16 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
For hsitorical reasons, struct fwd_ports contained both the new forwarding
table and some older state related to port / scanning auto-forwarding
detection. They are related, but keeping them together prevents some
future reworks we want to do.
Separate them into struct fwd_table (for the table) and struct fwd_scan
for the scanning state. Adjusting all the users makes for a logically
straightforward, but fairly extensive patch.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
conf.c | 12 +++---
flow.c | 5 ++-
fwd.c | 114 +++++++++++++++++++++++++++++++--------------------------
fwd.h | 38 +++++++++++--------
tcp.c | 5 ++-
tcp.h | 12 ++++--
udp.c | 5 ++-
udp.h | 12 ++++--
8 files changed, 116 insertions(+), 87 deletions(-)
diff --git a/conf.c b/conf.c
index c436b88e..80d8106d 100644
--- a/conf.c
+++ b/conf.c
@@ -130,7 +130,7 @@ static int parse_port_range(const char *s, char **endptr,
* @c: Execution context
* @optname: Short option name, t, T, u, or U
* @optarg: Option argument (port specification)
- * @fwd: Pointer to @fwd_ports to be updated
+ * @fwd: Forwarding table to be updated
* @addr: Listening address
* @ifname: Listening interface
* @first: First port to forward
@@ -140,7 +140,7 @@ static int parse_port_range(const char *s, char **endptr,
* @flags: Flags for forwarding entries
*/
static void conf_ports_range_except(const struct ctx *c, char optname,
- const char *optarg, struct fwd_ports *fwd,
+ const char *optarg, struct fwd_table *fwd,
const union inany_addr *addr,
const char *ifname,
uint16_t first, uint16_t last,
@@ -215,11 +215,11 @@ enum fwd_mode {
* @c: Execution context
* @optname: Short option name, t, T, u, or U
* @optarg: Option argument (port specification)
- * @fwd: Pointer to @fwd_ports to be updated
+ * @fwd: Forwarding table to be updated
* @mode: Overall port forwarding mode (updated)
*/
static void conf_ports(const struct ctx *c, char optname, const char *optarg,
- struct fwd_ports *fwd, enum fwd_mode *mode)
+ struct fwd_table *fwd, enum fwd_mode *mode)
{
union inany_addr addr_buf = inany_any6, *addr = &addr_buf;
char buf[BUFSIZ], *spec, *ifname = NULL, *p;
@@ -1245,7 +1245,7 @@ dns6:
info("Outbound TCP forwarding:");
fwd_rules_print(&c->tcp.fwd_out);
info("Outbound UDP forwarding:");
- fwd_rules_print(&c->udp.fwd_out);
+ fwd_rules_print(&c->tcp.fwd_out);
}
}
@@ -2116,7 +2116,7 @@ void conf(struct ctx *c, int argc, char **argv)
if (name == 't') {
conf_ports(c, name, optarg,
- &c->tcp.fwd_in, &tcp_in_mode);
+ &c->tcp.fwd_in, &tcp_in_mode);
} else if (name == 'u') {
conf_ports(c, name, optarg,
&c->udp.fwd_in, &udp_in_mode);
diff --git a/flow.c b/flow.c
index 5207143d..4b07697e 100644
--- a/flow.c
+++ b/flow.c
@@ -505,7 +505,7 @@ struct flowside *flow_target(const struct ctx *c, union flow *flow,
const struct flowside *ini = &f->side[INISIDE];
struct flowside *tgt = &f->side[TGTSIDE];
const struct fwd_rule *rule = NULL;
- const struct fwd_ports *fwd;
+ const struct fwd_table *fwd;
uint8_t tgtpif = PIF_NONE;
ASSERT(flow_new_entry == flow && f->state == FLOW_STATE_INI);
@@ -1023,7 +1023,8 @@ static int flow_migrate_source_rollback(struct ctx *c, unsigned bound, int ret)
debug("...roll back migration");
- if (fwd_listen_sync(c, &c->tcp.fwd_in, PIF_HOST, IPPROTO_TCP) < 0)
+ if (fwd_listen_sync(c, &c->tcp.fwd_in, &c->tcp.scan_in,
+ PIF_HOST, IPPROTO_TCP) < 0)
die("Failed to re-establish listening sockets");
foreach_established_tcp_flow(flow) {
diff --git a/fwd.c b/fwd.c
index 9e455094..3e0e1063 100644
--- a/fwd.c
+++ b/fwd.c
@@ -341,7 +341,7 @@ bool fwd_port_is_ephemeral(in_port_t port)
* @last: Last port number to forward
* @to: First port of target port range to map to
*/
-void fwd_rule_add(struct fwd_ports *fwd, uint8_t flags,
+void fwd_rule_add(struct fwd_table *fwd, uint8_t flags,
const union inany_addr *addr, const char *ifname,
in_port_t first, in_port_t last, in_port_t to)
{
@@ -431,7 +431,7 @@ static bool fwd_rule_match(const struct fwd_rule *rule,
*
* Returns: first matching rule, or NULL if there is none
*/
-const struct fwd_rule *fwd_rule_search(const struct fwd_ports *fwd,
+const struct fwd_rule *fwd_rule_search(const struct fwd_table *fwd,
const struct flowside *ini,
int hint)
{
@@ -464,7 +464,7 @@ const struct fwd_rule *fwd_rule_search(const struct fwd_ports *fwd,
* fwd_rules_print() - Print forwarding rules for debugging
* @fwd: Table to print
*/
-void fwd_rules_print(const struct fwd_ports *fwd)
+void fwd_rules_print(const struct fwd_table *fwd)
{
unsigned i;
@@ -504,8 +504,8 @@ void fwd_rules_print(const struct fwd_ports *fwd)
*
* Return: 0 on success, -1 on failure
*/
-static int fwd_sync_one(const struct ctx *c,
- const struct fwd_ports *fwd, const struct fwd_rule *rule,
+static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
+ const struct fwd_rule *rule,
uint8_t pif, uint8_t proto, const uint8_t *scanmap)
{
const union inany_addr *addr = fwd_rule_addr(rule);
@@ -582,7 +582,7 @@ static int fwd_sync_one(const struct ctx *c,
/** struct fwd_listen_args - arguments for fwd_listen_init_()
* @c: Execution context
- * @fwd: Forwarding information
+ * @fwd: Forwarding table
* @scanmap: Bitmap of ports to auto-forward
* @pif: Interface to create listening sockets for
* @proto: Protocol
@@ -590,7 +590,7 @@ static int fwd_sync_one(const struct ctx *c,
*/
struct fwd_listen_args {
const struct ctx *c;
- const struct fwd_ports *fwd;
+ const struct fwd_table *fwd;
const uint8_t *scanmap;
uint8_t pif;
uint8_t proto;
@@ -612,7 +612,7 @@ static int fwd_listen_sync_(void *arg)
for (i = 0; i < a->fwd->count; i++) {
a->ret = fwd_sync_one(a->c, a->fwd, &a->fwd->rules[i],
- a->pif, a->proto, a->fwd->map);
+ a->pif, a->proto, a->scanmap);
if (a->ret < 0)
break;
}
@@ -623,16 +623,18 @@ static int fwd_listen_sync_(void *arg)
/** fwd_listen_sync() - Call fwd_listen_sync_() in correct namespace
* @c: Execution context
* @fwd: Forwarding information
+ * @scan: Scanning state for direction and protocol
* @pif: Interface to create listening sockets for
* @proto: Protocol
*
* Return: 0 on success, -1 on failure
*/
-int fwd_listen_sync(const struct ctx *c, const struct fwd_ports *fwd,
- uint8_t pif, uint8_t proto)
+int fwd_listen_sync(const struct ctx *c, const struct fwd_table *fwd,
+ const struct fwd_scan *scan, uint8_t pif, uint8_t proto)
{
struct fwd_listen_args a = {
- .c = c, .fwd = fwd, .pif = pif, .proto = proto,
+ .c = c, .fwd = fwd, .scanmap = scan->map,
+ .pif = pif, .proto = proto,
};
if (pif == PIF_SPLICE)
@@ -652,7 +654,7 @@ int fwd_listen_sync(const struct ctx *c, const struct fwd_ports *fwd,
/** fwd_listen_close() - Close all listening sockets
* @fwd: Forwarding information
*/
-void fwd_listen_close(const struct fwd_ports *fwd)
+void fwd_listen_close(const struct fwd_table *fwd)
{
unsigned i;
@@ -716,7 +718,7 @@ static void procfs_scan_listen(int fd, unsigned int lstate, uint8_t *map)
* has_scan_rules() - Does the given table have any FWD_SCAN rules?
* @fwd: Forwarding table
*/
-static bool has_scan_rules(const struct fwd_ports *fwd)
+static bool has_scan_rules(const struct fwd_table *fwd)
{
unsigned i;
@@ -729,46 +731,50 @@ static bool has_scan_rules(const struct fwd_ports *fwd)
/**
* fwd_scan_ports_tcp() - Scan /proc to update TCP forwarding map
- * @fwd: Forwarding information to update
+ * @fwd: Forwarding table
+ * @scan: Scanning state to update
* @exclude: Ports to _not_ forward
*/
-static void fwd_scan_ports_tcp(struct fwd_ports *fwd, const uint8_t *exclude)
+static void fwd_scan_ports_tcp(const struct fwd_table *fwd,
+ struct fwd_scan *scan, const uint8_t *exclude)
{
if (!has_scan_rules(fwd))
return;
- memset(fwd->map, 0, PORT_BITMAP_SIZE);
- procfs_scan_listen(fwd->scan4, TCP_LISTEN, fwd->map);
- procfs_scan_listen(fwd->scan6, TCP_LISTEN, fwd->map);
- bitmap_and_not(fwd->map, PORT_BITMAP_SIZE, fwd->map, exclude);
+ memset(scan->map, 0, PORT_BITMAP_SIZE);
+ procfs_scan_listen(scan->scan4, TCP_LISTEN, scan->map);
+ procfs_scan_listen(scan->scan6, TCP_LISTEN, scan->map);
+ bitmap_and_not(scan->map, PORT_BITMAP_SIZE, scan->map, exclude);
}
/**
* fwd_scan_ports_udp() - Scan /proc to update UDP forwarding map
- * @fwd: Forwarding information to update
- * @tcp_fwd: Corresponding TCP forwarding information
+ * @fwd: Forwarding table
+ * @scan: Scanning state to update
+ * @tcp_scan: Corresponding TCP scanning state
* @exclude: Ports to _not_ forward
*/
-static void fwd_scan_ports_udp(struct fwd_ports *fwd,
- const struct fwd_ports *tcp_fwd,
+static void fwd_scan_ports_udp(const struct fwd_table *fwd,
+ struct fwd_scan *scan,
+ const struct fwd_scan *tcp_scan,
const uint8_t *exclude)
{
if (!has_scan_rules(fwd))
return;
- memset(fwd->map, 0, PORT_BITMAP_SIZE);
- procfs_scan_listen(fwd->scan4, UDP_LISTEN, fwd->map);
- procfs_scan_listen(fwd->scan6, UDP_LISTEN, fwd->map);
+ memset(scan->map, 0, PORT_BITMAP_SIZE);
+ procfs_scan_listen(scan->scan4, UDP_LISTEN, scan->map);
+ procfs_scan_listen(scan->scan6, UDP_LISTEN, scan->map);
/* Also forward UDP ports with the same numbers as bound TCP ports.
* This is useful for a handful of protocols (e.g. iperf3) where a TCP
* control port is used to set up transfers on a corresponding UDP
* port.
*/
- procfs_scan_listen(tcp_fwd->scan4, TCP_LISTEN, fwd->map);
- procfs_scan_listen(tcp_fwd->scan6, TCP_LISTEN, fwd->map);
+ procfs_scan_listen(tcp_scan->scan4, TCP_LISTEN, scan->map);
+ procfs_scan_listen(tcp_scan->scan6, TCP_LISTEN, scan->map);
- bitmap_and_not(fwd->map, PORT_BITMAP_SIZE, fwd->map, exclude);
+ bitmap_and_not(scan->map, PORT_BITMAP_SIZE, scan->map, exclude);
}
/**
@@ -776,7 +782,7 @@ static void fwd_scan_ports_udp(struct fwd_ports *fwd,
* @map: Bitmap to populate
* @fwd: Forwarding table to consider
*/
-static void current_listen_map(uint8_t *map, const struct fwd_ports *fwd)
+static void current_listen_map(uint8_t *map, const struct fwd_table *fwd)
{
unsigned i;
@@ -807,10 +813,12 @@ static void fwd_scan_ports(struct ctx *c)
current_listen_map(excl_udp_out, &c->udp.fwd_in);
current_listen_map(excl_udp_in, &c->udp.fwd_out);
- fwd_scan_ports_tcp(&c->tcp.fwd_out, excl_tcp_out);
- fwd_scan_ports_tcp(&c->tcp.fwd_in, excl_tcp_in);
- fwd_scan_ports_udp(&c->udp.fwd_out, &c->tcp.fwd_out, excl_udp_out);
- fwd_scan_ports_udp(&c->udp.fwd_in, &c->tcp.fwd_in, excl_udp_in);
+ fwd_scan_ports_tcp(&c->tcp.fwd_out, &c->tcp.scan_out, excl_tcp_out);
+ fwd_scan_ports_tcp(&c->tcp.fwd_in, &c->tcp.scan_in, excl_tcp_in);
+ fwd_scan_ports_udp(&c->udp.fwd_out, &c->udp.scan_out,
+ &c->tcp.scan_out, excl_udp_out);
+ fwd_scan_ports_udp(&c->udp.fwd_in, &c->udp.scan_in,
+ &c->tcp.scan_in, excl_udp_in);
}
/**
@@ -821,26 +829,26 @@ void fwd_scan_ports_init(struct ctx *c)
{
const int flags = O_RDONLY | O_CLOEXEC;
- c->tcp.fwd_in.scan4 = c->tcp.fwd_in.scan6 = -1;
- c->tcp.fwd_out.scan4 = c->tcp.fwd_out.scan6 = -1;
- c->udp.fwd_in.scan4 = c->udp.fwd_in.scan6 = -1;
- c->udp.fwd_out.scan4 = c->udp.fwd_out.scan6 = -1;
+ c->tcp.scan_in.scan4 = c->tcp.scan_in.scan6 = -1;
+ c->tcp.scan_out.scan4 = c->tcp.scan_out.scan6 = -1;
+ c->udp.scan_in.scan4 = c->udp.scan_in.scan6 = -1;
+ c->udp.scan_out.scan4 = c->udp.scan_out.scan6 = -1;
if (has_scan_rules(&c->tcp.fwd_in)) {
- c->tcp.fwd_in.scan4 = open_in_ns(c, "/proc/net/tcp", flags);
- c->tcp.fwd_in.scan6 = open_in_ns(c, "/proc/net/tcp6", flags);
+ c->tcp.scan_in.scan4 = open_in_ns(c, "/proc/net/tcp", flags);
+ c->tcp.scan_in.scan6 = open_in_ns(c, "/proc/net/tcp6", flags);
}
if (has_scan_rules(&c->udp.fwd_in)) {
- c->udp.fwd_in.scan4 = open_in_ns(c, "/proc/net/udp", flags);
- c->udp.fwd_in.scan6 = open_in_ns(c, "/proc/net/udp6", flags);
+ c->udp.scan_in.scan4 = open_in_ns(c, "/proc/net/udp", flags);
+ c->udp.scan_in.scan6 = open_in_ns(c, "/proc/net/udp6", flags);
}
- if (has_scan_rules(&c->tcp.fwd_out)) {
- c->tcp.fwd_out.scan4 = open("/proc/net/tcp", flags);
- c->tcp.fwd_out.scan6 = open("/proc/net/tcp6", flags);
+ if (has_scan_rules(&c->udp.fwd_out)) {
+ c->tcp.scan_out.scan4 = open("/proc/net/tcp", flags);
+ c->tcp.scan_out.scan6 = open("/proc/net/tcp6", flags);
}
if (has_scan_rules(&c->udp.fwd_out)) {
- c->udp.fwd_out.scan4 = open("/proc/net/udp", flags);
- c->udp.fwd_out.scan6 = open("/proc/net/udp6", flags);
+ c->udp.scan_out.scan4 = open("/proc/net/udp", flags);
+ c->udp.scan_out.scan6 = open("/proc/net/udp6", flags);
}
fwd_scan_ports(c);
}
@@ -866,12 +874,16 @@ void fwd_scan_ports_timer(struct ctx *c, const struct timespec *now)
fwd_scan_ports(c);
if (!c->no_tcp) {
- fwd_listen_sync(c, &c->tcp.fwd_in, PIF_HOST, IPPROTO_TCP);
- fwd_listen_sync(c, &c->tcp.fwd_out, PIF_SPLICE, IPPROTO_TCP);
+ fwd_listen_sync(c, &c->tcp.fwd_in, &c->tcp.scan_in,
+ PIF_HOST, IPPROTO_TCP);
+ fwd_listen_sync(c, &c->tcp.fwd_out, &c->tcp.scan_out,
+ PIF_SPLICE, IPPROTO_TCP);
}
if (!c->no_udp) {
- fwd_listen_sync(c, &c->udp.fwd_in, PIF_HOST, IPPROTO_UDP);
- fwd_listen_sync(c, &c->udp.fwd_out, PIF_SPLICE, IPPROTO_UDP);
+ fwd_listen_sync(c, &c->udp.fwd_in, &c->udp.scan_in,
+ PIF_HOST, IPPROTO_UDP);
+ fwd_listen_sync(c, &c->udp.fwd_out, &c->udp.scan_out,
+ PIF_SPLICE, IPPROTO_UDP);
}
}
diff --git a/fwd.h b/fwd.h
index 6d657ddc..1af13ad4 100644
--- a/fwd.h
+++ b/fwd.h
@@ -68,8 +68,6 @@ struct fwd_listen_ref {
unsigned rule :FWD_RULE_BITS;
};
-#define PORT_BITMAP_SIZE DIV_ROUND_UP(NUM_PORTS, 8)
-
/* Maximum number of listening sockets (per pif & protocol)
*
* Rationale: This lets us listen on every port for two addresses (which we need
@@ -78,41 +76,49 @@ struct fwd_listen_ref {
#define MAX_LISTEN_SOCKS (NUM_PORTS * 3)
/**
- * fwd_ports() - Describes port forwarding for one protocol and direction
- * @scan4: /proc/net fd to scan for IPv4 ports when in AUTO mode
- * @scan6: /proc/net fd to scan for IPv6 ports when in AUTO mode
+ * struct fwd_table - Table of forwarding rules (per protocol and ini pif)
* @count: Number of forwarding rules
* @rules: Array of forwarding rules
- * @map: Bitmap describing which ports are forwarded
* @sock_count: Number of entries used in @socks
* @socks: Listening sockets for forwarding
*/
-struct fwd_ports {
- int scan4;
- int scan6;
+struct fwd_table {
unsigned count;
struct fwd_rule rules[MAX_FWD_RULES];
- uint8_t map[PORT_BITMAP_SIZE];
unsigned sock_count;
int socks[MAX_LISTEN_SOCKS];
};
+#define PORT_BITMAP_SIZE DIV_ROUND_UP(NUM_PORTS, 8)
+
+/**
+ * struct fwd_scan - Port scanning state for a protocol+direction
+ * @scan4: /proc/net fd to scan for IPv4 ports when in AUTO mode
+ * @scan6: /proc/net fd to scan for IPv6 ports when in AUTO mode
+ * @map: Bitmap describing which ports are forwarded
+ */
+struct fwd_scan {
+ int scan4;
+ int scan6;
+ uint8_t map[PORT_BITMAP_SIZE];
+};
+
#define FWD_PORT_SCAN_INTERVAL 1000 /* ms */
-void fwd_rule_add(struct fwd_ports *fwd, uint8_t flags,
+void fwd_rule_add(struct fwd_table *fwd, uint8_t flags,
const union inany_addr *addr, const char *ifname,
in_port_t first, in_port_t last, in_port_t to);
-const struct fwd_rule *fwd_rule_search(const struct fwd_ports *fwd,
+const struct fwd_rule *fwd_rule_search(const struct fwd_table *fwd,
const struct flowside *ini,
int hint);
-void fwd_rules_print(const struct fwd_ports *fwd);
+void fwd_rules_print(const struct fwd_table *fwd);
void fwd_scan_ports_init(struct ctx *c);
void fwd_scan_ports_timer(struct ctx * c, const struct timespec *now);
-int fwd_listen_sync(const struct ctx *c, const struct fwd_ports *fwd,
- uint8_t pif, uint8_t proto);
-void fwd_listen_close(const struct fwd_ports *fwd);
+int fwd_listen_sync(const struct ctx *c, const struct fwd_table *fwd,
+ const struct fwd_scan *scan, uint8_t pif, uint8_t proto);
+void fwd_listen_close(const struct fwd_table *fwd);
bool nat_inbound(const struct ctx *c, const union inany_addr *addr,
union inany_addr *translated);
diff --git a/tcp.c b/tcp.c
index a0d7cd86..29355c0c 100644
--- a/tcp.c
+++ b/tcp.c
@@ -2863,11 +2863,12 @@ int tcp_init(struct ctx *c)
tcp_sock_refill_init(c);
- if (fwd_listen_sync(c, &c->tcp.fwd_in, PIF_HOST, IPPROTO_TCP) < 0)
+ if (fwd_listen_sync(c, &c->tcp.fwd_in, &c->tcp.scan_in,
+ PIF_HOST, IPPROTO_TCP) < 0)
return -1;
if (c->mode == MODE_PASTA) {
tcp_splice_init(c);
- if (fwd_listen_sync(c, &c->tcp.fwd_out,
+ if (fwd_listen_sync(c, &c->tcp.fwd_out, &c->tcp.scan_out,
PIF_SPLICE, IPPROTO_TCP) < 0)
return -1;
}
diff --git a/tcp.h b/tcp.h
index c9455e85..c4f200c1 100644
--- a/tcp.h
+++ b/tcp.h
@@ -38,8 +38,10 @@ extern bool peek_offset_cap;
/**
* struct tcp_ctx - Execution context for TCP routines
- * @fwd_in: Port forwarding configuration for inbound packets
- * @fwd_out: Port forwarding configuration for outbound packets
+ * @fwd_in: Forwarding table for inbound flows
+ * @scan_in: Port scanning state for inbound packets
+ * @fwd_out: Forwarding table for outbound flows
+ * @scan_out: Port scanning state for outbound packets
* @timer_run: Timestamp of most recent timer run
* @pipe_size: Size of pipes for spliced connections
* @rto_max: Maximum retry timeout (in s)
@@ -49,8 +51,10 @@ extern bool peek_offset_cap;
* @inactivity_run: Time we last scanned for inactive connections
*/
struct tcp_ctx {
- struct fwd_ports fwd_in;
- struct fwd_ports fwd_out;
+ struct fwd_table fwd_in;
+ struct fwd_scan scan_in;
+ struct fwd_table fwd_out;
+ struct fwd_scan scan_out;
struct timespec timer_run;
size_t pipe_size;
int rto_max;
diff --git a/udp.c b/udp.c
index 464aa093..ed23ab7c 100644
--- a/udp.c
+++ b/udp.c
@@ -1215,12 +1215,13 @@ int udp_init(struct ctx *c)
udp_iov_init(c);
- if (fwd_listen_sync(c, &c->udp.fwd_in, PIF_HOST, IPPROTO_UDP) < 0)
+ if (fwd_listen_sync(c, &c->udp.fwd_in, &c->udp.scan_in,
+ PIF_HOST, IPPROTO_UDP) < 0)
return -1;
if (c->mode == MODE_PASTA) {
udp_splice_iov_init();
- if (fwd_listen_sync(c, &c->udp.fwd_out,
+ if (fwd_listen_sync(c, &c->udp.fwd_out, &c->udp.scan_out,
PIF_SPLICE, IPPROTO_UDP) < 0)
return -1;
}
diff --git a/udp.h b/udp.h
index 0eca1abe..785133ef 100644
--- a/udp.h
+++ b/udp.h
@@ -26,15 +26,19 @@ void udp_update_l2_buf(const unsigned char *eth_d);
/**
* struct udp_ctx - Execution context for UDP
- * @fwd_in: Port forwarding configuration for inbound packets
- * @fwd_out: Port forwarding configuration for outbound packets
+ * @fwd_in: Forwarding table for inbound flows
+ * @scan_in: Port scanning state for inbound packets
+ * @fwd_out: Forwarding table for outbound flows
+ * @scan_out: Port scanning state for outbound packets
* @timer_run: Timestamp of most recent timer run
* @timeout: Timeout for unidirectional flows (in s)
* @stream_timeout: Timeout for stream-like flows (in s)
*/
struct udp_ctx {
- struct fwd_ports fwd_in;
- struct fwd_ports fwd_out;
+ struct fwd_table fwd_in;
+ struct fwd_scan scan_in;
+ struct fwd_table fwd_out;
+ struct fwd_scan scan_out;
struct timespec timer_run;
int timeout;
int stream_timeout;
--
2.53.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 6/6] fwd: Unify TCP and UDP forwarding tables
2026-03-10 4:15 [PATCH 0/6] Unify TCP and UDP forwarding tables David Gibson
` (4 preceding siblings ...)
2026-03-10 4:16 ` [PATCH 5/6] fwd: Split forwarding table from port scanning state David Gibson
@ 2026-03-10 4:16 ` David Gibson
2026-03-10 19:33 ` Stefano Brivio
5 siblings, 1 reply; 11+ messages in thread
From: David Gibson @ 2026-03-10 4:16 UTC (permalink / raw)
To: Stefano Brivio, passt-dev; +Cc: David Gibson
Currently TCP and UDP each have their own forwarding tables. This is
awkward in a few places, where we need switch statements to select the
correct table. More importantly, it would make things awkward and messy to
extend to other protocols in future, which we're likely to want to do.
Merge the TCP and UDP tables into a single table per (source) pif, with the
protocol given in each rule entry.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
conf.c | 65 +++++++++++------------
flow.c | 22 +++-----
fwd.c | 157 ++++++++++++++++++++++++++++++++++----------------------
fwd.h | 14 +++--
passt.c | 3 ++
passt.h | 5 ++
tcp.c | 9 +---
tcp.h | 2 -
udp.c | 10 +---
udp.h | 4 --
10 files changed, 152 insertions(+), 139 deletions(-)
diff --git a/conf.c b/conf.c
index 80d8106d..00b56161 100644
--- a/conf.c
+++ b/conf.c
@@ -149,12 +149,20 @@ static void conf_ports_range_except(const struct ctx *c, char optname,
{
unsigned delta = to - first;
unsigned base, i;
+ uint8_t proto;
if (first == 0) {
die("Can't forward port 0 for option '-%c %s'",
optname, optarg);
}
+ if (optname == 't' || optname == 'T')
+ proto = IPPROTO_TCP;
+ else if (optname == 'u' || optname == 'U')
+ proto = IPPROTO_UDP;
+ else
+ ASSERT(0);
+
if (addr) {
if (!c->ifi4 && inany_v4(addr)) {
die("IPv4 is disabled, can't use -%c %s",
@@ -184,15 +192,17 @@ static void conf_ports_range_except(const struct ctx *c, char optname,
optname, optarg);
if (c->ifi4) {
- fwd_rule_add(fwd, flags, &inany_loopback4, NULL,
+ fwd_rule_add(fwd, proto, flags,
+ &inany_loopback4, NULL,
base, i - 1, base + delta);
}
if (c->ifi6) {
- fwd_rule_add(fwd, flags, &inany_loopback6, NULL,
+ fwd_rule_add(fwd, proto, flags,
+ &inany_loopback6, NULL,
base, i - 1, base + delta);
}
} else {
- fwd_rule_add(fwd, flags, addr, ifname,
+ fwd_rule_add(fwd, proto, flags, addr, ifname,
base, i - 1, base + delta);
}
base = i - 1;
@@ -1237,15 +1247,11 @@ dns6:
}
}
- info("Inbound TCP forwarding:");
- fwd_rules_print(&c->tcp.fwd_in);
- info("Inbound UDP forwarding:");
- fwd_rules_print(&c->udp.fwd_in);
+ info("Inbound forwarding:");
+ fwd_rules_print(&c->fwd_in);
if (c->mode == MODE_PASTA) {
- info("Outbound TCP forwarding:");
- fwd_rules_print(&c->tcp.fwd_out);
- info("Outbound UDP forwarding:");
- fwd_rules_print(&c->tcp.fwd_out);
+ info("Outbound forwarding:");
+ fwd_rules_print(&c->fwd_out);
}
}
@@ -2115,11 +2121,9 @@ void conf(struct ctx *c, int argc, char **argv)
name = getopt_long(argc, argv, optstring, options, NULL);
if (name == 't') {
- conf_ports(c, name, optarg,
- &c->tcp.fwd_in, &tcp_in_mode);
+ conf_ports(c, name, optarg, &c->fwd_in, &tcp_in_mode);
} else if (name == 'u') {
- conf_ports(c, name, optarg,
- &c->udp.fwd_in, &udp_in_mode);
+ conf_ports(c, name, optarg, &c->fwd_in, &udp_in_mode);
} else if (name == 'D') {
struct in6_addr dns6_tmp;
struct in_addr dns4_tmp;
@@ -2189,13 +2193,10 @@ void conf(struct ctx *c, int argc, char **argv)
do {
name = getopt_long(argc, argv, optstring, options, NULL);
- if (name == 'T') {
- conf_ports(c, name, optarg,
- &c->tcp.fwd_out, &tcp_out_mode);
- } else if (name == 'U') {
- conf_ports(c, name, optarg,
- &c->udp.fwd_out, &udp_out_mode);
- }
+ if (name == 'T')
+ conf_ports(c, name, optarg, &c->fwd_out, &tcp_out_mode);
+ else if (name == 'U')
+ conf_ports(c, name, optarg, &c->fwd_out, &udp_out_mode);
} while (name != -1);
if (!c->ifi4)
@@ -2227,24 +2228,20 @@ void conf(struct ctx *c, int argc, char **argv)
udp_out_mode = fwd_default;
if (tcp_in_mode == FWD_MODE_AUTO) {
- conf_ports_range_except(c, 't', "auto", &c->tcp.fwd_in,
- NULL, NULL, 1, NUM_PORTS - 1,
- NULL, 1, FWD_SCAN);
+ conf_ports_range_except(c, 't', "auto", &c->fwd_in, NULL, NULL,
+ 1, NUM_PORTS - 1, NULL, 1, FWD_SCAN);
}
if (tcp_out_mode == FWD_MODE_AUTO) {
- conf_ports_range_except(c, 'T', "auto", &c->tcp.fwd_out,
- NULL, "lo", 1, NUM_PORTS - 1,
- NULL, 1, FWD_SCAN);
+ conf_ports_range_except(c, 'T', "auto", &c->fwd_out, NULL, "lo",
+ 1, NUM_PORTS - 1, NULL, 1, FWD_SCAN);
}
if (udp_in_mode == FWD_MODE_AUTO) {
- conf_ports_range_except(c, 'u', "auto", &c->udp.fwd_in,
- NULL, NULL, 1, NUM_PORTS - 1,
- NULL, 1, FWD_SCAN);
+ conf_ports_range_except(c, 'u', "auto", &c->fwd_in, NULL, NULL,
+ 1, NUM_PORTS - 1, NULL, 1, FWD_SCAN);
}
if (udp_out_mode == FWD_MODE_AUTO) {
- conf_ports_range_except(c, 'U', "auto", &c->udp.fwd_out,
- NULL, "lo", 1, NUM_PORTS - 1,
- NULL, 1, FWD_SCAN);
+ conf_ports_range_except(c, 'U', "auto", &c->fwd_out, NULL, "lo",
+ 1, NUM_PORTS - 1, NULL, 1, FWD_SCAN);
}
if (!c->quiet)
diff --git a/flow.c b/flow.c
index 4b07697e..edcbf838 100644
--- a/flow.c
+++ b/flow.c
@@ -520,28 +520,18 @@ struct flowside *flow_target(const struct ctx *c, union flow *flow,
break;
case PIF_SPLICE:
- if (proto == IPPROTO_TCP)
- fwd = &c->tcp.fwd_out;
- else if (proto == IPPROTO_UDP)
- fwd = &c->udp.fwd_out;
- else
- goto nofwd;
+ fwd = &c->fwd_out;
- if (!(rule = fwd_rule_search(fwd, ini, rule_hint)))
+ if (!(rule = fwd_rule_search(fwd, ini, proto, rule_hint)))
goto norule;
tgtpif = fwd_nat_from_splice(rule, proto, ini, tgt);
break;
case PIF_HOST:
- if (proto == IPPROTO_TCP)
- fwd = &c->tcp.fwd_in;
- else if (proto == IPPROTO_UDP)
- fwd = &c->udp.fwd_in;
- else
- goto nofwd;
+ fwd = &c->fwd_in;
- if (!(rule = fwd_rule_search(fwd, ini, rule_hint)))
+ if (!(rule = fwd_rule_search(fwd, ini, proto, rule_hint)))
goto norule;
tgtpif = fwd_nat_from_host(c, rule, proto, ini, tgt);
@@ -1023,8 +1013,8 @@ static int flow_migrate_source_rollback(struct ctx *c, unsigned bound, int ret)
debug("...roll back migration");
- if (fwd_listen_sync(c, &c->tcp.fwd_in, &c->tcp.scan_in,
- PIF_HOST, IPPROTO_TCP) < 0)
+ if (fwd_listen_sync(c, &c->fwd_in, PIF_HOST,
+ &c->tcp.scan_in, &c->udp.scan_in) < 0)
die("Failed to re-establish listening sockets");
foreach_established_tcp_flow(flow) {
diff --git a/fwd.c b/fwd.c
index 3e0e1063..75605cba 100644
--- a/fwd.c
+++ b/fwd.c
@@ -334,6 +334,7 @@ bool fwd_port_is_ephemeral(in_port_t port)
/**
* fwd_rule_add() - Add a rule to a forwarding table
* @fwd: Table to add to
+ * @proto: Protocol to forward
* @flags: Flags for this entry
* @addr: Our address to forward (NULL for both 0.0.0.0 and ::)
* @ifname: Only forward from this interface name, if non-empty
@@ -341,7 +342,7 @@ bool fwd_port_is_ephemeral(in_port_t port)
* @last: Last port number to forward
* @to: First port of target port range to map to
*/
-void fwd_rule_add(struct fwd_table *fwd, uint8_t flags,
+void fwd_rule_add(struct fwd_table *fwd, uint8_t proto, uint8_t flags,
const union inany_addr *addr, const char *ifname,
in_port_t first, in_port_t last, in_port_t to)
{
@@ -363,6 +364,10 @@ void fwd_rule_add(struct fwd_table *fwd, uint8_t flags,
char newstr[INANY_ADDRSTRLEN], rulestr[INANY_ADDRSTRLEN];
struct fwd_rule *rule = &fwd->rules[i];
+ if (proto != rule->proto)
+ /* Non-conflicting protocols */
+ continue;
+
if (!inany_matches(addr, fwd_rule_addr(rule)))
/* Non-conflicting addresses */
continue;
@@ -378,6 +383,7 @@ void fwd_rule_add(struct fwd_table *fwd, uint8_t flags,
}
new = &fwd->rules[fwd->count++];
+ new->proto = proto;
new->flags = flags;
if (addr) {
@@ -413,27 +419,30 @@ void fwd_rule_add(struct fwd_table *fwd, uint8_t flags,
* fwd_rule_match() - Does a prospective flow match a given forwarding rule?
* @rule: Forwarding rule
* @ini: Initiating side flow information
+ * @proto: Protocol to match
*
* Returns: true if the rule applies to the flow, false otherwise
*/
static bool fwd_rule_match(const struct fwd_rule *rule,
- const struct flowside *ini)
+ const struct flowside *ini, uint8_t proto)
{
- return inany_matches(&ini->oaddr, fwd_rule_addr(rule)) &&
- ini->oport >= rule->first && ini->oport <= rule->last;
+ return rule->proto == proto &&
+ inany_matches(&ini->oaddr, fwd_rule_addr(rule)) &&
+ ini->oport >= rule->first && ini->oport <= rule->last;
}
/**
* fwd_rule_search() - Find a rule which matches a prospective flow
* @fwd: Forwarding table
* @ini: Initiating side flow information
+ * @proto: Protocol to match
* @hint: Index of the rule in table, if known, otherwise FWD_NO_HINT
*
* Returns: first matching rule, or NULL if there is none
*/
const struct fwd_rule *fwd_rule_search(const struct fwd_table *fwd,
const struct flowside *ini,
- int hint)
+ uint8_t proto, int hint)
{
unsigned i;
@@ -442,7 +451,7 @@ const struct fwd_rule *fwd_rule_search(const struct fwd_table *fwd,
const struct fwd_rule *rule = &fwd->rules[hint];
ASSERT((unsigned)hint < fwd->count);
- if (fwd_rule_match(rule, ini))
+ if (fwd_rule_match(rule, ini, proto))
return rule;
debug("Incorrect rule hint: %s:%hu does not match %s:%hu-%hu",
@@ -453,7 +462,7 @@ const struct fwd_rule *fwd_rule_search(const struct fwd_table *fwd,
}
for (i = 0; i < fwd->count; i++) {
- if (fwd_rule_match(&fwd->rules[i], ini))
+ if (fwd_rule_match(&fwd->rules[i], ini, proto))
return &fwd->rules[i];
}
@@ -481,13 +490,13 @@ void fwd_rules_print(const struct fwd_table *fwd)
scan = " (auto-scan)";
if (rule->first == rule->last) {
- info(" [%s]%s%s:%hu => %hu %s%s",
- addr, percent, rule->ifname,
- rule->first, rule->to, weak, scan);
+ info(" %s [%s]%s%s:%hu => %hu %s%s",
+ ipproto_name(rule->proto), addr, percent,
+ rule->ifname, rule->first, rule->to, weak, scan);
} else {
- info(" [%s]%s%s:%hu-%hu => %hu-%hu %s%s",
- addr, percent, rule->ifname,
- rule->first, rule->last,
+ info(" %s [%s]%s%s:%hu-%hu => %hu-%hu %s%s",
+ ipproto_name(rule->proto), addr, percent,
+ rule->ifname, rule->first, rule->last,
rule->to, rule->last - rule->first + rule->to,
weak, scan);
}
@@ -499,14 +508,13 @@ void fwd_rules_print(const struct fwd_table *fwd)
* @fwd: Forwarding table
* @rule: Forwarding rule
* @pif: Interface to create listening sockets for
- * @proto: Protocol to listen for
* @scanmap: Bitmap of ports to listen for on FWD_SCAN entries
*
* Return: 0 on success, -1 on failure
*/
static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
- const struct fwd_rule *rule,
- uint8_t pif, uint8_t proto, const uint8_t *scanmap)
+ const struct fwd_rule *rule, uint8_t pif,
+ const uint8_t *scanmap)
{
const union inany_addr *addr = fwd_rule_addr(rule);
const char *ifname = rule->ifname;
@@ -520,6 +528,7 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
idx = rule - fwd->rules;
ASSERT(idx < MAX_FWD_RULES);
+ ASSERT(!(rule->flags & FWD_SCAN && !scanmap));
for (port = rule->first; port <= rule->last; port++) {
int fd = rule->socks[port - rule->first];
@@ -540,9 +549,9 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
continue;
}
- if (proto == IPPROTO_TCP)
+ if (rule->proto == IPPROTO_TCP)
fd = tcp_listen(c, pif, idx, addr, ifname, port);
- else if (proto == IPPROTO_UDP)
+ else if (rule->proto == IPPROTO_UDP)
fd = udp_listen(c, pif, idx, addr, ifname, port);
else
ASSERT(0);
@@ -551,7 +560,7 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
char astr[INANY_ADDRSTRLEN];
warn("Listen failed for %s %s port %s%s%s/%u: %s",
- pif_name(pif), ipproto_name(proto),
+ pif_name(pif), ipproto_name(rule->proto),
inany_ntop(addr, astr, sizeof(astr)),
ifname ? "%" : "", ifname ? ifname : "",
port, strerror_(-fd));
@@ -570,7 +579,7 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
char astr[INANY_ADDRSTRLEN];
warn("All listens failed for %s %s %s%s%s/%u-%u",
- pif_name(pif), ipproto_name(proto),
+ pif_name(pif), ipproto_name(rule->proto),
inany_ntop(addr, astr, sizeof(astr)),
ifname ? "%" : "", ifname ? ifname : "",
rule->first, rule->last);
@@ -583,17 +592,16 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
/** struct fwd_listen_args - arguments for fwd_listen_init_()
* @c: Execution context
* @fwd: Forwarding table
- * @scanmap: Bitmap of ports to auto-forward
+ * @tcpmap: Bitmap of TCP ports to auto-forward
+ * @udpmap: Bitmap of TCP ports to auto-forward
* @pif: Interface to create listening sockets for
- * @proto: Protocol
* @ret: Return code
*/
struct fwd_listen_args {
const struct ctx *c;
const struct fwd_table *fwd;
- const uint8_t *scanmap;
+ const uint8_t *tcpmap, *udpmap;
uint8_t pif;
- uint8_t proto;
int ret;
};
@@ -611,8 +619,15 @@ static int fwd_listen_sync_(void *arg)
ns_enter(a->c);
for (i = 0; i < a->fwd->count; i++) {
+ const uint8_t *scanmap = NULL;
+
+ if (a->fwd->rules[i].proto == IPPROTO_TCP)
+ scanmap = a->tcpmap;
+ else if (a->fwd->rules[i].proto == IPPROTO_UDP)
+ scanmap = a->udpmap;
+
a->ret = fwd_sync_one(a->c, a->fwd, &a->fwd->rules[i],
- a->pif, a->proto, a->scanmap);
+ a->pif, scanmap);
if (a->ret < 0)
break;
}
@@ -623,18 +638,20 @@ static int fwd_listen_sync_(void *arg)
/** fwd_listen_sync() - Call fwd_listen_sync_() in correct namespace
* @c: Execution context
* @fwd: Forwarding information
- * @scan: Scanning state for direction and protocol
* @pif: Interface to create listening sockets for
- * @proto: Protocol
+ * @tcp: Scanning state for TCP
+ * @udp: Scanning state for UDP
*
* Return: 0 on success, -1 on failure
*/
int fwd_listen_sync(const struct ctx *c, const struct fwd_table *fwd,
- const struct fwd_scan *scan, uint8_t pif, uint8_t proto)
+ uint8_t pif,
+ const struct fwd_scan *tcp, const struct fwd_scan *udp)
{
struct fwd_listen_args a = {
- .c = c, .fwd = fwd, .scanmap = scan->map,
- .pif = pif, .proto = proto,
+ .c = c, .fwd = fwd,
+ .tcpmap = tcp->map, .udpmap = udp->map,
+ .pif = pif,
};
if (pif == PIF_SPLICE)
@@ -643,8 +660,7 @@ int fwd_listen_sync(const struct ctx *c, const struct fwd_table *fwd,
fwd_listen_sync_(&a);
if (a.ret < 0) {
- err("Couldn't listen on requested %s ports",
- ipproto_name(proto));
+ err("Couldn't listen on requested ports");
return -1;
}
@@ -672,6 +688,26 @@ void fwd_listen_close(const struct fwd_table *fwd)
}
}
+/** fwd_listen_init() - Set up listening sockets at start up
+ * @c: Execution context
+ *
+ * Return: 0 on success, -1 on failure
+ */
+int fwd_listen_init(const struct ctx *c)
+{
+ if (fwd_listen_sync(c, &c->fwd_in, PIF_HOST,
+ &c->tcp.scan_in, &c->udp.scan_in) < 0)
+ return -1;
+
+ if (c->mode == MODE_PASTA) {
+ if (fwd_listen_sync(c, &c->fwd_out, PIF_SPLICE,
+ &c->tcp.scan_out, &c->udp.scan_out) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
/* See enum in kernel's include/net/tcp_states.h */
#define UDP_LISTEN 0x07
#define TCP_LISTEN 0x0a
@@ -717,13 +753,15 @@ static void procfs_scan_listen(int fd, unsigned int lstate, uint8_t *map)
/**
* has_scan_rules() - Does the given table have any FWD_SCAN rules?
* @fwd: Forwarding table
+ * @proto: Protocol to consider
*/
-static bool has_scan_rules(const struct fwd_table *fwd)
+static bool has_scan_rules(const struct fwd_table *fwd, uint8_t proto)
{
unsigned i;
for (i = 0; i < fwd->count; i++) {
- if (fwd->rules[i].flags & FWD_SCAN)
+ if (fwd->rules[i].proto == proto &&
+ fwd->rules[i].flags & FWD_SCAN)
return true;
}
return false;
@@ -738,7 +776,7 @@ static bool has_scan_rules(const struct fwd_table *fwd)
static void fwd_scan_ports_tcp(const struct fwd_table *fwd,
struct fwd_scan *scan, const uint8_t *exclude)
{
- if (!has_scan_rules(fwd))
+ if (!has_scan_rules(fwd, IPPROTO_TCP))
return;
memset(scan->map, 0, PORT_BITMAP_SIZE);
@@ -759,7 +797,7 @@ static void fwd_scan_ports_udp(const struct fwd_table *fwd,
const struct fwd_scan *tcp_scan,
const uint8_t *exclude)
{
- if (!has_scan_rules(fwd))
+ if (!has_scan_rules(fwd, IPPROTO_UDP))
return;
memset(scan->map, 0, PORT_BITMAP_SIZE);
@@ -781,8 +819,10 @@ static void fwd_scan_ports_udp(const struct fwd_table *fwd,
* current_listen_map() - Get bitmap of which ports we're already listening on
* @map: Bitmap to populate
* @fwd: Forwarding table to consider
+ * @proto: IP protocol to consider
*/
-static void current_listen_map(uint8_t *map, const struct fwd_table *fwd)
+static void current_listen_map(uint8_t *map, const struct fwd_table *fwd,
+ uint8_t proto)
{
unsigned i;
@@ -792,6 +832,9 @@ static void current_listen_map(uint8_t *map, const struct fwd_table *fwd)
const struct fwd_rule *rule = &fwd->rules[i];
unsigned port;
+ if (rule->proto != proto)
+ continue;
+
for (port = rule->first; port <= rule->last; port++) {
if (rule->socks[port - rule->first] >= 0)
bitmap_set(map, port);
@@ -808,16 +851,16 @@ static void fwd_scan_ports(struct ctx *c)
uint8_t excl_tcp_out[PORT_BITMAP_SIZE], excl_udp_out[PORT_BITMAP_SIZE];
uint8_t excl_tcp_in[PORT_BITMAP_SIZE], excl_udp_in[PORT_BITMAP_SIZE];
- current_listen_map(excl_tcp_out, &c->tcp.fwd_in);
- current_listen_map(excl_tcp_in, &c->tcp.fwd_out);
- current_listen_map(excl_udp_out, &c->udp.fwd_in);
- current_listen_map(excl_udp_in, &c->udp.fwd_out);
+ current_listen_map(excl_tcp_out, &c->fwd_in, IPPROTO_TCP);
+ current_listen_map(excl_tcp_in, &c->fwd_out, IPPROTO_TCP);
+ current_listen_map(excl_udp_out, &c->fwd_in, IPPROTO_UDP);
+ current_listen_map(excl_udp_in, &c->fwd_out, IPPROTO_UDP);
- fwd_scan_ports_tcp(&c->tcp.fwd_out, &c->tcp.scan_out, excl_tcp_out);
- fwd_scan_ports_tcp(&c->tcp.fwd_in, &c->tcp.scan_in, excl_tcp_in);
- fwd_scan_ports_udp(&c->udp.fwd_out, &c->udp.scan_out,
+ fwd_scan_ports_tcp(&c->fwd_out, &c->tcp.scan_out, excl_tcp_out);
+ fwd_scan_ports_tcp(&c->fwd_in, &c->tcp.scan_in, excl_tcp_in);
+ fwd_scan_ports_udp(&c->fwd_out, &c->udp.scan_out,
&c->tcp.scan_out, excl_udp_out);
- fwd_scan_ports_udp(&c->udp.fwd_in, &c->udp.scan_in,
+ fwd_scan_ports_udp(&c->fwd_in, &c->udp.scan_in,
&c->tcp.scan_in, excl_udp_in);
}
@@ -834,19 +877,19 @@ void fwd_scan_ports_init(struct ctx *c)
c->udp.scan_in.scan4 = c->udp.scan_in.scan6 = -1;
c->udp.scan_out.scan4 = c->udp.scan_out.scan6 = -1;
- if (has_scan_rules(&c->tcp.fwd_in)) {
+ if (has_scan_rules(&c->fwd_in, IPPROTO_TCP)) {
c->tcp.scan_in.scan4 = open_in_ns(c, "/proc/net/tcp", flags);
c->tcp.scan_in.scan6 = open_in_ns(c, "/proc/net/tcp6", flags);
}
- if (has_scan_rules(&c->udp.fwd_in)) {
+ if (has_scan_rules(&c->fwd_in, IPPROTO_UDP)) {
c->udp.scan_in.scan4 = open_in_ns(c, "/proc/net/udp", flags);
c->udp.scan_in.scan6 = open_in_ns(c, "/proc/net/udp6", flags);
}
- if (has_scan_rules(&c->udp.fwd_out)) {
+ if (has_scan_rules(&c->fwd_out, IPPROTO_TCP)) {
c->tcp.scan_out.scan4 = open("/proc/net/tcp", flags);
c->tcp.scan_out.scan6 = open("/proc/net/tcp6", flags);
}
- if (has_scan_rules(&c->udp.fwd_out)) {
+ if (has_scan_rules(&c->fwd_out, IPPROTO_UDP)) {
c->udp.scan_out.scan4 = open("/proc/net/udp", flags);
c->udp.scan_out.scan6 = open("/proc/net/udp6", flags);
}
@@ -873,18 +916,10 @@ void fwd_scan_ports_timer(struct ctx *c, const struct timespec *now)
fwd_scan_ports(c);
- if (!c->no_tcp) {
- fwd_listen_sync(c, &c->tcp.fwd_in, &c->tcp.scan_in,
- PIF_HOST, IPPROTO_TCP);
- fwd_listen_sync(c, &c->tcp.fwd_out, &c->tcp.scan_out,
- PIF_SPLICE, IPPROTO_TCP);
- }
- if (!c->no_udp) {
- fwd_listen_sync(c, &c->udp.fwd_in, &c->udp.scan_in,
- PIF_HOST, IPPROTO_UDP);
- fwd_listen_sync(c, &c->udp.fwd_out, &c->udp.scan_out,
- PIF_SPLICE, IPPROTO_UDP);
- }
+ fwd_listen_sync(c, &c->fwd_in, PIF_HOST,
+ &c->tcp.scan_in, &c->udp.scan_in);
+ fwd_listen_sync(c, &c->fwd_out, PIF_SPLICE,
+ &c->tcp.scan_out, &c->udp.scan_out);
}
/**
diff --git a/fwd.h b/fwd.h
index 1af13ad4..f6f0fa81 100644
--- a/fwd.h
+++ b/fwd.h
@@ -31,11 +31,12 @@ bool fwd_port_is_ephemeral(in_port_t port);
* @first: First port number to forward
* @last: Last port number to forward
* @to: Target port for @first, port n goes to @to + (n - @first)
- * @socks: Array of listening sockets for this entry
+ * @proto: Protocol to forward
* @flags: Flag mask
* FWD_DUAL_STACK_ANY - match any IPv4 or IPv6 address (@addr should be ::)
* FWD_WEAK - Don't give an error if binds fail for some forwards
* FWD_SCAN - Only forward if the matching port in the target is listening
+ * @socks: Array of listening sockets for this entry
*
* FIXME: @addr and @ifname currently ignored for outbound tables
*/
@@ -45,11 +46,12 @@ struct fwd_rule {
in_port_t first;
in_port_t last;
in_port_t to;
- int *socks;
+ uint8_t proto;
#define FWD_DUAL_STACK_ANY BIT(0)
#define FWD_WEAK BIT(1)
#define FWD_SCAN BIT(2)
uint8_t flags;
+ int *socks;
};
#define FWD_RULE_BITS 8
@@ -105,20 +107,22 @@ struct fwd_scan {
#define FWD_PORT_SCAN_INTERVAL 1000 /* ms */
-void fwd_rule_add(struct fwd_table *fwd, uint8_t flags,
+void fwd_rule_add(struct fwd_table *fwd, uint8_t proto, uint8_t flags,
const union inany_addr *addr, const char *ifname,
in_port_t first, in_port_t last, in_port_t to);
const struct fwd_rule *fwd_rule_search(const struct fwd_table *fwd,
const struct flowside *ini,
- int hint);
+ uint8_t proto, int hint);
void fwd_rules_print(const struct fwd_table *fwd);
void fwd_scan_ports_init(struct ctx *c);
void fwd_scan_ports_timer(struct ctx * c, const struct timespec *now);
int fwd_listen_sync(const struct ctx *c, const struct fwd_table *fwd,
- const struct fwd_scan *scan, uint8_t pif, uint8_t proto);
+ uint8_t pif,
+ const struct fwd_scan *tcp, const struct fwd_scan *udp);
void fwd_listen_close(const struct fwd_table *fwd);
+int fwd_listen_init(const struct ctx *c);
bool nat_inbound(const struct ctx *c, const union inany_addr *addr,
union inany_addr *translated);
diff --git a/passt.c b/passt.c
index 7488a84c..fc3b89b8 100644
--- a/passt.c
+++ b/passt.c
@@ -401,6 +401,9 @@ int main(int argc, char **argv)
if ((!c.no_udp && udp_init(&c)) || (!c.no_tcp && tcp_init(&c)))
passt_exit(EXIT_FAILURE);
+ if (fwd_listen_init(&c))
+ passt_exit(EXIT_FAILURE);
+
proto_update_l2_buf(c.guest_mac);
if (c.ifi4 && !c.no_dhcp)
diff --git a/passt.h b/passt.h
index 0548dacd..b614bdf0 100644
--- a/passt.h
+++ b/passt.h
@@ -184,6 +184,8 @@ struct ip6_ctx {
* @pasta_ifn: Name of namespace interface for pasta
* @pasta_ifi: Index of namespace interface for pasta
* @pasta_conf_ns: Configure namespace after creating it
+ * @fwd_in: Forwarding table for inbound flows
+ * @fwd_out: Forwarding table for outbound flows
* @no_tcp: Disable TCP operation
* @tcp: Context for TCP protocol handler
* @no_udp: Disable UDP operation
@@ -262,6 +264,9 @@ struct ctx {
unsigned int pasta_ifi;
int pasta_conf_ns;
+ struct fwd_table fwd_in;
+ struct fwd_table fwd_out;
+
int no_tcp;
struct tcp_ctx tcp;
int no_udp;
diff --git a/tcp.c b/tcp.c
index 29355c0c..77d128da 100644
--- a/tcp.c
+++ b/tcp.c
@@ -2863,15 +2863,8 @@ int tcp_init(struct ctx *c)
tcp_sock_refill_init(c);
- if (fwd_listen_sync(c, &c->tcp.fwd_in, &c->tcp.scan_in,
- PIF_HOST, IPPROTO_TCP) < 0)
- return -1;
- if (c->mode == MODE_PASTA) {
+ if (c->mode == MODE_PASTA)
tcp_splice_init(c);
- if (fwd_listen_sync(c, &c->tcp.fwd_out, &c->tcp.scan_out,
- PIF_SPLICE, IPPROTO_TCP) < 0)
- return -1;
- }
peek_offset_cap = (!c->ifi4 || tcp_probe_peek_offset_cap(AF_INET)) &&
(!c->ifi6 || tcp_probe_peek_offset_cap(AF_INET6));
diff --git a/tcp.h b/tcp.h
index c4f200c1..c992496a 100644
--- a/tcp.h
+++ b/tcp.h
@@ -38,9 +38,7 @@ extern bool peek_offset_cap;
/**
* struct tcp_ctx - Execution context for TCP routines
- * @fwd_in: Forwarding table for inbound flows
* @scan_in: Port scanning state for inbound packets
- * @fwd_out: Forwarding table for outbound flows
* @scan_out: Port scanning state for outbound packets
* @timer_run: Timestamp of most recent timer run
* @pipe_size: Size of pipes for spliced connections
diff --git a/udp.c b/udp.c
index ed23ab7c..2275c16b 100644
--- a/udp.c
+++ b/udp.c
@@ -1215,16 +1215,8 @@ int udp_init(struct ctx *c)
udp_iov_init(c);
- if (fwd_listen_sync(c, &c->udp.fwd_in, &c->udp.scan_in,
- PIF_HOST, IPPROTO_UDP) < 0)
- return -1;
-
- if (c->mode == MODE_PASTA) {
+ if (c->mode == MODE_PASTA)
udp_splice_iov_init();
- if (fwd_listen_sync(c, &c->udp.fwd_out, &c->udp.scan_out,
- PIF_SPLICE, IPPROTO_UDP) < 0)
- return -1;
- }
return 0;
}
diff --git a/udp.h b/udp.h
index 785133ef..a75d5aea 100644
--- a/udp.h
+++ b/udp.h
@@ -26,18 +26,14 @@ void udp_update_l2_buf(const unsigned char *eth_d);
/**
* struct udp_ctx - Execution context for UDP
- * @fwd_in: Forwarding table for inbound flows
* @scan_in: Port scanning state for inbound packets
- * @fwd_out: Forwarding table for outbound flows
* @scan_out: Port scanning state for outbound packets
* @timer_run: Timestamp of most recent timer run
* @timeout: Timeout for unidirectional flows (in s)
* @stream_timeout: Timeout for stream-like flows (in s)
*/
struct udp_ctx {
- struct fwd_table fwd_in;
struct fwd_scan scan_in;
- struct fwd_table fwd_out;
struct fwd_scan scan_out;
struct timespec timer_run;
int timeout;
--
2.53.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/6] conf, fwd: Make overall forwarding mode local to conf path
2026-03-10 4:16 ` [PATCH 1/6] conf, fwd: Make overall forwarding mode local to conf path David Gibson
@ 2026-03-10 19:33 ` Stefano Brivio
2026-03-11 1:34 ` David Gibson
0 siblings, 1 reply; 11+ messages in thread
From: Stefano Brivio @ 2026-03-10 19:33 UTC (permalink / raw)
To: David Gibson; +Cc: passt-dev
On Tue, 10 Mar 2026 15:16:00 +1100
David Gibson <david@gibson.dropbear.id.au> wrote:
> The 'mode' field of struct fwd_ports records the overall forwarding mode.
> Now that runtime forwarding decisions are made based on the forwarding
> table, this is almost unused outside conf().
>
> The only exception is the auto-port scanning code, which uses it to
> determine if a port scan is necessary. We can instead derive that from the
> forwarding table itself by checking if there are any entries with the
> FWD_SCAN flag.
>
> Once that's done, make the mode purely local to conf(). While we're there
> rename the constants to FWD_MODE_* to avoid confusion with the forwarding
> rule flag bits, which are also prefixed with FWD_.
>
> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
> ---
> conf.c | 82 ++++++++++++++++++++++++++++++++++++----------------------
> fwd.c | 27 ++++++++++++++-----
> fwd.h | 10 -------
> 3 files changed, 72 insertions(+), 47 deletions(-)
>
> diff --git a/conf.c b/conf.c
> index 11d84536..c436b88e 100644
> --- a/conf.c
> +++ b/conf.c
> @@ -199,15 +199,27 @@ static void conf_ports_range_except(const struct ctx *c, char optname,
> }
> }
>
> +/**
> + * enum fwd_mode - Overall forwarding mode for a direction and protocol
> + */
Nit: I'm actually trying to document enums in the matching kerneldoc
style when it comes to it, see for example enum udp_iov_idx.
I don't think it's so much of a problem to omit it here, even though
"SPEC" and "ALL" might not be that obvious. If you respin, maybe:
* @FWD_MODE_UNSET Initial value, not parsed/configured yet
* @FWD_MODE_SPEC Forward specified ports
* @FWD_MODE_NONE No forwarded ports
* @FWD_MODE_AUTO Automatic detection and forwarding based on bound ports
* @FWD_MODE_ALL Bind all free ports
?
--
Stefano
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 6/6] fwd: Unify TCP and UDP forwarding tables
2026-03-10 4:16 ` [PATCH 6/6] fwd: Unify TCP and UDP forwarding tables David Gibson
@ 2026-03-10 19:33 ` Stefano Brivio
2026-03-11 1:39 ` David Gibson
0 siblings, 1 reply; 11+ messages in thread
From: Stefano Brivio @ 2026-03-10 19:33 UTC (permalink / raw)
To: David Gibson; +Cc: passt-dev
Two nits and one thing that might be a bit more substantial:
On Tue, 10 Mar 2026 15:16:05 +1100
David Gibson <david@gibson.dropbear.id.au> wrote:
> Currently TCP and UDP each have their own forwarding tables. This is
> awkward in a few places, where we need switch statements to select the
> correct table. More importantly, it would make things awkward and messy to
> extend to other protocols in future, which we're likely to want to do.
>
> Merge the TCP and UDP tables into a single table per (source) pif, with the
> protocol given in each rule entry.
>
> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
> ---
> conf.c | 65 +++++++++++------------
> flow.c | 22 +++-----
> fwd.c | 157 ++++++++++++++++++++++++++++++++++----------------------
> fwd.h | 14 +++--
> passt.c | 3 ++
> passt.h | 5 ++
> tcp.c | 9 +---
> tcp.h | 2 -
> udp.c | 10 +---
> udp.h | 4 --
> 10 files changed, 152 insertions(+), 139 deletions(-)
>
> diff --git a/conf.c b/conf.c
> index 80d8106d..00b56161 100644
> --- a/conf.c
> +++ b/conf.c
> @@ -149,12 +149,20 @@ static void conf_ports_range_except(const struct ctx *c, char optname,
> {
> unsigned delta = to - first;
> unsigned base, i;
> + uint8_t proto;
>
> if (first == 0) {
> die("Can't forward port 0 for option '-%c %s'",
> optname, optarg);
> }
>
> + if (optname == 't' || optname == 'T')
> + proto = IPPROTO_TCP;
> + else if (optname == 'u' || optname == 'U')
> + proto = IPPROTO_UDP;
> + else
> + ASSERT(0);
> +
> if (addr) {
> if (!c->ifi4 && inany_v4(addr)) {
> die("IPv4 is disabled, can't use -%c %s",
> @@ -184,15 +192,17 @@ static void conf_ports_range_except(const struct ctx *c, char optname,
> optname, optarg);
>
> if (c->ifi4) {
> - fwd_rule_add(fwd, flags, &inany_loopback4, NULL,
> + fwd_rule_add(fwd, proto, flags,
> + &inany_loopback4, NULL,
> base, i - 1, base + delta);
> }
> if (c->ifi6) {
> - fwd_rule_add(fwd, flags, &inany_loopback6, NULL,
> + fwd_rule_add(fwd, proto, flags,
> + &inany_loopback6, NULL,
> base, i - 1, base + delta);
> }
> } else {
> - fwd_rule_add(fwd, flags, addr, ifname,
> + fwd_rule_add(fwd, proto, flags, addr, ifname,
> base, i - 1, base + delta);
> }
> base = i - 1;
> @@ -1237,15 +1247,11 @@ dns6:
> }
> }
>
> - info("Inbound TCP forwarding:");
> - fwd_rules_print(&c->tcp.fwd_in);
> - info("Inbound UDP forwarding:");
> - fwd_rules_print(&c->udp.fwd_in);
> + info("Inbound forwarding:");
> + fwd_rules_print(&c->fwd_in);
> if (c->mode == MODE_PASTA) {
> - info("Outbound TCP forwarding:");
> - fwd_rules_print(&c->tcp.fwd_out);
> - info("Outbound UDP forwarding:");
> - fwd_rules_print(&c->tcp.fwd_out);
> + info("Outbound forwarding:");
> + fwd_rules_print(&c->fwd_out);
> }
> }
>
> @@ -2115,11 +2121,9 @@ void conf(struct ctx *c, int argc, char **argv)
> name = getopt_long(argc, argv, optstring, options, NULL);
>
> if (name == 't') {
> - conf_ports(c, name, optarg,
> - &c->tcp.fwd_in, &tcp_in_mode);
> + conf_ports(c, name, optarg, &c->fwd_in, &tcp_in_mode);
> } else if (name == 'u') {
> - conf_ports(c, name, optarg,
> - &c->udp.fwd_in, &udp_in_mode);
> + conf_ports(c, name, optarg, &c->fwd_in, &udp_in_mode);
> } else if (name == 'D') {
> struct in6_addr dns6_tmp;
> struct in_addr dns4_tmp;
> @@ -2189,13 +2193,10 @@ void conf(struct ctx *c, int argc, char **argv)
> do {
> name = getopt_long(argc, argv, optstring, options, NULL);
>
> - if (name == 'T') {
> - conf_ports(c, name, optarg,
> - &c->tcp.fwd_out, &tcp_out_mode);
> - } else if (name == 'U') {
> - conf_ports(c, name, optarg,
> - &c->udp.fwd_out, &udp_out_mode);
> - }
> + if (name == 'T')
> + conf_ports(c, name, optarg, &c->fwd_out, &tcp_out_mode);
> + else if (name == 'U')
> + conf_ports(c, name, optarg, &c->fwd_out, &udp_out_mode);
> } while (name != -1);
>
> if (!c->ifi4)
> @@ -2227,24 +2228,20 @@ void conf(struct ctx *c, int argc, char **argv)
> udp_out_mode = fwd_default;
>
> if (tcp_in_mode == FWD_MODE_AUTO) {
> - conf_ports_range_except(c, 't', "auto", &c->tcp.fwd_in,
> - NULL, NULL, 1, NUM_PORTS - 1,
> - NULL, 1, FWD_SCAN);
> + conf_ports_range_except(c, 't', "auto", &c->fwd_in, NULL, NULL,
> + 1, NUM_PORTS - 1, NULL, 1, FWD_SCAN);
> }
> if (tcp_out_mode == FWD_MODE_AUTO) {
> - conf_ports_range_except(c, 'T', "auto", &c->tcp.fwd_out,
> - NULL, "lo", 1, NUM_PORTS - 1,
> - NULL, 1, FWD_SCAN);
> + conf_ports_range_except(c, 'T', "auto", &c->fwd_out, NULL, "lo",
> + 1, NUM_PORTS - 1, NULL, 1, FWD_SCAN);
> }
> if (udp_in_mode == FWD_MODE_AUTO) {
> - conf_ports_range_except(c, 'u', "auto", &c->udp.fwd_in,
> - NULL, NULL, 1, NUM_PORTS - 1,
> - NULL, 1, FWD_SCAN);
> + conf_ports_range_except(c, 'u', "auto", &c->fwd_in, NULL, NULL,
> + 1, NUM_PORTS - 1, NULL, 1, FWD_SCAN);
> }
> if (udp_out_mode == FWD_MODE_AUTO) {
> - conf_ports_range_except(c, 'U', "auto", &c->udp.fwd_out,
> - NULL, "lo", 1, NUM_PORTS - 1,
> - NULL, 1, FWD_SCAN);
> + conf_ports_range_except(c, 'U', "auto", &c->fwd_out, NULL, "lo",
> + 1, NUM_PORTS - 1, NULL, 1, FWD_SCAN);
> }
>
> if (!c->quiet)
> diff --git a/flow.c b/flow.c
> index 4b07697e..edcbf838 100644
> --- a/flow.c
> +++ b/flow.c
> @@ -520,28 +520,18 @@ struct flowside *flow_target(const struct ctx *c, union flow *flow,
> break;
>
> case PIF_SPLICE:
> - if (proto == IPPROTO_TCP)
> - fwd = &c->tcp.fwd_out;
> - else if (proto == IPPROTO_UDP)
> - fwd = &c->udp.fwd_out;
> - else
> - goto nofwd;
> + fwd = &c->fwd_out;
>
> - if (!(rule = fwd_rule_search(fwd, ini, rule_hint)))
> + if (!(rule = fwd_rule_search(fwd, ini, proto, rule_hint)))
> goto norule;
>
> tgtpif = fwd_nat_from_splice(rule, proto, ini, tgt);
> break;
>
> case PIF_HOST:
> - if (proto == IPPROTO_TCP)
> - fwd = &c->tcp.fwd_in;
> - else if (proto == IPPROTO_UDP)
> - fwd = &c->udp.fwd_in;
> - else
> - goto nofwd;
> + fwd = &c->fwd_in;
>
> - if (!(rule = fwd_rule_search(fwd, ini, rule_hint)))
> + if (!(rule = fwd_rule_search(fwd, ini, proto, rule_hint)))
> goto norule;
>
> tgtpif = fwd_nat_from_host(c, rule, proto, ini, tgt);
> @@ -1023,8 +1013,8 @@ static int flow_migrate_source_rollback(struct ctx *c, unsigned bound, int ret)
>
> debug("...roll back migration");
>
> - if (fwd_listen_sync(c, &c->tcp.fwd_in, &c->tcp.scan_in,
> - PIF_HOST, IPPROTO_TCP) < 0)
> + if (fwd_listen_sync(c, &c->fwd_in, PIF_HOST,
> + &c->tcp.scan_in, &c->udp.scan_in) < 0)
> die("Failed to re-establish listening sockets");
>
> foreach_established_tcp_flow(flow) {
> diff --git a/fwd.c b/fwd.c
> index 3e0e1063..75605cba 100644
> --- a/fwd.c
> +++ b/fwd.c
> @@ -334,6 +334,7 @@ bool fwd_port_is_ephemeral(in_port_t port)
> /**
> * fwd_rule_add() - Add a rule to a forwarding table
> * @fwd: Table to add to
> + * @proto: Protocol to forward
> * @flags: Flags for this entry
> * @addr: Our address to forward (NULL for both 0.0.0.0 and ::)
> * @ifname: Only forward from this interface name, if non-empty
> @@ -341,7 +342,7 @@ bool fwd_port_is_ephemeral(in_port_t port)
> * @last: Last port number to forward
> * @to: First port of target port range to map to
> */
> -void fwd_rule_add(struct fwd_table *fwd, uint8_t flags,
> +void fwd_rule_add(struct fwd_table *fwd, uint8_t proto, uint8_t flags,
> const union inany_addr *addr, const char *ifname,
> in_port_t first, in_port_t last, in_port_t to)
> {
> @@ -363,6 +364,10 @@ void fwd_rule_add(struct fwd_table *fwd, uint8_t flags,
> char newstr[INANY_ADDRSTRLEN], rulestr[INANY_ADDRSTRLEN];
> struct fwd_rule *rule = &fwd->rules[i];
>
> + if (proto != rule->proto)
> + /* Non-conflicting protocols */
> + continue;
> +
> if (!inany_matches(addr, fwd_rule_addr(rule)))
> /* Non-conflicting addresses */
> continue;
> @@ -378,6 +383,7 @@ void fwd_rule_add(struct fwd_table *fwd, uint8_t flags,
> }
>
> new = &fwd->rules[fwd->count++];
> + new->proto = proto;
> new->flags = flags;
>
> if (addr) {
> @@ -413,27 +419,30 @@ void fwd_rule_add(struct fwd_table *fwd, uint8_t flags,
> * fwd_rule_match() - Does a prospective flow match a given forwarding rule?
> * @rule: Forwarding rule
> * @ini: Initiating side flow information
> + * @proto: Protocol to match
> *
> * Returns: true if the rule applies to the flow, false otherwise
> */
> static bool fwd_rule_match(const struct fwd_rule *rule,
> - const struct flowside *ini)
> + const struct flowside *ini, uint8_t proto)
> {
> - return inany_matches(&ini->oaddr, fwd_rule_addr(rule)) &&
> - ini->oport >= rule->first && ini->oport <= rule->last;
> + return rule->proto == proto &&
> + inany_matches(&ini->oaddr, fwd_rule_addr(rule)) &&
> + ini->oport >= rule->first && ini->oport <= rule->last;
Nit: we usually align things for return clauses like we do with
function calls (like in the existing version), that is, here:
return rule->proto == proto &&
inany_matches(&ini->oaddr, fwd_rule_addr(rule)) &&
ini->oport >= rule->first && ini->oport <= rule->last;
> }
>
> /**
> * fwd_rule_search() - Find a rule which matches a prospective flow
> * @fwd: Forwarding table
> * @ini: Initiating side flow information
> + * @proto: Protocol to match
> * @hint: Index of the rule in table, if known, otherwise FWD_NO_HINT
> *
> * Returns: first matching rule, or NULL if there is none
> */
> const struct fwd_rule *fwd_rule_search(const struct fwd_table *fwd,
> const struct flowside *ini,
> - int hint)
> + uint8_t proto, int hint)
> {
> unsigned i;
>
> @@ -442,7 +451,7 @@ const struct fwd_rule *fwd_rule_search(const struct fwd_table *fwd,
> const struct fwd_rule *rule = &fwd->rules[hint];
>
> ASSERT((unsigned)hint < fwd->count);
> - if (fwd_rule_match(rule, ini))
> + if (fwd_rule_match(rule, ini, proto))
> return rule;
>
> debug("Incorrect rule hint: %s:%hu does not match %s:%hu-%hu",
> @@ -453,7 +462,7 @@ const struct fwd_rule *fwd_rule_search(const struct fwd_table *fwd,
> }
>
> for (i = 0; i < fwd->count; i++) {
> - if (fwd_rule_match(&fwd->rules[i], ini))
> + if (fwd_rule_match(&fwd->rules[i], ini, proto))
> return &fwd->rules[i];
> }
>
> @@ -481,13 +490,13 @@ void fwd_rules_print(const struct fwd_table *fwd)
> scan = " (auto-scan)";
>
> if (rule->first == rule->last) {
> - info(" [%s]%s%s:%hu => %hu %s%s",
> - addr, percent, rule->ifname,
> - rule->first, rule->to, weak, scan);
> + info(" %s [%s]%s%s:%hu => %hu %s%s",
> + ipproto_name(rule->proto), addr, percent,
> + rule->ifname, rule->first, rule->to, weak, scan);
> } else {
> - info(" [%s]%s%s:%hu-%hu => %hu-%hu %s%s",
> - addr, percent, rule->ifname,
> - rule->first, rule->last,
> + info(" %s [%s]%s%s:%hu-%hu => %hu-%hu %s%s",
> + ipproto_name(rule->proto), addr, percent,
> + rule->ifname, rule->first, rule->last,
> rule->to, rule->last - rule->first + rule->to,
> weak, scan);
> }
> @@ -499,14 +508,13 @@ void fwd_rules_print(const struct fwd_table *fwd)
> * @fwd: Forwarding table
> * @rule: Forwarding rule
> * @pif: Interface to create listening sockets for
> - * @proto: Protocol to listen for
> * @scanmap: Bitmap of ports to listen for on FWD_SCAN entries
> *
> * Return: 0 on success, -1 on failure
> */
> static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
> - const struct fwd_rule *rule,
> - uint8_t pif, uint8_t proto, const uint8_t *scanmap)
> + const struct fwd_rule *rule, uint8_t pif,
> + const uint8_t *scanmap)
> {
> const union inany_addr *addr = fwd_rule_addr(rule);
> const char *ifname = rule->ifname;
> @@ -520,6 +528,7 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
>
> idx = rule - fwd->rules;
> ASSERT(idx < MAX_FWD_RULES);
> + ASSERT(!(rule->flags & FWD_SCAN && !scanmap));
>
> for (port = rule->first; port <= rule->last; port++) {
> int fd = rule->socks[port - rule->first];
> @@ -540,9 +549,9 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
> continue;
> }
>
> - if (proto == IPPROTO_TCP)
> + if (rule->proto == IPPROTO_TCP)
> fd = tcp_listen(c, pif, idx, addr, ifname, port);
> - else if (proto == IPPROTO_UDP)
> + else if (rule->proto == IPPROTO_UDP)
> fd = udp_listen(c, pif, idx, addr, ifname, port);
> else
> ASSERT(0);
> @@ -551,7 +560,7 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
> char astr[INANY_ADDRSTRLEN];
>
> warn("Listen failed for %s %s port %s%s%s/%u: %s",
> - pif_name(pif), ipproto_name(proto),
> + pif_name(pif), ipproto_name(rule->proto),
> inany_ntop(addr, astr, sizeof(astr)),
> ifname ? "%" : "", ifname ? ifname : "",
> port, strerror_(-fd));
> @@ -570,7 +579,7 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
> char astr[INANY_ADDRSTRLEN];
>
> warn("All listens failed for %s %s %s%s%s/%u-%u",
> - pif_name(pif), ipproto_name(proto),
> + pif_name(pif), ipproto_name(rule->proto),
> inany_ntop(addr, astr, sizeof(astr)),
> ifname ? "%" : "", ifname ? ifname : "",
> rule->first, rule->last);
> @@ -583,17 +592,16 @@ static int fwd_sync_one(const struct ctx *c, const struct fwd_table *fwd,
> /** struct fwd_listen_args - arguments for fwd_listen_init_()
> * @c: Execution context
> * @fwd: Forwarding table
> - * @scanmap: Bitmap of ports to auto-forward
> + * @tcpmap: Bitmap of TCP ports to auto-forward
> + * @udpmap: Bitmap of TCP ports to auto-forward
> * @pif: Interface to create listening sockets for
> - * @proto: Protocol
> * @ret: Return code
> */
> struct fwd_listen_args {
> const struct ctx *c;
> const struct fwd_table *fwd;
> - const uint8_t *scanmap;
> + const uint8_t *tcpmap, *udpmap;
> uint8_t pif;
> - uint8_t proto;
> int ret;
> };
>
> @@ -611,8 +619,15 @@ static int fwd_listen_sync_(void *arg)
> ns_enter(a->c);
>
> for (i = 0; i < a->fwd->count; i++) {
> + const uint8_t *scanmap = NULL;
> +
> + if (a->fwd->rules[i].proto == IPPROTO_TCP)
> + scanmap = a->tcpmap;
> + else if (a->fwd->rules[i].proto == IPPROTO_UDP)
> + scanmap = a->udpmap;
> +
> a->ret = fwd_sync_one(a->c, a->fwd, &a->fwd->rules[i],
> - a->pif, a->proto, a->scanmap);
> + a->pif, scanmap);
> if (a->ret < 0)
> break;
> }
> @@ -623,18 +638,20 @@ static int fwd_listen_sync_(void *arg)
> /** fwd_listen_sync() - Call fwd_listen_sync_() in correct namespace
> * @c: Execution context
> * @fwd: Forwarding information
> - * @scan: Scanning state for direction and protocol
> * @pif: Interface to create listening sockets for
> - * @proto: Protocol
> + * @tcp: Scanning state for TCP
> + * @udp: Scanning state for UDP
> *
> * Return: 0 on success, -1 on failure
> */
> int fwd_listen_sync(const struct ctx *c, const struct fwd_table *fwd,
> - const struct fwd_scan *scan, uint8_t pif, uint8_t proto)
> + uint8_t pif,
> + const struct fwd_scan *tcp, const struct fwd_scan *udp)
> {
> struct fwd_listen_args a = {
> - .c = c, .fwd = fwd, .scanmap = scan->map,
> - .pif = pif, .proto = proto,
> + .c = c, .fwd = fwd,
> + .tcpmap = tcp->map, .udpmap = udp->map,
> + .pif = pif,
> };
>
> if (pif == PIF_SPLICE)
> @@ -643,8 +660,7 @@ int fwd_listen_sync(const struct ctx *c, const struct fwd_table *fwd,
> fwd_listen_sync_(&a);
>
> if (a.ret < 0) {
> - err("Couldn't listen on requested %s ports",
> - ipproto_name(proto));
> + err("Couldn't listen on requested ports");
> return -1;
> }
>
> @@ -672,6 +688,26 @@ void fwd_listen_close(const struct fwd_table *fwd)
> }
> }
>
> +/** fwd_listen_init() - Set up listening sockets at start up
> + * @c: Execution context
> + *
> + * Return: 0 on success, -1 on failure
> + */
> +int fwd_listen_init(const struct ctx *c)
> +{
> + if (fwd_listen_sync(c, &c->fwd_in, PIF_HOST,
> + &c->tcp.scan_in, &c->udp.scan_in) < 0)
> + return -1;
> +
> + if (c->mode == MODE_PASTA) {
> + if (fwd_listen_sync(c, &c->fwd_out, PIF_SPLICE,
> + &c->tcp.scan_out, &c->udp.scan_out) < 0)
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> /* See enum in kernel's include/net/tcp_states.h */
> #define UDP_LISTEN 0x07
> #define TCP_LISTEN 0x0a
> @@ -717,13 +753,15 @@ static void procfs_scan_listen(int fd, unsigned int lstate, uint8_t *map)
> /**
> * has_scan_rules() - Does the given table have any FWD_SCAN rules?
> * @fwd: Forwarding table
> + * @proto: Protocol to consider
> */
> -static bool has_scan_rules(const struct fwd_table *fwd)
> +static bool has_scan_rules(const struct fwd_table *fwd, uint8_t proto)
> {
> unsigned i;
>
> for (i = 0; i < fwd->count; i++) {
> - if (fwd->rules[i].flags & FWD_SCAN)
> + if (fwd->rules[i].proto == proto &&
> + fwd->rules[i].flags & FWD_SCAN)
> return true;
> }
> return false;
> @@ -738,7 +776,7 @@ static bool has_scan_rules(const struct fwd_table *fwd)
> static void fwd_scan_ports_tcp(const struct fwd_table *fwd,
> struct fwd_scan *scan, const uint8_t *exclude)
> {
> - if (!has_scan_rules(fwd))
> + if (!has_scan_rules(fwd, IPPROTO_TCP))
> return;
>
> memset(scan->map, 0, PORT_BITMAP_SIZE);
> @@ -759,7 +797,7 @@ static void fwd_scan_ports_udp(const struct fwd_table *fwd,
> const struct fwd_scan *tcp_scan,
> const uint8_t *exclude)
> {
> - if (!has_scan_rules(fwd))
> + if (!has_scan_rules(fwd, IPPROTO_UDP))
> return;
>
> memset(scan->map, 0, PORT_BITMAP_SIZE);
> @@ -781,8 +819,10 @@ static void fwd_scan_ports_udp(const struct fwd_table *fwd,
> * current_listen_map() - Get bitmap of which ports we're already listening on
> * @map: Bitmap to populate
> * @fwd: Forwarding table to consider
> + * @proto: IP protocol to consider
> */
> -static void current_listen_map(uint8_t *map, const struct fwd_table *fwd)
> +static void current_listen_map(uint8_t *map, const struct fwd_table *fwd,
> + uint8_t proto)
> {
> unsigned i;
>
> @@ -792,6 +832,9 @@ static void current_listen_map(uint8_t *map, const struct fwd_table *fwd)
> const struct fwd_rule *rule = &fwd->rules[i];
> unsigned port;
>
> + if (rule->proto != proto)
> + continue;
> +
> for (port = rule->first; port <= rule->last; port++) {
> if (rule->socks[port - rule->first] >= 0)
> bitmap_set(map, port);
> @@ -808,16 +851,16 @@ static void fwd_scan_ports(struct ctx *c)
> uint8_t excl_tcp_out[PORT_BITMAP_SIZE], excl_udp_out[PORT_BITMAP_SIZE];
> uint8_t excl_tcp_in[PORT_BITMAP_SIZE], excl_udp_in[PORT_BITMAP_SIZE];
>
> - current_listen_map(excl_tcp_out, &c->tcp.fwd_in);
> - current_listen_map(excl_tcp_in, &c->tcp.fwd_out);
> - current_listen_map(excl_udp_out, &c->udp.fwd_in);
> - current_listen_map(excl_udp_in, &c->udp.fwd_out);
> + current_listen_map(excl_tcp_out, &c->fwd_in, IPPROTO_TCP);
> + current_listen_map(excl_tcp_in, &c->fwd_out, IPPROTO_TCP);
> + current_listen_map(excl_udp_out, &c->fwd_in, IPPROTO_UDP);
> + current_listen_map(excl_udp_in, &c->fwd_out, IPPROTO_UDP);
>
> - fwd_scan_ports_tcp(&c->tcp.fwd_out, &c->tcp.scan_out, excl_tcp_out);
> - fwd_scan_ports_tcp(&c->tcp.fwd_in, &c->tcp.scan_in, excl_tcp_in);
> - fwd_scan_ports_udp(&c->udp.fwd_out, &c->udp.scan_out,
> + fwd_scan_ports_tcp(&c->fwd_out, &c->tcp.scan_out, excl_tcp_out);
> + fwd_scan_ports_tcp(&c->fwd_in, &c->tcp.scan_in, excl_tcp_in);
> + fwd_scan_ports_udp(&c->fwd_out, &c->udp.scan_out,
> &c->tcp.scan_out, excl_udp_out);
> - fwd_scan_ports_udp(&c->udp.fwd_in, &c->udp.scan_in,
> + fwd_scan_ports_udp(&c->fwd_in, &c->udp.scan_in,
> &c->tcp.scan_in, excl_udp_in);
> }
>
> @@ -834,19 +877,19 @@ void fwd_scan_ports_init(struct ctx *c)
> c->udp.scan_in.scan4 = c->udp.scan_in.scan6 = -1;
> c->udp.scan_out.scan4 = c->udp.scan_out.scan6 = -1;
>
> - if (has_scan_rules(&c->tcp.fwd_in)) {
> + if (has_scan_rules(&c->fwd_in, IPPROTO_TCP)) {
> c->tcp.scan_in.scan4 = open_in_ns(c, "/proc/net/tcp", flags);
> c->tcp.scan_in.scan6 = open_in_ns(c, "/proc/net/tcp6", flags);
> }
> - if (has_scan_rules(&c->udp.fwd_in)) {
> + if (has_scan_rules(&c->fwd_in, IPPROTO_UDP)) {
> c->udp.scan_in.scan4 = open_in_ns(c, "/proc/net/udp", flags);
> c->udp.scan_in.scan6 = open_in_ns(c, "/proc/net/udp6", flags);
> }
> - if (has_scan_rules(&c->udp.fwd_out)) {
> + if (has_scan_rules(&c->fwd_out, IPPROTO_TCP)) {
> c->tcp.scan_out.scan4 = open("/proc/net/tcp", flags);
> c->tcp.scan_out.scan6 = open("/proc/net/tcp6", flags);
> }
> - if (has_scan_rules(&c->udp.fwd_out)) {
> + if (has_scan_rules(&c->fwd_out, IPPROTO_UDP)) {
> c->udp.scan_out.scan4 = open("/proc/net/udp", flags);
> c->udp.scan_out.scan6 = open("/proc/net/udp6", flags);
> }
> @@ -873,18 +916,10 @@ void fwd_scan_ports_timer(struct ctx *c, const struct timespec *now)
>
> fwd_scan_ports(c);
>
> - if (!c->no_tcp) {
> - fwd_listen_sync(c, &c->tcp.fwd_in, &c->tcp.scan_in,
> - PIF_HOST, IPPROTO_TCP);
> - fwd_listen_sync(c, &c->tcp.fwd_out, &c->tcp.scan_out,
> - PIF_SPLICE, IPPROTO_TCP);
> - }
> - if (!c->no_udp) {
> - fwd_listen_sync(c, &c->udp.fwd_in, &c->udp.scan_in,
> - PIF_HOST, IPPROTO_UDP);
> - fwd_listen_sync(c, &c->udp.fwd_out, &c->udp.scan_out,
> - PIF_SPLICE, IPPROTO_UDP);
> - }
> + fwd_listen_sync(c, &c->fwd_in, PIF_HOST,
> + &c->tcp.scan_in, &c->udp.scan_in);
> + fwd_listen_sync(c, &c->fwd_out, PIF_SPLICE,
> + &c->tcp.scan_out, &c->udp.scan_out);
> }
>
> /**
> diff --git a/fwd.h b/fwd.h
> index 1af13ad4..f6f0fa81 100644
> --- a/fwd.h
> +++ b/fwd.h
> @@ -31,11 +31,12 @@ bool fwd_port_is_ephemeral(in_port_t port);
> * @first: First port number to forward
> * @last: Last port number to forward
> * @to: Target port for @first, port n goes to @to + (n - @first)
> - * @socks: Array of listening sockets for this entry
> + * @proto: Protocol to forward
> * @flags: Flag mask
> * FWD_DUAL_STACK_ANY - match any IPv4 or IPv6 address (@addr should be ::)
> * FWD_WEAK - Don't give an error if binds fail for some forwards
> * FWD_SCAN - Only forward if the matching port in the target is listening
> + * @socks: Array of listening sockets for this entry
> *
> * FIXME: @addr and @ifname currently ignored for outbound tables
> */
> @@ -45,11 +46,12 @@ struct fwd_rule {
> in_port_t first;
> in_port_t last;
> in_port_t to;
> - int *socks;
> + uint8_t proto;
Nit: as a result, the comment to struct fwd_table should change to:
* struct fwd_table - Table of forwarding rules, per initiating pif
as those are not per-protocol anymore.
And the comment to MAX_LISTEN_SOCKS ("Maximum number of listening
sockets (per pif & protocol)") should also be changed but, at this
point, shouldn't we double that? Or at least NUM_PORTS * 5?
The rest of the series good to me, so I could apply it with the couple
of style fixes I pointed out if you agree... but I guess you should
change MAX_LISTEN_SOCKS (unless I'm missing something), so maybe a
respin would be more convenient at that point.
--
Stefano
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/6] conf, fwd: Make overall forwarding mode local to conf path
2026-03-10 19:33 ` Stefano Brivio
@ 2026-03-11 1:34 ` David Gibson
0 siblings, 0 replies; 11+ messages in thread
From: David Gibson @ 2026-03-11 1:34 UTC (permalink / raw)
To: Stefano Brivio; +Cc: passt-dev
[-- Attachment #1: Type: text/plain, Size: 2220 bytes --]
On Tue, Mar 10, 2026 at 08:33:11PM +0100, Stefano Brivio wrote:
> On Tue, 10 Mar 2026 15:16:00 +1100
> David Gibson <david@gibson.dropbear.id.au> wrote:
>
> > The 'mode' field of struct fwd_ports records the overall forwarding mode.
> > Now that runtime forwarding decisions are made based on the forwarding
> > table, this is almost unused outside conf().
> >
> > The only exception is the auto-port scanning code, which uses it to
> > determine if a port scan is necessary. We can instead derive that from the
> > forwarding table itself by checking if there are any entries with the
> > FWD_SCAN flag.
> >
> > Once that's done, make the mode purely local to conf(). While we're there
> > rename the constants to FWD_MODE_* to avoid confusion with the forwarding
> > rule flag bits, which are also prefixed with FWD_.
> >
> > Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
> > ---
> > conf.c | 82 ++++++++++++++++++++++++++++++++++++----------------------
> > fwd.c | 27 ++++++++++++++-----
> > fwd.h | 10 -------
> > 3 files changed, 72 insertions(+), 47 deletions(-)
> >
> > diff --git a/conf.c b/conf.c
> > index 11d84536..c436b88e 100644
> > --- a/conf.c
> > +++ b/conf.c
> > @@ -199,15 +199,27 @@ static void conf_ports_range_except(const struct ctx *c, char optname,
> > }
> > }
> >
> > +/**
> > + * enum fwd_mode - Overall forwarding mode for a direction and protocol
> > + */
>
> Nit: I'm actually trying to document enums in the matching kerneldoc
> style when it comes to it, see for example enum udp_iov_idx.
>
> I don't think it's so much of a problem to omit it here, even though
> "SPEC" and "ALL" might not be that obvious. If you respin, maybe:
>
> * @FWD_MODE_UNSET Initial value, not parsed/configured yet
> * @FWD_MODE_SPEC Forward specified ports
> * @FWD_MODE_NONE No forwarded ports
> * @FWD_MODE_AUTO Automatic detection and forwarding based on bound ports
> * @FWD_MODE_ALL Bind all free ports
>
> ?
Done.
--
David Gibson (he or they) | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you, not the other way
| around.
http://www.ozlabs.org/~dgibson
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 6/6] fwd: Unify TCP and UDP forwarding tables
2026-03-10 19:33 ` Stefano Brivio
@ 2026-03-11 1:39 ` David Gibson
0 siblings, 0 replies; 11+ messages in thread
From: David Gibson @ 2026-03-11 1:39 UTC (permalink / raw)
To: Stefano Brivio; +Cc: passt-dev
[-- Attachment #1: Type: text/plain, Size: 3698 bytes --]
On Tue, Mar 10, 2026 at 08:33:17PM +0100, Stefano Brivio wrote:
> Two nits and one thing that might be a bit more substantial:
>
> On Tue, 10 Mar 2026 15:16:05 +1100
> David Gibson <david@gibson.dropbear.id.au> wrote:
>
> > Currently TCP and UDP each have their own forwarding tables. This is
> > awkward in a few places, where we need switch statements to select the
> > correct table. More importantly, it would make things awkward and messy to
> > extend to other protocols in future, which we're likely to want to do.
> >
> > Merge the TCP and UDP tables into a single table per (source) pif, with the
> > protocol given in each rule entry.
> >
> > Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
[snip]
> > static bool fwd_rule_match(const struct fwd_rule *rule,
> > - const struct flowside *ini)
> > + const struct flowside *ini, uint8_t proto)
> > {
> > - return inany_matches(&ini->oaddr, fwd_rule_addr(rule)) &&
> > - ini->oport >= rule->first && ini->oport <= rule->last;
> > + return rule->proto == proto &&
> > + inany_matches(&ini->oaddr, fwd_rule_addr(rule)) &&
> > + ini->oport >= rule->first && ini->oport <= rule->last;
>
> Nit: we usually align things for return clauses like we do with
> function calls (like in the existing version), that is, here:
>
> return rule->proto == proto &&
> inany_matches(&ini->oaddr, fwd_rule_addr(rule)) &&
> ini->oport >= rule->first && ini->oport <= rule->last;
Ah, right. That's one of the handful of cases where I didn't figure
out how to make emacs auto-indent match our style. I tend to forget
because it doesn't arise very often
[snip]
> > diff --git a/fwd.h b/fwd.h
> > index 1af13ad4..f6f0fa81 100644
> > --- a/fwd.h
> > +++ b/fwd.h
> > @@ -31,11 +31,12 @@ bool fwd_port_is_ephemeral(in_port_t port);
> > * @first: First port number to forward
> > * @last: Last port number to forward
> > * @to: Target port for @first, port n goes to @to + (n - @first)
> > - * @socks: Array of listening sockets for this entry
> > + * @proto: Protocol to forward
> > * @flags: Flag mask
> > * FWD_DUAL_STACK_ANY - match any IPv4 or IPv6 address (@addr should be ::)
> > * FWD_WEAK - Don't give an error if binds fail for some forwards
> > * FWD_SCAN - Only forward if the matching port in the target is listening
> > + * @socks: Array of listening sockets for this entry
> > *
> > * FIXME: @addr and @ifname currently ignored for outbound tables
> > */
> > @@ -45,11 +46,12 @@ struct fwd_rule {
> > in_port_t first;
> > in_port_t last;
> > in_port_t to;
> > - int *socks;
> > + uint8_t proto;
>
> Nit: as a result, the comment to struct fwd_table should change to:
>
> * struct fwd_table - Table of forwarding rules, per initiating pif
>
> as those are not per-protocol anymore.
Good point, done.
>
> And the comment to MAX_LISTEN_SOCKS ("Maximum number of listening
> sockets (per pif & protocol)") should also be changed but, at this
> point, shouldn't we double that? Or at least NUM_PORTS * 5?
Oh, good catch, I didn't think of that. I've updated it to NUM_PORTS
* 5, and fixed the comment.
> The rest of the series good to me, so I could apply it with the couple
> of style fixes I pointed out if you agree... but I guess you should
> change MAX_LISTEN_SOCKS (unless I'm missing something), so maybe a
> respin would be more convenient at that point.
Will do.
--
David Gibson (he or they) | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you, not the other way
| around.
http://www.ozlabs.org/~dgibson
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-03-11 1:39 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-03-10 4:15 [PATCH 0/6] Unify TCP and UDP forwarding tables David Gibson
2026-03-10 4:16 ` [PATCH 1/6] conf, fwd: Make overall forwarding mode local to conf path David Gibson
2026-03-10 19:33 ` Stefano Brivio
2026-03-11 1:34 ` David Gibson
2026-03-10 4:16 ` [PATCH 2/6] tcp: Remove stale description of port_to_tap field David Gibson
2026-03-10 4:16 ` [PATCH 3/6] fwd: Don't initialise unused port bitmaps David Gibson
2026-03-10 4:16 ` [PATCH 4/6] Fix misnamed field in struct ctx comments David Gibson
2026-03-10 4:16 ` [PATCH 5/6] fwd: Split forwarding table from port scanning state David Gibson
2026-03-10 4:16 ` [PATCH 6/6] fwd: Unify TCP and UDP forwarding tables David Gibson
2026-03-10 19:33 ` Stefano Brivio
2026-03-11 1:39 ` 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).