aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge
diff options
context:
space:
mode:
authorToshiaki Makita <makita.toshiaki@lab.ntt.co.jp>2014-02-07 02:48:22 -0500
committerDavid S. Miller <davem@davemloft.net>2014-02-10 17:34:33 -0500
commit2b292fb4a57dc233e298a84196d33be0bc3828e4 (patch)
tree3ed6f64d59ea5a91e8524bdc87ffb23823a08a26 /net/bridge
parenta4b816d8ba1c1917842dc3de97cbf8ef116e043e (diff)
bridge: Fix the way to check if a local fdb entry can be deleted
We should take into account the followings when deleting a local fdb entry. - nbp_vlan_find() can be used only when vid != 0 to check if an entry is deletable, because a fdb entry with vid 0 can exist at any time while nbp_vlan_find() always return false with vid 0. Example of problematic case: ip link set eth0 address 12:34:56:78:90:ab ip link set eth1 address 12:34:56:78:90:ab brctl addif br0 eth0 brctl addif br0 eth1 ip link set eth0 address aa:bb:cc:dd:ee:ff Then, the fdb entry 12:34:56:78:90:ab will be deleted even though the bridge port eth1 still has that address. - The port to which the bridge device is attached might needs a local entry if its mac address is set manually. Example of problematic case: ip link set eth0 address 12:34:56:78:90:ab brctl addif br0 eth0 ip link set br0 address 12:34:56:78:90:ab ip link set eth0 address aa:bb:cc:dd:ee:ff Then, the fdb still must have the entry 12:34:56:78:90:ab, but it will be deleted. We can use br->dev->addr_assign_type to check if the address is manually set or not, but I propose another approach. Since we delete and insert local entries whenever changing mac address of the bridge device, we can change dst of the entry to NULL regardless of addr_assign_type when deleting an entry associated with a certain port, and if it is found to be unnecessary later, then delete it. That is, if changing mac address of a port, the entry might be changed to its dst being NULL first, but is eventually deleted when recalculating and changing bridge id. This approach is especially useful when we want to share the code with deleting vlan in which the bridge device might want such an entry regardless of addr_assign_type, and makes things easy because we don't have to consider if mac address of the bridge device will be changed or not at the time we delete a local entry of a port, which means fdb code will not be bothered even if the bridge id calculating logic is changed in the future. Also, this change reduces inconsistent state, where frames whose dst is the mac address of the bridge, can't reach the bridge because of premature fdb entry deletion. This change reduces the possibility that the bridge device replies unreachable mac address to arp requests, which could occur during the short window between calling del_nbp() and br_stp_recalculate_bridge_id() in br_del_if(). This will effective after br_fdb_delete_by_port() starts to use the same code by following patch. Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp> Acked-by: Vlad Yasevich <vyasevic@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge')
-rw-r--r--net/bridge/br_fdb.c10
-rw-r--r--net/bridge/br_private.h6
-rw-r--r--net/bridge/br_vlan.c19
3 files changed, 34 insertions, 1 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 96ab1d1748d0..b4005f5b28f4 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -114,12 +114,20 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
114 if (op != p && 114 if (op != p &&
115 ether_addr_equal(op->dev->dev_addr, 115 ether_addr_equal(op->dev->dev_addr,
116 f->addr.addr) && 116 f->addr.addr) &&
117 nbp_vlan_find(op, vid)) { 117 (!vid || nbp_vlan_find(op, vid))) {
118 f->dst = op; 118 f->dst = op;
119 goto skip_delete; 119 goto skip_delete;
120 } 120 }
121 } 121 }
122 122
123 /* maybe bridge device has same hw addr? */
124 if (ether_addr_equal(br->dev->dev_addr,
125 f->addr.addr) &&
126 (!vid || br_vlan_find(br, vid))) {
127 f->dst = NULL;
128 goto skip_delete;
129 }
130
123 /* delete old one */ 131 /* delete old one */
124 fdb_delete(br, f); 132 fdb_delete(br, f);
125skip_delete: 133skip_delete:
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 939a59e15036..f91e1d9c8e92 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -585,6 +585,7 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
585int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags); 585int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags);
586int br_vlan_delete(struct net_bridge *br, u16 vid); 586int br_vlan_delete(struct net_bridge *br, u16 vid);
587void br_vlan_flush(struct net_bridge *br); 587void br_vlan_flush(struct net_bridge *br);
588bool br_vlan_find(struct net_bridge *br, u16 vid);
588int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); 589int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
589int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); 590int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags);
590int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); 591int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
@@ -666,6 +667,11 @@ static inline void br_vlan_flush(struct net_bridge *br)
666{ 667{
667} 668}
668 669
670static inline bool br_vlan_find(struct net_bridge *br, u16 vid)
671{
672 return false;
673}
674
669static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) 675static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
670{ 676{
671 return -EOPNOTSUPP; 677 return -EOPNOTSUPP;
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 4ca4d0a0151c..233ec1c6e9db 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -295,6 +295,25 @@ void br_vlan_flush(struct net_bridge *br)
295 __vlan_flush(pv); 295 __vlan_flush(pv);
296} 296}
297 297
298bool br_vlan_find(struct net_bridge *br, u16 vid)
299{
300 struct net_port_vlans *pv;
301 bool found = false;
302
303 rcu_read_lock();
304 pv = rcu_dereference(br->vlan_info);
305
306 if (!pv)
307 goto out;
308
309 if (test_bit(vid, pv->vlan_bitmap))
310 found = true;
311
312out:
313 rcu_read_unlock();
314 return found;
315}
316
298int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val) 317int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
299{ 318{
300 if (!rtnl_trylock()) 319 if (!rtnl_trylock())