[PATCH 09/18] fwd: Split rule building from rule adding

David Gibson
2026-04-07T03:16:42Z

Currently fwd_rule_add() both builds the struct fwd_rule and inserts it
into the table. This will be inconvenient when we want to dynamically add
rules from a configuration client. Alter fwd_rule_add() to take a
pre-constructed struct fwd_rule, which we build in the caller.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
conf.c | 42 ++++++++++++++++++++++++++++++++--------
fwd.c | 61 ++++++++++++++--------------------------------------------
fwd.h | 4 +---
3 files changed, 49 insertions(+), 58 deletions(-)

diff --git a/conf.c b/conf.c
index 23125d2c..c9ee8c59 100644
--- a/conf.c
+++ b/conf.c
@@ -151,9 +151,26 @@ static void conf_ports_range_except(const struct ctx *c, char optname,
const uint8_t *exclude, uint16_t to,
uint8_t flags)
{
+ struct fwd_rule rule = {
+ .addr = addr ? *addr : inany_any6,
+ .ifname = { 0 },
+ .proto = proto,
+ .flags = flags,
+ };
unsigned delta = to - first;
unsigned base, i;

+ if (!addr)
+ rule.flags |= FWD_DUAL_STACK_ANY;
+ if (ifname) {
+ int ret;
+
+ ret = snprintf(rule.ifname, sizeof(rule.ifname),
+ "%s", ifname);
+ if (ret <= 0 || (size_t)ret >= sizeof(rule.ifname))
+ die("Invalid interface name: %s", ifname);
+ }
+
assert(first != 0);

for (base = first; base <= last; base++) {
@@ -165,28 +182,37 @@ static void conf_ports_range_except(const struct ctx *c, char optname,
break;
}

+ rule.first = base;
+ rule.last = i - 1;
+ rule.to = base + delta;
+
if ((optname == 'T' || optname == 'U') && c->no_bindtodevice) {
/* FIXME: Once the fwd bitmaps are removed, move this
* workaround to the caller
*/
+ struct fwd_rule rulev = {
+ .ifname = { 0 },
+ .flags = flags,
+ .first = base,
+ .last = i - 1,
+ .to = base + delta,
+ };
+
assert(!addr && ifname && !strcmp(ifname, "lo"));
warn(
"SO_BINDTODEVICE unavailable, forwarding only 127.0.0.1 and ::1 for '-%c %s'",
optname, optarg);

if (c->ifi4) {
- fwd_rule_add(fwd, proto, flags,
- &inany_loopback4, NULL,
- base, i - 1, base + delta);
+ rulev.addr = inany_loopback4;
+ fwd_rule_add(fwd, &rulev);
}
if (c->ifi6) {
- fwd_rule_add(fwd, proto, flags,
- &inany_loopback6, NULL,
- base, i - 1, base + delta);
+ rulev.addr = inany_loopback6;
+ fwd_rule_add(fwd, &rulev);
}
} else {
- fwd_rule_add(fwd, proto, flags, addr, ifname,
- base, i - 1, base + delta);
+ fwd_rule_add(fwd, &rule);
}
base = i - 1;
}
diff --git a/fwd.c b/fwd.c
index 39a14c40..c05107d1 100644
--- a/fwd.c
+++ b/fwd.c
@@ -332,30 +332,22 @@ void fwd_rule_init(struct ctx *c)
}

/**
- * fwd_rule_add() - Add a rule to a forwarding table
+ * fwd_rule_add() - Validate and 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
- * @first: First port number to forward
- * @last: Last port number to forward
- * @to: First port of target port range to map to
+ * @new: Rule to add
*/
-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)
+void fwd_rule_add(struct fwd_table *fwd, const struct fwd_rule *new)
{
/* Flags which can be set from the caller */
const uint8_t allowed_flags = FWD_WEAK | FWD_SCAN | FWD_DUAL_STACK_ANY;
- unsigned num = (unsigned)last - first + 1;
- struct fwd_rule *new;
+ unsigned num = (unsigned)new->last - new->first + 1;
unsigned i, port;

- assert(!(flags & ~allowed_flags));
+ assert(!(new->flags & ~allowed_flags));
/* Passing a non-wildcard address with DUAL_STACK_ANY is a bug */
- assert(!(flags & FWD_DUAL_STACK_ANY) || !addr ||
- inany_equals(addr, &inany_any6));
+ assert(!(new->flags & FWD_DUAL_STACK_ANY) ||
+ inany_equals(&new->addr, &inany_any6));
+ assert(new->first <= new->last);

if (fwd->count >= ARRAY_SIZE(fwd->rules))
die("Too many port forwarding ranges");
@@ -367,55 +359,30 @@ void fwd_rule_add(struct fwd_table *fwd, uint8_t proto, uint8_t flags,
char newstr[INANY_ADDRSTRLEN], rulestr[INANY_ADDRSTRLEN];
const struct fwd_rule *rule = &fwd->rules[i];

- if (proto != rule->proto)
+ if (new->proto != rule->proto)
/* Non-conflicting protocols */
continue;

- if (!inany_matches(addr, fwd_rule_addr(rule)))
+ if (!inany_matches(fwd_rule_addr(new), fwd_rule_addr(rule)))
/* Non-conflicting addresses */
continue;

- if (last < rule->first || rule->last < first)
+ if (new->last < rule->first || rule->last < new->first)
/* Port ranges don't overlap */
continue;

die("Forwarding configuration conflict: %s/%u-%u versus %s/%u-%u",
- inany_ntop(addr, newstr, sizeof(newstr)), first, last,
+ inany_ntop(fwd_rule_addr(new), newstr, sizeof(newstr)),
+ new->first, new->last,
inany_ntop(fwd_rule_addr(rule), rulestr, sizeof(rulestr)),
rule->first, rule->last);
}

- new = &fwd->rules[fwd->count];
- new->proto = proto;
- new->flags = flags;
-
- if (addr) {
- new->addr = *addr;
- } else {
- new->addr = inany_any6;
- new->flags |= FWD_DUAL_STACK_ANY;
- }
-
- memset(new->ifname, 0, sizeof(new->ifname));
- if (ifname) {
- int ret;
-
- ret = snprintf(new->ifname, sizeof(new->ifname),
- "%s", ifname);
- if (ret <= 0 || (size_t)ret >= sizeof(new->ifname))
- die("Invalid interface name: %s", ifname);
- }
-
- assert(first <= last);
- new->first = first;
- new->last = last;
- new->to = to;
-
fwd->rulesocks[fwd->count] = &fwd->socks[fwd->sock_count];
for (port = new->first; port <= new->last; port++)
fwd->rulesocks[fwd->count][port - new->first] = -1;

- fwd->count++;
+ fwd->rules[fwd->count++] = *new;
fwd->sock_count += num;
}

diff --git a/fwd.h b/fwd.h
index c5f6d554..1d74cbd2 100644
--- a/fwd.h
+++ b/fwd.h
@@ -83,9 +83,7 @@ struct fwd_scan {
#define FWD_PORT_SCAN_INTERVAL 1000 /* ms */

void fwd_rule_init(struct ctx *c);
-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);
+void fwd_rule_add(struct fwd_table *fwd, const struct fwd_rule *new);
const struct fwd_rule *fwd_rule_search(const struct fwd_table *fwd,
const struct flowside *ini,
uint8_t proto, int hint);
--
2.53.0