From: Daniel Pawlik <pawlik.dan@gmail.com>
To: netfilter-devel@vger.kernel.org, netdev@vger.kernel.org
Cc: pablo@netfilter.org, fw@strlen.de, phil@nwl.cc,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, horms@kernel.org, andrew+netdev@lunn.ch,
razor@blackwall.org, idosch@nvidia.com, matthias.bgg@gmail.com,
angelogioacchino.delregno@collabora.com, bridge@lists.linux.dev,
coreteam@netfilter.org, linux-mediatek@lists.infradead.org,
linux-arm-kernel@lists.infradead.org, rchen14b@gmail.com,
lorenzo@kernel.org, Daniel Pawlik <pawlik.dan@gmail.com>
Subject: [PATCH 2/5] net: bridge: add flow offload helpers
Date: Tue, 30 Jun 2026 08:57:32 +0200 [thread overview]
Message-ID: <20260630065735.3341614-3-pawlik.dan@gmail.com> (raw)
In-Reply-To: <20260630065735.3341614-1-pawlik.dan@gmail.com>
Add three helpers that expose the bridge state needed by nft_flow_offload
without requiring callers to include net/bridge/br_private.h. Each
performs a single br_port_get_rcu() lookup:
- br_fdb_has_forwarding_entry_rcu(): resolves the VLAN id for the packet
(skb tag or PVID when filtering is on, 0 otherwise) then checks whether
the bridge FDB contains a forwarding entry (dst != NULL, non-local) for
the resulting MAC/VLAN pair.
- br_vlan_get_offload_info_rcu(): when VLAN filtering is active, returns
the VLAN id (skb tag or PVID) and writes the bridge VLAN protocol to
*proto in a single port lookup. Returns 0 when filtering is off.
- br_vlan_is_enabled_rcu(): returns true when VLAN filtering is enabled
on the bridge a port device belongs to.
Based on MediaTek SDK patches by Bo-Cun Chen <bc-bocun.chen@mediatek.com>
and the OpenWrt bridge offload series by Ryan Chen <rchen14b@gmail.com>.
Signed-off-by: Daniel Pawlik <pawlik.dan@gmail.com>
---
include/linux/if_bridge.h | 23 +++++++++++++++++++
net/bridge/br_fdb.c | 34 ++++++++++++++++++++++++++++
net/bridge/br_vlan.c | 47 +++++++++++++++++++++++++++++++++++++++
3 files changed, 104 insertions(+)
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index 75673b8bffcb..c1cae54749c5 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -148,6 +148,9 @@ int br_vlan_get_info(const struct net_device *dev, u16 vid,
struct bridge_vlan_info *p_vinfo);
int br_vlan_get_info_rcu(const struct net_device *dev, u16 vid,
struct bridge_vlan_info *p_vinfo);
+u16 br_vlan_get_offload_info_rcu(const struct net_device *dev,
+ const struct sk_buff *skb, __be16 *proto);
+bool br_vlan_is_enabled_rcu(const struct net_device *dev);
bool br_mst_enabled(const struct net_device *dev);
int br_mst_get_info(const struct net_device *dev, u16 msti, unsigned long *vids);
int br_mst_get_state(const struct net_device *dev, u16 msti, u8 *state);
@@ -184,6 +187,17 @@ static inline int br_vlan_get_info_rcu(const struct net_device *dev, u16 vid,
return -EINVAL;
}
+static inline u16 br_vlan_get_offload_info_rcu(const struct net_device *dev,
+ const struct sk_buff *skb,
+ __be16 *proto)
+{
+ return 0;
+}
+
+static inline bool br_vlan_is_enabled_rcu(const struct net_device *dev)
+{
+ return false;
+}
static inline bool br_mst_enabled(const struct net_device *dev)
{
return false;
@@ -209,6 +223,8 @@ void br_fdb_clear_offload(const struct net_device *dev, u16 vid);
bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag);
u8 br_port_get_stp_state(const struct net_device *dev);
clock_t br_get_ageing_time(const struct net_device *br_dev);
+bool br_fdb_has_forwarding_entry_rcu(const struct net_device *dev,
+ const struct sk_buff *skb, const u8 *addr);
#else
static inline struct net_device *
br_fdb_find_port(const struct net_device *br_dev,
@@ -237,6 +253,13 @@ static inline clock_t br_get_ageing_time(const struct net_device *br_dev)
{
return 0;
}
+
+static inline bool br_fdb_has_forwarding_entry_rcu(const struct net_device *dev,
+ const struct sk_buff *skb,
+ const u8 *addr)
+{
+ return false;
+}
#endif
#endif
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index e4570bbed854..2a65b203274e 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -267,6 +267,40 @@ struct net_bridge_fdb_entry *br_fdb_find_rcu(struct net_bridge *br,
return fdb_find_rcu(&br->fdb_hash_tbl, addr, vid);
}
+/**
+ * br_fdb_has_forwarding_entry_rcu - check if a MAC can be forwarded by the bridge
+ * @dev: bridge port network device
+ * @skb: packet buffer (used to determine VLAN id)
+ * @addr: destination MAC address
+ *
+ * Resolves the VLAN id for @skb on @dev (skb VLAN tag when present, PVID
+ * when VLAN filtering is enabled, 0 otherwise) then checks whether the bridge
+ * FDB contains a forwarding entry (dst != NULL, not a local/self entry) for
+ * @addr and that VLAN id. Single br_port_get_rcu() lookup.
+ * Must be called under RCU read lock.
+ *
+ * Returns: true if a forwarding entry exists for @addr, false otherwise.
+ */
+bool br_fdb_has_forwarding_entry_rcu(const struct net_device *dev,
+ const struct sk_buff *skb, const u8 *addr)
+{
+ struct net_bridge_port *port = br_port_get_rcu(dev);
+ struct net_bridge_fdb_entry *fdb;
+ u16 vid = 0;
+
+ if (!port)
+ return false;
+ if (br_opt_get(port->br, BROPT_VLAN_ENABLED)) {
+ if (skb_vlan_tag_present(skb))
+ vid = skb_vlan_tag_get_id(skb);
+ else
+ br_vlan_get_pvid_rcu(dev, &vid);
+ }
+ fdb = br_fdb_find_rcu(port->br, addr, vid);
+ return fdb && fdb->dst;
+}
+EXPORT_SYMBOL_GPL(br_fdb_has_forwarding_entry_rcu);
+
/* When a static FDB entry is added, the mac address from the entry is
* added to the bridge private HW address list and all required ports
* are then updated with the new information.
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 5560afcaaca3..974e57ea533a 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -1559,6 +1559,53 @@ int br_vlan_get_info_rcu(const struct net_device *dev, u16 vid,
}
EXPORT_SYMBOL_GPL(br_vlan_get_info_rcu);
+/**
+ * br_vlan_get_offload_info_rcu - get VLAN id and protocol for bridge flow offload
+ * @dev: bridge port network device
+ * @skb: packet buffer
+ * @proto: output for the bridge VLAN protocol (set only when return value != 0)
+ *
+ * When VLAN filtering is enabled, resolves the VLAN id for flow offload (skb
+ * VLAN tag id if present, PVID otherwise) and writes the bridge VLAN protocol
+ * to @proto. Single br_port_get_rcu() lookup. Must be called under RCU read
+ * lock.
+ *
+ * Returns: the VLAN id, or 0 when filtering is off or @dev is not a bridge port.
+ */
+u16 br_vlan_get_offload_info_rcu(const struct net_device *dev,
+ const struct sk_buff *skb, __be16 *proto)
+{
+ struct net_bridge_port *port = br_port_get_rcu(dev);
+ u16 vid = 0;
+
+ if (!port || !br_opt_get(port->br, BROPT_VLAN_ENABLED))
+ return 0;
+ if (skb_vlan_tag_present(skb))
+ vid = skb_vlan_tag_get_id(skb);
+ else
+ br_vlan_get_pvid_rcu(dev, &vid);
+ if (vid)
+ *proto = port->br->vlan_proto;
+ return vid;
+}
+EXPORT_SYMBOL_GPL(br_vlan_get_offload_info_rcu);
+
+/**
+ * br_vlan_is_enabled_rcu - check if VLAN filtering is active on a port's bridge
+ * @dev: bridge port network device
+ *
+ * Must be called under RCU read lock.
+ *
+ * Returns: true if VLAN filtering is enabled, false otherwise.
+ */
+bool br_vlan_is_enabled_rcu(const struct net_device *dev)
+{
+ struct net_bridge_port *port = br_port_get_rcu(dev);
+
+ return port && br_opt_get(port->br, BROPT_VLAN_ENABLED);
+}
+EXPORT_SYMBOL_GPL(br_vlan_is_enabled_rcu);
+
static int br_vlan_is_bind_vlan_dev(const struct net_device *dev)
{
return is_vlan_dev(dev) &&
--
2.54.0
next prev parent reply other threads:[~2026-06-30 6:58 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-30 6:57 [PATCH v2 0/5] netfilter: nf_flow_table_path: L2 bridge offload Daniel Pawlik
2026-06-30 6:57 ` [PATCH 1/5] net: export __dev_fill_forward_path Daniel Pawlik
2026-06-30 6:57 ` Daniel Pawlik [this message]
2026-06-30 6:57 ` [PATCH 3/5] netfilter: nf_flow_table_path: add L2 bridge offload Daniel Pawlik
2026-06-30 6:57 ` [PATCH 4/5] netfilter: nf_flow_table_path: handle DEV_PATH_MTK_WDMA in path info Daniel Pawlik
2026-06-30 6:57 ` [PATCH 5/5] netfilter: nf_flow_table_path: add VLAN passthrough support Daniel Pawlik
2026-06-30 8:45 ` [PATCH v2 0/5] netfilter: nf_flow_table_path: L2 bridge offload Pablo Neira Ayuso
2026-07-01 6:11 ` Daniel Pawlik
-- strict thread matches above, loose matches on Subject: below --
2026-06-29 12:32 [PATCH " Daniel Pawlik
2026-06-29 12:32 ` [PATCH 2/5] net: bridge: add flow offload helpers Daniel Pawlik
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=20260630065735.3341614-3-pawlik.dan@gmail.com \
--to=pawlik.dan@gmail.com \
--cc=andrew+netdev@lunn.ch \
--cc=angelogioacchino.delregno@collabora.com \
--cc=bridge@lists.linux.dev \
--cc=coreteam@netfilter.org \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=fw@strlen.de \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=kuba@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=lorenzo@kernel.org \
--cc=matthias.bgg@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=netfilter-devel@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=pablo@netfilter.org \
--cc=phil@nwl.cc \
--cc=razor@blackwall.org \
--cc=rchen14b@gmail.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.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.