On 05/26/2014 02:15 AM, Toshiaki Makita wrote: > br_handle_local_finish() is allowing us to insert an FDB entry with > disallowed vlan. For example, when port 1 and 2 are communicating in > vlan 10, and even if vlan 10 is disallowed on port 3, port 3 can > interfere with their communication by spoofed src mac address with > vlan id 10. > > Note: Even if it is judged that a frame should not be learned, it should > not be dropped because it is destined for not forwarding layer but higher > layer. See IEEE 802.1Q-2011 8.13.10. > > Signed-off-by: Toshiaki Makita <makita.toshiaki@xxxxxxxxxxxxx> > --- > net/bridge/br_input.c | 4 ++-- > net/bridge/br_private.h | 7 +++++++ > net/bridge/br_vlan.c | 28 ++++++++++++++++++++++++++++ > 3 files changed, 37 insertions(+), 2 deletions(-) > > diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c > index 7985dea..04d6348 100644 > --- a/net/bridge/br_input.c > +++ b/net/bridge/br_input.c > @@ -147,8 +147,8 @@ static int br_handle_local_finish(struct sk_buff *skb) > struct net_bridge_port *p = br_port_get_rcu(skb->dev); > u16 vid = 0; > > - br_vlan_get_tag(skb, &vid); > - if (p->flags & BR_LEARNING) > + /* check if vlan is allowed, to avoid spoofing */ > + if (p->flags & BR_LEARNING && br_should_learn(p, skb, &vid)) > br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid, false); > return 0; /* process further */ > } > diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h > index 06811d7..59d3a85 100644 > --- a/net/bridge/br_private.h > +++ b/net/bridge/br_private.h > @@ -581,6 +581,7 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, > struct sk_buff *skb, u16 *vid); > bool br_allowed_egress(struct net_bridge *br, const struct net_port_vlans *v, > const struct sk_buff *skb); > +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_port_vlans *v, > struct sk_buff *skb); > @@ -648,6 +649,12 @@ static inline bool br_allowed_egress(struct net_bridge *br, > return true; > } > > +static inline bool br_should_learn(struct net_bridge_port *p, > + struct sk_buff *skb, u16 *vid) > +{ > + return true; > +} > + > static inline struct sk_buff *br_handle_vlan(struct net_bridge *br, > const struct net_port_vlans *v, > struct sk_buff *skb) > diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c > index 4a37161..5fee2fe 100644 > --- a/net/bridge/br_vlan.c > +++ b/net/bridge/br_vlan.c > @@ -241,6 +241,34 @@ bool br_allowed_egress(struct net_bridge *br, > return false; > } > > +/* Called under RCU */ > +bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid) > +{ > + struct net_bridge *br = p->br; > + struct net_port_vlans *v; > + > + if (!br->vlan_enabled) > + return true; > + > + v = rcu_dereference(p->vlan_info); > + if (!v) > + return false; > + > + br_vlan_get_tag(skb, vid); > + if (!*vid) { > + *vid = br_get_pvid(v); > + if (*vid == VLAN_N_VID) > + return false; > + > + return true; > + } > + > + if (test_bit(*vid, v->vlan_bitmap)) > + return true; > + > + return false; > +} > + > /* Must be protected by RTNL. > * Must be called with vid in range from 1 to 4094 inclusive. > */ > This is very similar to br_allow_ingress(), so may be you can re-factor so that we only have 1 such function... -vlad