From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=none (p=none dis=none) header.from=gibson.dropbear.id.au Authentication-Results: passt.top; dkim=pass (2048-bit key; secure) header.d=gibson.dropbear.id.au header.i=@gibson.dropbear.id.au header.a=rsa-sha256 header.s=202512 header.b=OEfjNjLj; dkim-atps=neutral Received: from mail.ozlabs.org (gandalf.ozlabs.org [150.107.74.76]) by passt.top (Postfix) with ESMTPS id 3D49B5A077D for ; Fri, 16 Jan 2026 01:59:36 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gibson.dropbear.id.au; s=202512; t=1768525168; bh=nhEMe+QFO1kKJzzSczVOV5aZFOh9hEJoyQ8dD3Sv5SI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OEfjNjLjmcs/qgEBt9ET5LLGZja3gcShIuYCdpxoNBFE9Lyqa+ZDcbQD2tWQks3xX hEsnglr0SA8vBFm8KIKPZJttPIY13qTaBQDgVIbK5AwLwU1AkZATd/sk2vSX9c+uTy hRhQc+xnfDopIpugy5ejrT8GDJoQy5Km3Mg41EIv5mZQ4q/f57Qf5hQ7u+D3uvuPCf xMpvbJYVUepo5LInMaOySZhGkAVC1Croe2o9esijGEk7PBwNU4jabG2AcdMRbm0C8U ZiUzUSETMOfVdd1fAo7pdgyNaYwjA3ravDXpipNnksRUFVmNp+eV0aZ0iII2nKa3Ho kkKDbZ9Ke1u6w== Received: by gandalf.ozlabs.org (Postfix, from userid 1007) id 4dshMN6cqXz4wMG; Fri, 16 Jan 2026 11:59:28 +1100 (AEDT) From: David Gibson To: passt-dev@passt.top, Stefano Brivio Subject: [PATCH v5 12/14] fwd: Remap ports based directly on forwarding rule Date: Fri, 16 Jan 2026 11:59:24 +1100 Message-ID: <20260116005926.616085-13-david@gibson.dropbear.id.au> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116005926.616085-1-david@gibson.dropbear.id.au> References: <20260116005926.616085-1-david@gibson.dropbear.id.au> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Message-ID-Hash: 2UM2RFQIKPXMT6XOUXW2IHI2VDIDVP4X X-Message-ID-Hash: 2UM2RFQIKPXMT6XOUXW2IHI2VDIDVP4X X-MailFrom: dgibson@gandalf.ozlabs.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: David Gibson X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Currently we remap port numbers based on the legacy delta[] array, which is indexed only by original port number, not the listening address. Now that we look up a forwarding rule entry in flow_target(), we can use this entry to directly determine the correct remapped port. Implement this, and remove the old delta[] array. Link: https://bugs.passt.top/show_bug.cgi?id=187 Signed-off-by: David Gibson --- flow.c | 57 +++++++++++++++++++++++++++++++-------------------------- fwd.c | 21 +++++++-------------- fwd.h | 7 +++---- 3 files changed, 41 insertions(+), 44 deletions(-) diff --git a/flow.c b/flow.c index 0d97f161..99e4f7f6 100644 --- a/flow.c +++ b/flow.c @@ -530,6 +530,8 @@ struct flowside *flow_target(const struct ctx *c, union flow *flow, struct flow_common *f = &flow->f; const struct flowside *ini = &f->side[INISIDE]; struct flowside *tgt = &f->side[TGTSIDE]; + const struct fwd_rule *rule = NULL; + const struct fwd_ports *fwd; uint8_t tgtpif = PIF_NONE; ASSERT(flow_new_entry == flow && f->state == FLOW_STATE_INI); @@ -537,30 +539,6 @@ struct flowside *flow_target(const struct ctx *c, union flow *flow, ASSERT(f->pif[INISIDE] != PIF_NONE && f->pif[TGTSIDE] == PIF_NONE); ASSERT(flow->f.state == FLOW_STATE_INI); - if (pif_is_socket(f->pif[INISIDE])) { - const struct fwd_ports *fwd; - - if (f->pif[INISIDE] == PIF_HOST && proto == IPPROTO_TCP) - fwd = &c->tcp.fwd_in; - else if (f->pif[INISIDE] == PIF_HOST && proto == IPPROTO_UDP) - fwd = &c->udp.fwd_in; - else if (f->pif[INISIDE] == PIF_SPLICE && proto == IPPROTO_TCP) - fwd = &c->tcp.fwd_out; - else if (f->pif[INISIDE] == PIF_SPLICE && proto == IPPROTO_UDP) - fwd = &c->udp.fwd_out; - else - goto nofwd; - - if (!fwd_rule_search(fwd, ini)) { - /* This shouldn't happen, because if there's no rule for - * it we should have no listening socket that would let - * us get here - */ - flow_dbg(flow, "Unexpected missing forward rule"); - goto nofwd; - } - } - switch (f->pif[INISIDE]) { case PIF_TAP: memcpy(f->tap_omac, MAC_UNDEF, ETH_ALEN); @@ -568,11 +546,31 @@ struct flowside *flow_target(const struct ctx *c, union flow *flow, break; case PIF_SPLICE: - tgtpif = fwd_nat_from_splice(c, proto, ini, tgt); + if (proto == IPPROTO_TCP) + fwd = &c->tcp.fwd_out; + else if (proto == IPPROTO_UDP) + fwd = &c->udp.fwd_out; + else + goto nofwd; + + if (!(rule = fwd_rule_search(fwd, ini))) + goto norule; + + tgtpif = fwd_nat_from_splice(rule, proto, ini, tgt); break; case PIF_HOST: - tgtpif = fwd_nat_from_host(c, proto, ini, tgt); + if (proto == IPPROTO_TCP) + fwd = &c->tcp.fwd_in; + else if (proto == IPPROTO_UDP) + fwd = &c->udp.fwd_in; + else + goto nofwd; + + if (!(rule = fwd_rule_search(fwd, ini))) + goto norule; + + tgtpif = fwd_nat_from_host(c, rule, proto, ini, tgt); fwd_neigh_mac_get(c, &tgt->oaddr, f->tap_omac); break; default: @@ -586,6 +584,13 @@ struct flowside *flow_target(const struct ctx *c, union flow *flow, flow_set_state(f, FLOW_STATE_TGT); return tgt; +norule: + /* This shouldn't happen, because if there's no rule for it we should + * have no listening socket that would let us get here + */ + flow_dbg(flow, "Missing forward rule"); + flow_log_details_(f, LOG_DEBUG, f->state); + nofwd: flow_err(flow, "No rules to forward %s %s [%s]:%hu -> [%s]:%hu", pif_name(f->pif[INISIDE]), ipproto_name(proto), diff --git a/fwd.c b/fwd.c index 4b83f2f9..595fe4b1 100644 --- a/fwd.c +++ b/fwd.c @@ -411,7 +411,6 @@ void fwd_rule_add(struct fwd_ports *fwd, uint8_t flags, /* Fill in the legacy forwarding data structures to match the table */ if (!(new->flags & FWD_SCAN)) bitmap_set(fwd->map, port); - fwd->delta[port] = new->to - new->first; } } @@ -984,7 +983,7 @@ uint8_t fwd_nat_from_tap(const struct ctx *c, uint8_t proto, /** * fwd_nat_from_splice() - Determine to forward a flow from the splice interface - * @c: Execution context + * @rule: Forwarding rule to apply * @proto: Protocol (IP L4 protocol number) * @ini: Flow address information of the initiating side * @tgt: Flow address information on the target side (updated) @@ -992,7 +991,7 @@ uint8_t fwd_nat_from_tap(const struct ctx *c, uint8_t proto, * Return: pif of the target interface to forward the flow to, PIF_NONE if the * flow cannot or should not be forwarded at all. */ -uint8_t fwd_nat_from_splice(const struct ctx *c, uint8_t proto, +uint8_t fwd_nat_from_splice(const struct fwd_rule *rule, uint8_t proto, const struct flowside *ini, struct flowside *tgt) { if (!inany_is_loopback(&ini->eaddr) || @@ -1016,11 +1015,7 @@ uint8_t fwd_nat_from_splice(const struct ctx *c, uint8_t proto, /* But for UDP preserve the source port */ tgt->oport = ini->eport; - tgt->eport = ini->oport; - if (proto == IPPROTO_TCP) - tgt->eport += c->tcp.fwd_out.delta[tgt->eport]; - else if (proto == IPPROTO_UDP) - tgt->eport += c->udp.fwd_out.delta[tgt->eport]; + tgt->eport = rule->to + (ini->oport - rule->first); return PIF_HOST; } @@ -1064,6 +1059,7 @@ bool nat_inbound(const struct ctx *c, const union inany_addr *addr, /** * fwd_nat_from_host() - Determine to forward a flow from the host interface * @c: Execution context + * @rule: Forwarding rule to apply * @proto: Protocol (IP L4 protocol number) * @ini: Flow address information of the initiating side * @tgt: Flow address information on the target side (updated) @@ -1071,15 +1067,12 @@ bool nat_inbound(const struct ctx *c, const union inany_addr *addr, * Return: pif of the target interface to forward the flow to, PIF_NONE if the * flow cannot or should not be forwarded at all. */ -uint8_t fwd_nat_from_host(const struct ctx *c, uint8_t proto, +uint8_t fwd_nat_from_host(const struct ctx *c, + const struct fwd_rule *rule, uint8_t proto, const struct flowside *ini, struct flowside *tgt) { /* Common for spliced and non-spliced cases */ - tgt->eport = ini->oport; - if (proto == IPPROTO_TCP) - tgt->eport += c->tcp.fwd_in.delta[tgt->eport]; - else if (proto == IPPROTO_UDP) - tgt->eport += c->udp.fwd_in.delta[tgt->eport]; + tgt->eport = rule->to + (ini->oport - rule->first); if (!c->no_splice && inany_is_loopback(&ini->eaddr) && (proto == IPPROTO_TCP || proto == IPPROTO_UDP)) { diff --git a/fwd.h b/fwd.h index 954db021..184c1767 100644 --- a/fwd.h +++ b/fwd.h @@ -85,7 +85,6 @@ enum fwd_ports_mode { * @count: Number of forwarding rules * @rules: Array of forwarding rules * @map: Bitmap describing which ports are forwarded - * @delta: Offset between the original mapped port number * @sock_count: Number of entries used in @socks * @socks: Listening sockets for forwarding */ @@ -96,7 +95,6 @@ struct fwd_ports { unsigned count; struct fwd_rule rules[MAX_FWD_RULES]; uint8_t map[PORT_BITMAP_SIZE]; - in_port_t delta[NUM_PORTS]; unsigned sock_count; int socks[MAX_LISTEN_SOCKS]; }; @@ -120,9 +118,10 @@ bool nat_inbound(const struct ctx *c, const union inany_addr *addr, union inany_addr *translated); uint8_t fwd_nat_from_tap(const struct ctx *c, uint8_t proto, const struct flowside *ini, struct flowside *tgt); -uint8_t fwd_nat_from_splice(const struct ctx *c, uint8_t proto, +uint8_t fwd_nat_from_splice(const struct fwd_rule *rule, uint8_t proto, const struct flowside *ini, struct flowside *tgt); -uint8_t fwd_nat_from_host(const struct ctx *c, uint8_t proto, +uint8_t fwd_nat_from_host(const struct ctx *c, + const struct fwd_rule *rule, uint8_t proto, const struct flowside *ini, struct flowside *tgt); void fwd_neigh_table_update(const struct ctx *c, const union inany_addr *addr, const uint8_t *mac, bool permanent); -- 2.52.0