aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVlad Yasevich <vyasevic@redhat.com>2013-02-13 07:00:19 -0500
committerDavid S. Miller <davem@davemloft.net>2013-02-13 19:42:16 -0500
commitbc9a25d21ef8bad30e259af5114ccfb845c066db (patch)
tree70b44a0a84ecf60c227af9bb35c43c2f7c718d5e
parent1690be63a27b20ae65c792729a44f5970561ffa4 (diff)
bridge: Add vlan support for local fdb entries
When VLAN is added to the port, a local fdb entry for that port (the entry with the mac address of the port) is added for that VLAN. This way we can correctly determine if the traffic is for the bridge itself. If the address of the port changes, we try to change all the local fdb entries we have for that port. Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/bridge/br_fdb.c61
-rw-r--r--net/bridge/br_if.c2
-rw-r--r--net/bridge/br_private.h10
-rw-r--r--net/bridge/br_vlan.c77
4 files changed, 125 insertions, 25 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 4b75ad43aa85..8117900af4de 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -28,7 +28,7 @@
28 28
29static struct kmem_cache *br_fdb_cache __read_mostly; 29static struct kmem_cache *br_fdb_cache __read_mostly;
30static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, 30static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
31 const unsigned char *addr); 31 const unsigned char *addr, u16 vid);
32static void fdb_notify(struct net_bridge *br, 32static void fdb_notify(struct net_bridge *br,
33 const struct net_bridge_fdb_entry *, int); 33 const struct net_bridge_fdb_entry *, int);
34 34
@@ -92,6 +92,7 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
92void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) 92void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
93{ 93{
94 struct net_bridge *br = p->br; 94 struct net_bridge *br = p->br;
95 bool no_vlan = (nbp_get_vlan_info(p) == NULL) ? true : false;
95 int i; 96 int i;
96 97
97 spin_lock_bh(&br->hash_lock); 98 spin_lock_bh(&br->hash_lock);
@@ -106,10 +107,12 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
106 if (f->dst == p && f->is_local) { 107 if (f->dst == p && f->is_local) {
107 /* maybe another port has same hw addr? */ 108 /* maybe another port has same hw addr? */
108 struct net_bridge_port *op; 109 struct net_bridge_port *op;
110 u16 vid = f->vlan_id;
109 list_for_each_entry(op, &br->port_list, list) { 111 list_for_each_entry(op, &br->port_list, list) {
110 if (op != p && 112 if (op != p &&
111 ether_addr_equal(op->dev->dev_addr, 113 ether_addr_equal(op->dev->dev_addr,
112 f->addr.addr)) { 114 f->addr.addr) &&
115 nbp_vlan_find(op, vid)) {
113 f->dst = op; 116 f->dst = op;
114 goto insert; 117 goto insert;
115 } 118 }
@@ -117,27 +120,55 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
117 120
118 /* delete old one */ 121 /* delete old one */
119 fdb_delete(br, f); 122 fdb_delete(br, f);
120 goto insert; 123insert:
124 /* insert new address, may fail if invalid
125 * address or dup.
126 */
127 fdb_insert(br, p, newaddr, vid);
128
129 /* if this port has no vlan information
130 * configured, we can safely be done at
131 * this point.
132 */
133 if (no_vlan)
134 goto done;
121 } 135 }
122 } 136 }
123 } 137 }
124 insert:
125 /* insert new address, may fail if invalid address or dup. */
126 fdb_insert(br, p, newaddr);
127 138
139done:
128 spin_unlock_bh(&br->hash_lock); 140 spin_unlock_bh(&br->hash_lock);
129} 141}
130 142
131void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) 143void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
132{ 144{
133 struct net_bridge_fdb_entry *f; 145 struct net_bridge_fdb_entry *f;
146 struct net_port_vlans *pv;
147 u16 vid = 0;
134 148
135 /* If old entry was unassociated with any port, then delete it. */ 149 /* If old entry was unassociated with any port, then delete it. */
136 f = __br_fdb_get(br, br->dev->dev_addr, 0); 150 f = __br_fdb_get(br, br->dev->dev_addr, 0);
137 if (f && f->is_local && !f->dst) 151 if (f && f->is_local && !f->dst)
138 fdb_delete(br, f); 152 fdb_delete(br, f);
139 153
140 fdb_insert(br, NULL, newaddr); 154 fdb_insert(br, NULL, newaddr, 0);
155
156 /* Now remove and add entries for every VLAN configured on the
157 * bridge. This function runs under RTNL so the bitmap will not
158 * change from under us.
159 */
160 pv = br_get_vlan_info(br);
161 if (!pv)
162 return;
163
164 for (vid = find_next_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN, vid);
165 vid < BR_VLAN_BITMAP_LEN;
166 vid = find_next_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN, vid+1)) {
167 f = __br_fdb_get(br, br->dev->dev_addr, vid);
168 if (f && f->is_local && !f->dst)
169 fdb_delete(br, f);
170 fdb_insert(br, NULL, newaddr, vid);
171 }
141} 172}
142 173
143void br_fdb_cleanup(unsigned long _data) 174void br_fdb_cleanup(unsigned long _data)
@@ -379,15 +410,15 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
379} 410}
380 411
381static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, 412static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
382 const unsigned char *addr) 413 const unsigned char *addr, u16 vid)
383{ 414{
384 struct hlist_head *head = &br->hash[br_mac_hash(addr, 0)]; 415 struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)];
385 struct net_bridge_fdb_entry *fdb; 416 struct net_bridge_fdb_entry *fdb;
386 417
387 if (!is_valid_ether_addr(addr)) 418 if (!is_valid_ether_addr(addr))
388 return -EINVAL; 419 return -EINVAL;
389 420
390 fdb = fdb_find(head, addr, 0); 421 fdb = fdb_find(head, addr, vid);
391 if (fdb) { 422 if (fdb) {
392 /* it is okay to have multiple ports with same 423 /* it is okay to have multiple ports with same
393 * address, just use the first one. 424 * address, just use the first one.
@@ -400,7 +431,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
400 fdb_delete(br, fdb); 431 fdb_delete(br, fdb);
401 } 432 }
402 433
403 fdb = fdb_create(head, source, addr, 0); 434 fdb = fdb_create(head, source, addr, vid);
404 if (!fdb) 435 if (!fdb)
405 return -ENOMEM; 436 return -ENOMEM;
406 437
@@ -411,12 +442,12 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
411 442
412/* Add entry for local address of interface */ 443/* Add entry for local address of interface */
413int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, 444int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
414 const unsigned char *addr) 445 const unsigned char *addr, u16 vid)
415{ 446{
416 int ret; 447 int ret;
417 448
418 spin_lock_bh(&br->hash_lock); 449 spin_lock_bh(&br->hash_lock);
419 ret = fdb_insert(br, source, addr); 450 ret = fdb_insert(br, source, addr, vid);
420 spin_unlock_bh(&br->hash_lock); 451 spin_unlock_bh(&br->hash_lock);
421 return ret; 452 return ret;
422} 453}
@@ -712,8 +743,8 @@ out:
712 return err; 743 return err;
713} 744}
714 745
715static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, 746int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr,
716 u16 vlan) 747 u16 vlan)
717{ 748{
718 struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)]; 749 struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)];
719 struct net_bridge_fdb_entry *fdb; 750 struct net_bridge_fdb_entry *fdb;
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 335c60cebfd1..ef1b91431c6b 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -397,7 +397,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
397 397
398 dev_set_mtu(br->dev, br_min_mtu(br)); 398 dev_set_mtu(br->dev, br_min_mtu(br));
399 399
400 if (br_fdb_insert(br, p, dev->dev_addr)) 400 if (br_fdb_insert(br, p, dev->dev_addr, 0))
401 netdev_err(dev, "failed insert local address bridge forwarding table\n"); 401 netdev_err(dev, "failed insert local address bridge forwarding table\n");
402 402
403 kobject_uevent(&p->kobj, KOBJ_ADD); 403 kobject_uevent(&p->kobj, KOBJ_ADD);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 799dbb37e5a2..32ecfa4ef47f 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -382,11 +382,13 @@ extern int br_fdb_fillbuf(struct net_bridge *br, void *buf,
382 unsigned long count, unsigned long off); 382 unsigned long count, unsigned long off);
383extern int br_fdb_insert(struct net_bridge *br, 383extern int br_fdb_insert(struct net_bridge *br,
384 struct net_bridge_port *source, 384 struct net_bridge_port *source,
385 const unsigned char *addr); 385 const unsigned char *addr,
386 u16 vid);
386extern void br_fdb_update(struct net_bridge *br, 387extern void br_fdb_update(struct net_bridge *br,
387 struct net_bridge_port *source, 388 struct net_bridge_port *source,
388 const unsigned char *addr, 389 const unsigned char *addr,
389 u16 vid); 390 u16 vid);
391extern int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vid);
390 392
391extern int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], 393extern int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
392 struct net_device *dev, 394 struct net_device *dev,
@@ -573,6 +575,7 @@ extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
573extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); 575extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags);
574extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); 576extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid);
575extern void nbp_vlan_flush(struct net_bridge_port *port); 577extern void nbp_vlan_flush(struct net_bridge_port *port);
578extern bool nbp_vlan_find(struct net_bridge_port *port, u16 vid);
576 579
577static inline struct net_port_vlans *br_get_vlan_info( 580static inline struct net_port_vlans *br_get_vlan_info(
578 const struct net_bridge *br) 581 const struct net_bridge *br)
@@ -676,6 +679,11 @@ static inline struct net_port_vlans *nbp_get_vlan_info(
676 return NULL; 679 return NULL;
677} 680}
678 681
682static inline bool nbp_vlan_find(struct net_bridge_port *port, u16 vid)
683{
684 return false;
685}
686
679static inline u16 br_vlan_get_tag(const struct sk_buff *skb, u16 *tag) 687static inline u16 br_vlan_get_tag(const struct sk_buff *skb, u16 *tag)
680{ 688{
681 return 0; 689 return 0;
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index c79940cff3a1..9ea358fbbf78 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -25,6 +25,9 @@ static void __vlan_delete_pvid(struct net_port_vlans *v, u16 vid)
25 25
26static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) 26static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
27{ 27{
28 struct net_bridge_port *p = NULL;
29 struct net_bridge *br;
30 struct net_device *dev;
28 int err; 31 int err;
29 32
30 if (test_bit(vid, v->vlan_bitmap)) { 33 if (test_bit(vid, v->vlan_bitmap)) {
@@ -33,19 +36,35 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
33 return 0; 36 return 0;
34 } 37 }
35 38
36 if (v->port_idx && vid) { 39 if (vid) {
37 struct net_device *dev = v->parent.port->dev; 40 if (v->port_idx) {
41 p = v->parent.port;
42 br = p->br;
43 dev = p->dev;
44 } else {
45 br = v->parent.br;
46 dev = br->dev;
47 }
38 48
39 /* Add VLAN to the device filter if it is supported. 49 if (p && (dev->features & NETIF_F_HW_VLAN_FILTER)) {
40 * Stricly speaking, this is not necessary now, since devices 50 /* Add VLAN to the device filter if it is supported.
41 * are made promiscuous by the bridge, but if that ever changes 51 * Stricly speaking, this is not necessary now, since
42 * this code will allow tagged traffic to enter the bridge. 52 * devices are made promiscuous by the bridge, but if
43 */ 53 * that ever changes this code will allow tagged
44 if (dev->features & NETIF_F_HW_VLAN_FILTER) { 54 * traffic to enter the bridge.
55 */
45 err = dev->netdev_ops->ndo_vlan_rx_add_vid(dev, vid); 56 err = dev->netdev_ops->ndo_vlan_rx_add_vid(dev, vid);
46 if (err) 57 if (err)
47 return err; 58 return err;
48 } 59 }
60
61 err = br_fdb_insert(br, p, dev->dev_addr, vid);
62 if (err) {
63 br_err(br, "failed insert local address into bridge "
64 "forwarding table\n");
65 goto out_filt;
66 }
67
49 } 68 }
50 69
51 set_bit(vid, v->vlan_bitmap); 70 set_bit(vid, v->vlan_bitmap);
@@ -54,6 +73,11 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
54 __vlan_add_pvid(v, vid); 73 __vlan_add_pvid(v, vid);
55 74
56 return 0; 75 return 0;
76
77out_filt:
78 if (p && (dev->features & NETIF_F_HW_VLAN_FILTER))
79 dev->netdev_ops->ndo_vlan_rx_kill_vid(dev, vid);
80 return err;
57} 81}
58 82
59static int __vlan_del(struct net_port_vlans *v, u16 vid) 83static int __vlan_del(struct net_port_vlans *v, u16 vid)
@@ -253,6 +277,15 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)
253 if (!pv) 277 if (!pv)
254 return -EINVAL; 278 return -EINVAL;
255 279
280 if (vid) {
281 /* If the VID !=0 remove fdb for this vid. VID 0 is special
282 * in that it's the default and is always there in the fdb.
283 */
284 spin_lock_bh(&br->hash_lock);
285 fdb_delete_by_addr(br, br->dev->dev_addr, vid);
286 spin_unlock_bh(&br->hash_lock);
287 }
288
256 __vlan_del(pv, vid); 289 __vlan_del(pv, vid);
257 return 0; 290 return 0;
258} 291}
@@ -329,6 +362,15 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
329 if (!pv) 362 if (!pv)
330 return -EINVAL; 363 return -EINVAL;
331 364
365 if (vid) {
366 /* If the VID !=0 remove fdb for this vid. VID 0 is special
367 * in that it's the default and is always there in the fdb.
368 */
369 spin_lock_bh(&port->br->hash_lock);
370 fdb_delete_by_addr(port->br, port->dev->dev_addr, vid);
371 spin_unlock_bh(&port->br->hash_lock);
372 }
373
332 return __vlan_del(pv, vid); 374 return __vlan_del(pv, vid);
333} 375}
334 376
@@ -344,3 +386,22 @@ void nbp_vlan_flush(struct net_bridge_port *port)
344 386
345 __vlan_flush(pv); 387 __vlan_flush(pv);
346} 388}
389
390bool nbp_vlan_find(struct net_bridge_port *port, u16 vid)
391{
392 struct net_port_vlans *pv;
393 bool found = false;
394
395 rcu_read_lock();
396 pv = rcu_dereference(port->vlan_info);
397
398 if (!pv)
399 goto out;
400
401 if (test_bit(vid, pv->vlan_bitmap))
402 found = true;
403
404out:
405 rcu_read_unlock();
406 return found;
407}