diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 845193baf992..d89f1b7d6def 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -21,17 +21,26 @@ struct br_fwd_dst { struct net_bridge_vlan *vlan; }; +static bool should_deliver_vlan(const struct br_fwd_dst *fwd, + const struct sk_buff *skb) +{ + if (fwd->vlan) + return br_vlan_state_allowed(br_vlan_get_state(fwd->vlan), + false); + + return br_allowed_egress(nbp_vlan_group_rcu(fwd->port), skb); +} + /* Don't forward packets to originating port or forwarding disabled */ -static inline int should_deliver(const struct br_fwd_dst *fwd, - const struct sk_buff *skb) +static __always_inline bool should_deliver(const struct br_fwd_dst *fwd, + const struct sk_buff *skb) { const struct net_bridge_port *p = fwd->port; - struct net_bridge_vlan_group *vg; - vg = nbp_vlan_group_rcu(p); return (test_bit(BR_HAIRPIN_MODE_BIT, &p->flags) || skb->dev != p->dev) && (br_mst_is_enabled(p) || p->state == BR_STATE_FORWARDING) && - br_allowed_egress(vg, skb) && nbp_switchdev_allowed_egress(p, skb) && + should_deliver_vlan(fwd, skb) && + nbp_switchdev_allowed_egress(p, skb) && !br_skb_isolated(p, skb); } @@ -89,8 +98,8 @@ static void __br_forward(const struct br_fwd_dst *fwd, */ nbp_switchdev_frame_mark_tx_fwd_offload(to, skb); - vg = nbp_vlan_group_rcu(to); - skb = br_handle_vlan(to->br, to, vg, skb); + vg = fwd->vlan ? NULL : nbp_vlan_group_rcu(to); + skb = br_handle_vlan(to->br, to, vg, fwd->vlan, skb); if (!skb) return; diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index b20c7c182a80..4357d78524a6 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -58,7 +58,7 @@ static int br_pass_frame_up(struct sk_buff *skb, bool promisc) indev = skb->dev; skb->dev = brdev; - skb = br_handle_vlan(br, NULL, vg, skb); + skb = br_handle_vlan(br, NULL, vg, NULL, skb); if (!skb) return NET_RX_DROP; /* update the multicast stats if the packet is IGMP/MLD */ diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index a33da6e9765f..7c0b1d3e7931 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -1606,6 +1606,7 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid); struct sk_buff *br_handle_vlan(struct net_bridge *br, const struct net_bridge_port *port, struct net_bridge_vlan_group *vg, + struct net_bridge_vlan *vlan, struct sk_buff *skb); int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags, bool *changed, struct netlink_ext_ack *extack); @@ -1743,6 +1744,7 @@ static inline bool br_should_learn(struct net_bridge_port *p, static inline struct sk_buff *br_handle_vlan(struct net_bridge *br, const struct net_bridge_port *port, struct net_bridge_vlan_group *vg, + struct net_bridge_vlan *vlan, struct sk_buff *skb) { return skb; @@ -1953,6 +1955,11 @@ static inline bool br_vlan_state_allowed(u8 state, bool learn_allow) return false; } } +#else +static inline bool br_vlan_state_allowed(u8 state, bool learn_allow) +{ + return false; +} #endif /* br_mst.c */ diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index d750581df64d..ce5aa15c4540 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -520,10 +520,10 @@ static void __vlan_flush(const struct net_bridge *br, struct sk_buff *br_handle_vlan(struct net_bridge *br, const struct net_bridge_port *p, struct net_bridge_vlan_group *vg, + struct net_bridge_vlan *v, struct sk_buff *skb) { struct pcpu_sw_netstats *stats; - struct net_bridge_vlan *v; u16 vid; /* If this packet was not filtered at input, let it pass */ @@ -534,19 +534,22 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br, * a valid vlan id. If the vlan id has untagged flag set, * send untagged; otherwise, send tagged. */ - br_vlan_get_tag(skb, &vid); - v = br_vlan_find(vg, vid); - /* Vlan entry must be configured at this point. The - * only exception is the bridge is set in promisc mode and the - * packet is destined for the bridge device. In this case - * pass the packet as is. - */ - if (!v || !br_vlan_should_use(v)) { - if ((br->dev->flags & IFF_PROMISC) && skb->dev == br->dev) { - goto out; - } else { - kfree_skb(skb); - return NULL; + if (!v) { + br_vlan_get_tag(skb, &vid); + v = br_vlan_find(vg, vid); + /* Vlan entry must be configured at this point. The + * only exception is the bridge is set in promisc mode and the + * packet is destined for the bridge device. In this case + * pass the packet as is. + */ + if (!v || !br_vlan_should_use(v)) { + if ((br->dev->flags & IFF_PROMISC) && + skb->dev == br->dev) { + goto out; + } else { + kfree_skb(skb); + return NULL; + } } } if (br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) {