aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorToshiaki Makita <makita.toshiaki@lab.ntt.co.jp>2014-02-07 02:48:25 -0500
committerDavid S. Miller <davem@davemloft.net>2014-02-10 17:34:34 -0500
commit424bb9c97cc1992018950485ca7410779b8ea21d (patch)
tree2f416b3ba61c34bfd3e48ba27166650c9d0ead60 /net
parenta778e6d1a51faaafa6a3a3cef9bee11c3bd47f9f (diff)
bridge: Properly check if local fdb entry can be deleted when deleting vlan
Vlan codes unconditionally delete local fdb entries. We should consider the possibility that other ports have the same address and vlan. Example of problematic case: ip link set eth0 address 12:34:56:78:90:ab ip link set eth1 address aa:bb:cc:dd:ee:ff brctl addif br0 eth0 brctl addif br0 eth1 # br0 will have mac address 12:34:56:78:90:ab bridge vlan add dev eth0 vid 10 bridge vlan add dev eth1 vid 10 bridge vlan add dev br0 vid 10 self We will have fdb entry such that f->dst == eth0, f->vlan_id == 10 and f->addr == 12:34:56:78:90:ab at this time. Next, delete eth0 vlan 10. bridge vlan del dev eth0 vid 10 In this case, we still need the entry for br0, but it will be deleted. Note that br0 needs the entry even though its mac address is not set manually. To delete the entry with proper condition checking, fdb_delete_local() is suitable to use. 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')
-rw-r--r--net/bridge/br_fdb.c20
-rw-r--r--net/bridge/br_private.h4
-rw-r--r--net/bridge/br_vlan.c8
3 files changed, 23 insertions, 9 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 43e54d1ae956..0426dff4b3fd 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -27,6 +27,9 @@
27#include "br_private.h" 27#include "br_private.h"
28 28
29static struct kmem_cache *br_fdb_cache __read_mostly; 29static struct kmem_cache *br_fdb_cache __read_mostly;
30static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head,
31 const unsigned char *addr,
32 __u16 vid);
30static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, 33static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
31 const unsigned char *addr, u16 vid); 34 const unsigned char *addr, u16 vid);
32static void fdb_notify(struct net_bridge *br, 35static void fdb_notify(struct net_bridge *br,
@@ -119,6 +122,20 @@ static void fdb_delete_local(struct net_bridge *br,
119 fdb_delete(br, f); 122 fdb_delete(br, f);
120} 123}
121 124
125void br_fdb_find_delete_local(struct net_bridge *br,
126 const struct net_bridge_port *p,
127 const unsigned char *addr, u16 vid)
128{
129 struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)];
130 struct net_bridge_fdb_entry *f;
131
132 spin_lock_bh(&br->hash_lock);
133 f = fdb_find(head, addr, vid);
134 if (f && f->is_local && !f->added_by_user && f->dst == p)
135 fdb_delete_local(br, p, f);
136 spin_unlock_bh(&br->hash_lock);
137}
138
122void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) 139void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
123{ 140{
124 struct net_bridge *br = p->br; 141 struct net_bridge *br = p->br;
@@ -770,8 +787,7 @@ out:
770 return err; 787 return err;
771} 788}
772 789
773int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, 790static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vlan)
774 u16 vlan)
775{ 791{
776 struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)]; 792 struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)];
777 struct net_bridge_fdb_entry *fdb; 793 struct net_bridge_fdb_entry *fdb;
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index f91e1d9c8e92..3ba11bc99b65 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -371,6 +371,9 @@ static inline void br_netpoll_disable(struct net_bridge_port *p)
371int br_fdb_init(void); 371int br_fdb_init(void);
372void br_fdb_fini(void); 372void br_fdb_fini(void);
373void br_fdb_flush(struct net_bridge *br); 373void br_fdb_flush(struct net_bridge *br);
374void br_fdb_find_delete_local(struct net_bridge *br,
375 const struct net_bridge_port *p,
376 const unsigned char *addr, u16 vid);
374void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr); 377void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr);
375void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr); 378void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr);
376void br_fdb_cleanup(unsigned long arg); 379void br_fdb_cleanup(unsigned long arg);
@@ -385,7 +388,6 @@ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
385 const unsigned char *addr, u16 vid); 388 const unsigned char *addr, u16 vid);
386void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, 389void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
387 const unsigned char *addr, u16 vid, bool added_by_user); 390 const unsigned char *addr, u16 vid, bool added_by_user);
388int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vid);
389 391
390int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], 392int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
391 struct net_device *dev, const unsigned char *addr); 393 struct net_device *dev, const unsigned char *addr);
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 233ec1c6e9db..8249ca764c79 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -275,9 +275,7 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)
275 if (!pv) 275 if (!pv)
276 return -EINVAL; 276 return -EINVAL;
277 277
278 spin_lock_bh(&br->hash_lock); 278 br_fdb_find_delete_local(br, NULL, br->dev->dev_addr, vid);
279 fdb_delete_by_addr(br, br->dev->dev_addr, vid);
280 spin_unlock_bh(&br->hash_lock);
281 279
282 __vlan_del(pv, vid); 280 __vlan_del(pv, vid);
283 return 0; 281 return 0;
@@ -378,9 +376,7 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
378 if (!pv) 376 if (!pv)
379 return -EINVAL; 377 return -EINVAL;
380 378
381 spin_lock_bh(&port->br->hash_lock); 379 br_fdb_find_delete_local(port->br, port, port->dev->dev_addr, vid);
382 fdb_delete_by_addr(port->br, port->dev->dev_addr, vid);
383 spin_unlock_bh(&port->br->hash_lock);
384 380
385 return __vlan_del(pv, vid); 381 return __vlan_del(pv, vid);
386} 382}