From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id A31341FF0B0 for ; Tue, 27 Oct 2026 15:23:17 +0100 (CET) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id B3A2121226; Tue, 27 Oct 2026 15:23:14 +0100 (CET) From: Hannes Laimer To: pve-devel@lists.proxmox.com Subject: [PATCH pve-kernel] fix #8217: cherry-pick fix for flowtable vlan packets being untagged Date: Tue, 27 Oct 2026 15:23:06 +0100 Message-ID: <20261027142306.3245660-1-h.laimer@proxmox.com> X-Mailer: git-send-email 2.47.3 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1793110988647 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.357 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: IIZI65KUNRY3UJQ4KPVXUDF4AI4LJSPU X-Message-ID-Hash: IIZI65KUNRY3UJQ4KPVXUDF4AI4LJSPU X-MailFrom: h.laimer@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: The problem was that after the walk to construct the flowtable entry it was possible that only part of the constructed entry was copied into the table. Namely when the egress device the walk discovered in its chain was not in the set of devices for the flowtable. That resulted in the entry construction failing half way through, but the egress being set. That led to a flowtable entry for a vlan device that had its egress set, but missed the step to set the tag in its entry. With this fix an entry that failed part of its construction is not added. Signed-off-by: Hannes Laimer --- ...ble-bail-out-if-forward-path-cannot-.patch | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 patches/kernel/0078-netfilter-flowtable-bail-out-if-forward-path-cannot-.patch diff --git a/patches/kernel/0078-netfilter-flowtable-bail-out-if-forward-path-cannot-.patch b/patches/kernel/0078-netfilter-flowtable-bail-out-if-forward-path-cannot-.patch new file mode 100644 index 000000000000..536b82381a06 --- /dev/null +++ b/patches/kernel/0078-netfilter-flowtable-bail-out-if-forward-path-cannot-.patch @@ -0,0 +1,189 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Tue, 9 Jun 2026 01:36:48 +0200 +Subject: [PATCH] netfilter: flowtable: bail out if forward path cannot be + discovered + +If forward path discovery fails for any reason or netdevice is not +registered for this flowtable, then bail out to classic forwarding path +rather than providing incomplete forwarding path. + +Update the existing forward path parser functions to report an error +so the flow_offload expressions gives up on setting up the flowtable +entry. + +Link: https://sashiko.dev/#/patchset/20260607094954.48892-15-pablo%40netfilter.org?part=14 +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit 871df5007edaacaf2eac58a2871af6044a444ad4) +[HL: drop needs_gso_segment line that does not exist in the base] +Signed-off-by: Hannes Laimer +--- + net/netfilter/nf_flow_table_path.c | 81 +++++++++++++++++------------- + 1 file changed, 46 insertions(+), 35 deletions(-) + +diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c +index 382206e8720c..409c0ea5a49e 100644 +--- a/net/netfilter/nf_flow_table_path.c ++++ b/net/netfilter/nf_flow_table_path.c +@@ -96,9 +96,9 @@ struct nft_forward_info { + enum flow_offload_xmit_type xmit_type; + }; + +-static void nft_dev_path_info(const struct net_device_path_stack *stack, +- struct nft_forward_info *info, +- unsigned char *ha, struct nf_flowtable *flowtable) ++static int nft_dev_path_info(const struct net_device_path_stack *stack, ++ struct nft_forward_info *info, ++ unsigned char *ha, struct nf_flowtable *flowtable) + { + const struct net_device_path *path; + int i; +@@ -126,19 +126,17 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack, + + /* DEV_PATH_VLAN, DEV_PATH_PPPOE and DEV_PATH_TUN */ + if (path->type == DEV_PATH_TUN) { +- if (info->num_tuns) { +- info->indev = NULL; +- break; +- } ++ if (info->num_tuns) ++ return -1; ++ + info->tun.src_v6 = path->tun.src_v6; + info->tun.dst_v6 = path->tun.dst_v6; + info->tun.l3_proto = path->tun.l3_proto; + info->num_tuns++; + } else { +- if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) { +- info->indev = NULL; +- break; +- } ++ if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) ++ return -1; ++ + info->encap[info->num_encaps].id = + path->encap.id; + info->encap[info->num_encaps].proto = +@@ -154,22 +152,23 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack, + + switch (path->bridge.vlan_mode) { + case DEV_PATH_BR_VLAN_UNTAG_HW: ++ if (info->num_encaps == 0) ++ return -1; ++ + info->ingress_vlans |= BIT(info->num_encaps - 1); + break; + case DEV_PATH_BR_VLAN_TAG: +- if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) { +- info->indev = NULL; +- break; +- } ++ if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX) ++ return -1; ++ + info->encap[info->num_encaps].id = path->bridge.vlan_id; + info->encap[info->num_encaps].proto = path->bridge.vlan_proto; + info->num_encaps++; + break; + case DEV_PATH_BR_VLAN_UNTAG: +- if (info->num_encaps == 0) { +- info->indev = NULL; +- break; +- } ++ if (info->num_encaps == 0) ++ return -1; ++ + info->num_encaps--; + break; + case DEV_PATH_BR_VLAN_KEEP: +@@ -178,8 +177,7 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack, + info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; + break; + default: +- info->indev = NULL; +- break; ++ return -1; + } + } + info->outdev = info->indev; +@@ -187,6 +185,8 @@ static void nft_dev_path_info(const struct net_device_path_stack *stack, + if (nf_flowtable_hw_offload(flowtable) && + nft_is_valid_ether_device(info->indev)) + info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT; ++ ++ return 0; + } + + static bool nft_flowtable_find_dev(const struct net_device *dev, +@@ -244,11 +244,11 @@ static int nft_flow_tunnel_update_route(const struct nft_pktinfo *pkt, + return 0; + } + +-static void nft_dev_forward_path(const struct nft_pktinfo *pkt, +- struct nf_flow_route *route, +- const struct nf_conn *ct, +- enum ip_conntrack_dir dir, +- struct nft_flowtable *ft) ++static int nft_dev_forward_path(const struct nft_pktinfo *pkt, ++ struct nf_flow_route *route, ++ const struct nf_conn *ct, ++ enum ip_conntrack_dir dir, ++ struct nft_flowtable *ft) + { + const struct dst_entry *dst = route->tuple[dir].dst; + struct net_device_path_stack stack; +@@ -256,15 +256,16 @@ static void nft_dev_forward_path(const struct nft_pktinfo *pkt, + unsigned char ha[ETH_ALEN]; + int i; + +- if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) >= 0) +- nft_dev_path_info(&stack, &info, ha, &ft->data); ++ if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) < 0 || ++ nft_dev_path_info(&stack, &info, ha, &ft->data) < 0) ++ return -ENOENT; ++ ++ if (!nft_flowtable_find_dev(info.indev, ft)) ++ return -ENOENT; + + if (info.outdev) + route->tuple[dir].out.ifindex = info.outdev->ifindex; + +- if (!info.indev || !nft_flowtable_find_dev(info.indev, ft)) +- return; +- + route->tuple[!dir].in.ifindex = info.indev->ifindex; + for (i = 0; i < info.num_encaps; i++) { + route->tuple[!dir].in.encap[i].id = info.encap[i].id; +@@ -287,6 +288,8 @@ static void nft_dev_forward_path(const struct nft_pktinfo *pkt, + memcpy(route->tuple[dir].out.h_dest, info.h_dest, ETH_ALEN); + route->tuple[dir].xmit_type = info.xmit_type; + } ++ ++ return 0; + } + + int nft_flow_route(const struct nft_pktinfo *pkt, const struct nf_conn *ct, +@@ -331,11 +334,19 @@ int nft_flow_route(const struct nft_pktinfo *pkt, const struct nf_conn *ct, + nft_default_forward_path(route, this_dst, dir); + nft_default_forward_path(route, other_dst, !dir); + +- if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) +- nft_dev_forward_path(pkt, route, ct, dir, ft); +- if (route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) +- nft_dev_forward_path(pkt, route, ct, !dir, ft); ++ if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH && ++ nft_dev_forward_path(pkt, route, ct, dir, ft) < 0) ++ goto err_dst_release; ++ ++ if (route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH && ++ nft_dev_forward_path(pkt, route, ct, !dir, ft) < 0) ++ goto err_dst_release; + + return 0; ++ ++err_dst_release: ++ dst_release(route->tuple[dir].dst); ++ dst_release(route->tuple[!dir].dst); ++ return -ENOENT; + } + EXPORT_SYMBOL_GPL(nft_flow_route); -- 2.47.3