Commit 20adfa1a authored by Vlad Yasevich's avatar Vlad Yasevich Committed by David S. Miller

bridge: Check if vlan filtering is enabled only once.

The bridge code checks if vlan filtering is enabled on both
ingress and egress.   When the state flip happens, it
is possible for the bridge to currently be forwarding packets
and forwarding behavior becomes non-deterministic.  Bridge
may drop packets on some interfaces, but not others.

This patch solves this by caching the filtered state of the
packet into skb_cb on ingress.  The skb_cb is guaranteed to
not be over-written between the time packet entres bridge
forwarding path and the time it leaves it.  On egress, we
can then check the cached state to see if we need to
apply filtering information.
Signed-off-by: default avatarVladislav Yasevich <vyasevic@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9a72c2da
...@@ -309,6 +309,9 @@ struct br_input_skb_cb { ...@@ -309,6 +309,9 @@ struct br_input_skb_cb {
int igmp; int igmp;
int mrouters_only; int mrouters_only;
#endif #endif
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
bool vlan_filtered;
#endif
}; };
#define BR_INPUT_SKB_CB(__skb) ((struct br_input_skb_cb *)(__skb)->cb) #define BR_INPUT_SKB_CB(__skb) ((struct br_input_skb_cb *)(__skb)->cb)
......
...@@ -125,7 +125,8 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br, ...@@ -125,7 +125,8 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
{ {
u16 vid; u16 vid;
if (!br->vlan_enabled) /* If this packet was not filtered at input, let it pass */
if (!BR_INPUT_SKB_CB(skb)->vlan_filtered)
goto out; goto out;
/* Vlan filter table must be configured at this point. The /* Vlan filter table must be configured at this point. The
...@@ -164,8 +165,10 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, ...@@ -164,8 +165,10 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
/* If VLAN filtering is disabled on the bridge, all packets are /* If VLAN filtering is disabled on the bridge, all packets are
* permitted. * permitted.
*/ */
if (!br->vlan_enabled) if (!br->vlan_enabled) {
BR_INPUT_SKB_CB(skb)->vlan_filtered = false;
return true; return true;
}
/* If there are no vlan in the permitted list, all packets are /* If there are no vlan in the permitted list, all packets are
* rejected. * rejected.
...@@ -173,6 +176,7 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, ...@@ -173,6 +176,7 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
if (!v) if (!v)
goto drop; goto drop;
BR_INPUT_SKB_CB(skb)->vlan_filtered = true;
proto = br->vlan_proto; proto = br->vlan_proto;
/* If vlan tx offload is disabled on bridge device and frame was /* If vlan tx offload is disabled on bridge device and frame was
...@@ -251,7 +255,8 @@ bool br_allowed_egress(struct net_bridge *br, ...@@ -251,7 +255,8 @@ bool br_allowed_egress(struct net_bridge *br,
{ {
u16 vid; u16 vid;
if (!br->vlan_enabled) /* If this packet was not filtered at input, let it pass */
if (!BR_INPUT_SKB_CB(skb)->vlan_filtered)
return true; return true;
if (!v) if (!v)
...@@ -270,7 +275,8 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid) ...@@ -270,7 +275,8 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
struct net_bridge *br = p->br; struct net_bridge *br = p->br;
struct net_port_vlans *v; struct net_port_vlans *v;
if (!br->vlan_enabled) /* If filtering was disabled at input, let it pass. */
if (!BR_INPUT_SKB_CB(skb)->vlan_filtered)
return true; return true;
v = rcu_dereference(p->vlan_info); v = rcu_dereference(p->vlan_info);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment