From: David Gibson <david@gibson.dropbear.id.au>
To: passt-dev@passt.top, Stefano Brivio <sbrivio@redhat.com>
Cc: David Gibson <david@gibson.dropbear.id.au>
Subject: [PATCH v2 6/9] fwd: Unify TCP and UDP forwarding tables
Date: Wed, 11 Mar 2026 23:03:11 +1100 [thread overview]
Message-ID: <20260311120314.933546-7-david@gibson.dropbear.id.au> (raw)
In-Reply-To: <20260311120314.933546-1-david@gibson.dropbear.id.au>
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 | 24 +++------
fwd.c | 155 ++++++++++++++++++++++++++++++++++----------------------
fwd.h | 25 +++++----
passt.c | 3 ++
passt.h | 5 ++
tcp.c | 9 +---
tcp.h | 4 --
udp.c | 10 +---
udp.h | 4 --
10 files changed, 158 insertions(+), 146 deletions(-)
diff --git a/conf.c b/conf.c
index 4f5a6dba..96aa506d 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;
@@ -1242,15 +1252,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);
}
}
@@ -2120,11 +2126,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;
@@ -2194,13 +2198,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)
@@ -2232,24 +2233,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..735d3c5f 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) {
@@ -1158,7 +1148,7 @@ int flow_migrate_source(struct ctx *c, const struct migrate_stage *stage,
* fix that is to not allow local to local migration, which arguably we
* should (use namespaces for testing instead). */
debug("Stop listen()s");
- fwd_listen_close(&c->tcp.fwd_in);
+ fwd_listen_close(&c->fwd_in);
debug("Sending %u flows", ntohl(count));
diff --git a/fwd.c b/fwd.c
index 3e0e1063..1843ec8b 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,13 +419,15 @@ 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)) &&
+ return rule->proto == proto &&
+ inany_matches(&ini->oaddr, fwd_rule_addr(rule)) &&
ini->oport >= rule->first && ini->oport <= rule->last;
}
@@ -427,13 +435,14 @@ static bool fwd_rule_match(const struct fwd_rule *rule,
* 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..958eee25 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
@@ -68,15 +70,16 @@ struct fwd_listen_ref {
unsigned rule :FWD_RULE_BITS;
};
-/* Maximum number of listening sockets (per pif & protocol)
+/* Maximum number of listening sockets (per pif)
*
- * Rationale: This lets us listen on every port for two addresses (which we need
- * for -T auto without SO_BINDTODEVICE), plus a comfortable number of extras.
+ * Rationale: This lets us listen on every port for two addresses and two
+ * protocols (which we need for -T auto -U auto without SO_BINDTODEVICE), plus a
+ * comfortable number of extras.
*/
-#define MAX_LISTEN_SOCKS (NUM_PORTS * 3)
+#define MAX_LISTEN_SOCKS (NUM_PORTS * 5)
/**
- * struct fwd_table - Table of forwarding rules (per protocol and ini pif)
+ * struct fwd_table - Table of forwarding rules (per initiating pif)
* @count: Number of forwarding rules
* @rules: Array of forwarding rules
* @sock_count: Number of entries used in @socks
@@ -105,20 +108,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 976bfdb2..9d91c3c8 100644
--- a/tcp.c
+++ b/tcp.c
@@ -2864,15 +2864,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..8a0eb930 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
@@ -51,9 +49,7 @@ extern bool peek_offset_cap;
* @inactivity_run: Time we last scanned for inactive connections
*/
struct tcp_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;
size_t pipe_size;
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
next prev parent reply other threads:[~2026-03-11 12:03 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-11 12:03 [PATCH v2 0/9] " David Gibson
2026-03-11 12:03 ` [PATCH v2 1/9] conf, fwd: Make overall forwarding mode local to conf path David Gibson
2026-03-11 12:03 ` [PATCH v2 2/9] tcp: Remove stale description of port_to_tap field David Gibson
2026-03-11 12:03 ` [PATCH v2 3/9] fwd: Don't initialise unused port bitmaps David Gibson
2026-03-11 12:03 ` [PATCH v2 4/9] Fix misnamed field in struct ctx comments David Gibson
2026-03-11 12:03 ` [PATCH v2 5/9] fwd: Split forwarding table from port scanning state David Gibson
2026-03-11 12:03 ` David Gibson [this message]
2026-03-11 12:03 ` [PATCH v2 7/9] fwd: Always open /proc/net{tcp,tcp6,udp,udp6} in pasta mode David Gibson
2026-03-11 12:03 ` [PATCH v2 8/9] conf: Don't defer handling of --dns option David Gibson
2026-03-11 12:03 ` [PATCH v2 9/9] conf: Parse all forwarding options at the same time David Gibson
2026-03-11 21:12 ` [PATCH v2 0/9] Unify TCP and UDP forwarding tables Stefano Brivio
2026-03-11 23:33 ` David Gibson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260311120314.933546-7-david@gibson.dropbear.id.au \
--to=david@gibson.dropbear.id.au \
--cc=passt-dev@passt.top \
--cc=sbrivio@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://passt.top/passt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).