diff options
-rw-r--r-- | net/bridge/br_fdb.c | 61 | ||||
-rw-r--r-- | net/bridge/br_if.c | 2 | ||||
-rw-r--r-- | net/bridge/br_private.h | 10 | ||||
-rw-r--r-- | net/bridge/br_vlan.c | 77 |
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 | ||
29 | static struct kmem_cache *br_fdb_cache __read_mostly; | 29 | static struct kmem_cache *br_fdb_cache __read_mostly; |
30 | static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | 30 | static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, |
31 | const unsigned char *addr); | 31 | const unsigned char *addr, u16 vid); |
32 | static void fdb_notify(struct net_bridge *br, | 32 | static 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) | |||
92 | void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) | 92 | void 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; | 123 | insert: |
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 | ||
139 | done: | ||
128 | spin_unlock_bh(&br->hash_lock); | 140 | spin_unlock_bh(&br->hash_lock); |
129 | } | 141 | } |
130 | 142 | ||
131 | void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) | 143 | void 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 | ||
143 | void br_fdb_cleanup(unsigned long _data) | 174 | void 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 | ||
381 | static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | 412 | static 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 */ |
413 | int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | 444 | int 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 | ||
715 | static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, | 746 | int 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); |
383 | extern int br_fdb_insert(struct net_bridge *br, | 383 | extern 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); | ||
386 | extern void br_fdb_update(struct net_bridge *br, | 387 | extern 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); |
391 | extern int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vid); | ||
390 | 392 | ||
391 | extern int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], | 393 | extern 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); | |||
573 | extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); | 575 | extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); |
574 | extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); | 576 | extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); |
575 | extern void nbp_vlan_flush(struct net_bridge_port *port); | 577 | extern void nbp_vlan_flush(struct net_bridge_port *port); |
578 | extern bool nbp_vlan_find(struct net_bridge_port *port, u16 vid); | ||
576 | 579 | ||
577 | static inline struct net_port_vlans *br_get_vlan_info( | 580 | static 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 | ||
682 | static inline bool nbp_vlan_find(struct net_bridge_port *port, u16 vid) | ||
683 | { | ||
684 | return false; | ||
685 | } | ||
686 | |||
679 | static inline u16 br_vlan_get_tag(const struct sk_buff *skb, u16 *tag) | 687 | static 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 | ||
26 | static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags) | 26 | static 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 | |||
77 | out_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 | ||
59 | static int __vlan_del(struct net_port_vlans *v, u16 vid) | 83 | static 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 | |||
390 | bool 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 | |||
404 | out: | ||
405 | rcu_read_unlock(); | ||
406 | return found; | ||
407 | } | ||