diff options
| -rw-r--r-- | net/bridge/br_device.c | 3 | ||||
| -rw-r--r-- | net/bridge/br_fdb.c | 137 | ||||
| -rw-r--r-- | net/bridge/br_if.c | 6 | ||||
| -rw-r--r-- | net/bridge/br_input.c | 4 | ||||
| -rw-r--r-- | net/bridge/br_private.h | 13 | ||||
| -rw-r--r-- | net/bridge/br_stp_if.c | 2 | ||||
| -rw-r--r-- | net/bridge/br_vlan.c | 27 |
7 files changed, 129 insertions, 63 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index d9a9b0fc1795..63f0455c0bc3 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c | |||
| @@ -187,8 +187,7 @@ static int br_set_mac_address(struct net_device *dev, void *p) | |||
| 187 | 187 | ||
| 188 | spin_lock_bh(&br->lock); | 188 | spin_lock_bh(&br->lock); |
| 189 | if (!ether_addr_equal(dev->dev_addr, addr->sa_data)) { | 189 | if (!ether_addr_equal(dev->dev_addr, addr->sa_data)) { |
| 190 | memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); | 190 | /* Mac address will be changed in br_stp_change_bridge_id(). */ |
| 191 | br_fdb_change_mac_address(br, addr->sa_data); | ||
| 192 | br_stp_change_bridge_id(br, addr->sa_data); | 191 | br_stp_change_bridge_id(br, addr->sa_data); |
| 193 | } | 192 | } |
| 194 | spin_unlock_bh(&br->lock); | 193 | spin_unlock_bh(&br->lock); |
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index c5f5a4a933f4..9203d5a1943f 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 | ||
| 29 | static struct kmem_cache *br_fdb_cache __read_mostly; | 29 | static struct kmem_cache *br_fdb_cache __read_mostly; |
| 30 | static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, | ||
| 31 | const unsigned char *addr, | ||
| 32 | __u16 vid); | ||
| 30 | static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | 33 | static 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); |
| 32 | static void fdb_notify(struct net_bridge *br, | 35 | static void fdb_notify(struct net_bridge *br, |
| @@ -89,11 +92,57 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) | |||
| 89 | call_rcu(&f->rcu, fdb_rcu_free); | 92 | call_rcu(&f->rcu, fdb_rcu_free); |
| 90 | } | 93 | } |
| 91 | 94 | ||
| 95 | /* Delete a local entry if no other port had the same address. */ | ||
| 96 | static void fdb_delete_local(struct net_bridge *br, | ||
| 97 | const struct net_bridge_port *p, | ||
| 98 | struct net_bridge_fdb_entry *f) | ||
| 99 | { | ||
| 100 | const unsigned char *addr = f->addr.addr; | ||
| 101 | u16 vid = f->vlan_id; | ||
| 102 | struct net_bridge_port *op; | ||
| 103 | |||
| 104 | /* Maybe another port has same hw addr? */ | ||
| 105 | list_for_each_entry(op, &br->port_list, list) { | ||
| 106 | if (op != p && ether_addr_equal(op->dev->dev_addr, addr) && | ||
| 107 | (!vid || nbp_vlan_find(op, vid))) { | ||
| 108 | f->dst = op; | ||
| 109 | f->added_by_user = 0; | ||
| 110 | return; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | /* Maybe bridge device has same hw addr? */ | ||
| 115 | if (p && ether_addr_equal(br->dev->dev_addr, addr) && | ||
| 116 | (!vid || br_vlan_find(br, vid))) { | ||
| 117 | f->dst = NULL; | ||
| 118 | f->added_by_user = 0; | ||
| 119 | return; | ||
| 120 | } | ||
| 121 | |||
| 122 | fdb_delete(br, f); | ||
| 123 | } | ||
| 124 | |||
| 125 | void 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 | |||
| 92 | void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) | 139 | void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) |
| 93 | { | 140 | { |
| 94 | struct net_bridge *br = p->br; | 141 | struct net_bridge *br = p->br; |
| 95 | bool no_vlan = (nbp_get_vlan_info(p) == NULL) ? true : false; | 142 | struct net_port_vlans *pv = nbp_get_vlan_info(p); |
| 143 | bool no_vlan = !pv; | ||
| 96 | int i; | 144 | int i; |
| 145 | u16 vid; | ||
| 97 | 146 | ||
| 98 | spin_lock_bh(&br->hash_lock); | 147 | spin_lock_bh(&br->hash_lock); |
| 99 | 148 | ||
| @@ -104,38 +153,34 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) | |||
| 104 | struct net_bridge_fdb_entry *f; | 153 | struct net_bridge_fdb_entry *f; |
| 105 | 154 | ||
| 106 | f = hlist_entry(h, struct net_bridge_fdb_entry, hlist); | 155 | f = hlist_entry(h, struct net_bridge_fdb_entry, hlist); |
| 107 | if (f->dst == p && f->is_local) { | 156 | if (f->dst == p && f->is_local && !f->added_by_user) { |
| 108 | /* maybe another port has same hw addr? */ | ||
| 109 | struct net_bridge_port *op; | ||
| 110 | u16 vid = f->vlan_id; | ||
| 111 | list_for_each_entry(op, &br->port_list, list) { | ||
| 112 | if (op != p && | ||
| 113 | ether_addr_equal(op->dev->dev_addr, | ||
| 114 | f->addr.addr) && | ||
| 115 | nbp_vlan_find(op, vid)) { | ||
| 116 | f->dst = op; | ||
| 117 | goto insert; | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | /* delete old one */ | 157 | /* delete old one */ |
| 122 | fdb_delete(br, f); | 158 | fdb_delete_local(br, p, f); |
| 123 | insert: | ||
| 124 | /* insert new address, may fail if invalid | ||
| 125 | * address or dup. | ||
| 126 | */ | ||
| 127 | fdb_insert(br, p, newaddr, vid); | ||
| 128 | 159 | ||
| 129 | /* if this port has no vlan information | 160 | /* if this port has no vlan information |
| 130 | * configured, we can safely be done at | 161 | * configured, we can safely be done at |
| 131 | * this point. | 162 | * this point. |
| 132 | */ | 163 | */ |
| 133 | if (no_vlan) | 164 | if (no_vlan) |
| 134 | goto done; | 165 | goto insert; |
| 135 | } | 166 | } |
| 136 | } | 167 | } |
| 137 | } | 168 | } |
| 138 | 169 | ||
| 170 | insert: | ||
| 171 | /* insert new address, may fail if invalid address or dup. */ | ||
| 172 | fdb_insert(br, p, newaddr, 0); | ||
| 173 | |||
| 174 | if (no_vlan) | ||
| 175 | goto done; | ||
| 176 | |||
| 177 | /* Now add entries for every VLAN configured on the port. | ||
| 178 | * This function runs under RTNL so the bitmap will not change | ||
| 179 | * from under us. | ||
| 180 | */ | ||
| 181 | for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) | ||
| 182 | fdb_insert(br, p, newaddr, vid); | ||
| 183 | |||
| 139 | done: | 184 | done: |
| 140 | spin_unlock_bh(&br->hash_lock); | 185 | spin_unlock_bh(&br->hash_lock); |
| 141 | } | 186 | } |
| @@ -146,10 +191,12 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) | |||
| 146 | struct net_port_vlans *pv; | 191 | struct net_port_vlans *pv; |
| 147 | u16 vid = 0; | 192 | u16 vid = 0; |
| 148 | 193 | ||
| 194 | spin_lock_bh(&br->hash_lock); | ||
| 195 | |||
| 149 | /* If old entry was unassociated with any port, then delete it. */ | 196 | /* If old entry was unassociated with any port, then delete it. */ |
| 150 | f = __br_fdb_get(br, br->dev->dev_addr, 0); | 197 | f = __br_fdb_get(br, br->dev->dev_addr, 0); |
| 151 | if (f && f->is_local && !f->dst) | 198 | if (f && f->is_local && !f->dst) |
| 152 | fdb_delete(br, f); | 199 | fdb_delete_local(br, NULL, f); |
| 153 | 200 | ||
| 154 | fdb_insert(br, NULL, newaddr, 0); | 201 | fdb_insert(br, NULL, newaddr, 0); |
| 155 | 202 | ||
| @@ -159,14 +206,16 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) | |||
| 159 | */ | 206 | */ |
| 160 | pv = br_get_vlan_info(br); | 207 | pv = br_get_vlan_info(br); |
| 161 | if (!pv) | 208 | if (!pv) |
| 162 | return; | 209 | goto out; |
| 163 | 210 | ||
| 164 | for_each_set_bit_from(vid, pv->vlan_bitmap, VLAN_N_VID) { | 211 | for_each_set_bit_from(vid, pv->vlan_bitmap, VLAN_N_VID) { |
| 165 | f = __br_fdb_get(br, br->dev->dev_addr, vid); | 212 | f = __br_fdb_get(br, br->dev->dev_addr, vid); |
| 166 | if (f && f->is_local && !f->dst) | 213 | if (f && f->is_local && !f->dst) |
| 167 | fdb_delete(br, f); | 214 | fdb_delete_local(br, NULL, f); |
| 168 | fdb_insert(br, NULL, newaddr, vid); | 215 | fdb_insert(br, NULL, newaddr, vid); |
| 169 | } | 216 | } |
| 217 | out: | ||
| 218 | spin_unlock_bh(&br->hash_lock); | ||
| 170 | } | 219 | } |
| 171 | 220 | ||
| 172 | void br_fdb_cleanup(unsigned long _data) | 221 | void br_fdb_cleanup(unsigned long _data) |
| @@ -235,25 +284,11 @@ void br_fdb_delete_by_port(struct net_bridge *br, | |||
| 235 | 284 | ||
| 236 | if (f->is_static && !do_all) | 285 | if (f->is_static && !do_all) |
| 237 | continue; | 286 | continue; |
| 238 | /* | ||
| 239 | * if multiple ports all have the same device address | ||
| 240 | * then when one port is deleted, assign | ||
| 241 | * the local entry to other port | ||
| 242 | */ | ||
| 243 | if (f->is_local) { | ||
| 244 | struct net_bridge_port *op; | ||
| 245 | list_for_each_entry(op, &br->port_list, list) { | ||
| 246 | if (op != p && | ||
| 247 | ether_addr_equal(op->dev->dev_addr, | ||
| 248 | f->addr.addr)) { | ||
| 249 | f->dst = op; | ||
| 250 | goto skip_delete; | ||
| 251 | } | ||
| 252 | } | ||
| 253 | } | ||
| 254 | 287 | ||
| 255 | fdb_delete(br, f); | 288 | if (f->is_local) |
| 256 | skip_delete: ; | 289 | fdb_delete_local(br, p, f); |
| 290 | else | ||
| 291 | fdb_delete(br, f); | ||
| 257 | } | 292 | } |
| 258 | } | 293 | } |
| 259 | spin_unlock_bh(&br->hash_lock); | 294 | spin_unlock_bh(&br->hash_lock); |
| @@ -397,6 +432,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, | |||
| 397 | fdb->vlan_id = vid; | 432 | fdb->vlan_id = vid; |
| 398 | fdb->is_local = 0; | 433 | fdb->is_local = 0; |
| 399 | fdb->is_static = 0; | 434 | fdb->is_static = 0; |
| 435 | fdb->added_by_user = 0; | ||
| 400 | fdb->updated = fdb->used = jiffies; | 436 | fdb->updated = fdb->used = jiffies; |
| 401 | hlist_add_head_rcu(&fdb->hlist, head); | 437 | hlist_add_head_rcu(&fdb->hlist, head); |
| 402 | } | 438 | } |
| @@ -447,7 +483,7 @@ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | |||
| 447 | } | 483 | } |
| 448 | 484 | ||
| 449 | void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, | 485 | void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, |
| 450 | const unsigned char *addr, u16 vid) | 486 | const unsigned char *addr, u16 vid, bool added_by_user) |
| 451 | { | 487 | { |
| 452 | struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; | 488 | struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; |
| 453 | struct net_bridge_fdb_entry *fdb; | 489 | struct net_bridge_fdb_entry *fdb; |
| @@ -473,13 +509,18 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, | |||
| 473 | /* fastpath: update of existing entry */ | 509 | /* fastpath: update of existing entry */ |
| 474 | fdb->dst = source; | 510 | fdb->dst = source; |
| 475 | fdb->updated = jiffies; | 511 | fdb->updated = jiffies; |
| 512 | if (unlikely(added_by_user)) | ||
| 513 | fdb->added_by_user = 1; | ||
| 476 | } | 514 | } |
| 477 | } else { | 515 | } else { |
| 478 | spin_lock(&br->hash_lock); | 516 | spin_lock(&br->hash_lock); |
| 479 | if (likely(!fdb_find(head, addr, vid))) { | 517 | if (likely(!fdb_find(head, addr, vid))) { |
| 480 | fdb = fdb_create(head, source, addr, vid); | 518 | fdb = fdb_create(head, source, addr, vid); |
| 481 | if (fdb) | 519 | if (fdb) { |
| 520 | if (unlikely(added_by_user)) | ||
| 521 | fdb->added_by_user = 1; | ||
| 482 | fdb_notify(br, fdb, RTM_NEWNEIGH); | 522 | fdb_notify(br, fdb, RTM_NEWNEIGH); |
| 523 | } | ||
| 483 | } | 524 | } |
| 484 | /* else we lose race and someone else inserts | 525 | /* else we lose race and someone else inserts |
| 485 | * it first, don't bother updating | 526 | * it first, don't bother updating |
| @@ -647,6 +688,7 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, | |||
| 647 | 688 | ||
| 648 | modified = true; | 689 | modified = true; |
| 649 | } | 690 | } |
| 691 | fdb->added_by_user = 1; | ||
| 650 | 692 | ||
| 651 | fdb->used = jiffies; | 693 | fdb->used = jiffies; |
| 652 | if (modified) { | 694 | if (modified) { |
| @@ -664,7 +706,7 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge_port *p, | |||
| 664 | 706 | ||
| 665 | if (ndm->ndm_flags & NTF_USE) { | 707 | if (ndm->ndm_flags & NTF_USE) { |
| 666 | rcu_read_lock(); | 708 | rcu_read_lock(); |
| 667 | br_fdb_update(p->br, p, addr, vid); | 709 | br_fdb_update(p->br, p, addr, vid, true); |
| 668 | rcu_read_unlock(); | 710 | rcu_read_unlock(); |
| 669 | } else { | 711 | } else { |
| 670 | spin_lock_bh(&p->br->hash_lock); | 712 | spin_lock_bh(&p->br->hash_lock); |
| @@ -749,8 +791,7 @@ out: | |||
| 749 | return err; | 791 | return err; |
| 750 | } | 792 | } |
| 751 | 793 | ||
| 752 | int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, | 794 | static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vlan) |
| 753 | u16 vlan) | ||
| 754 | { | 795 | { |
| 755 | struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)]; | 796 | struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)]; |
| 756 | struct net_bridge_fdb_entry *fdb; | 797 | struct net_bridge_fdb_entry *fdb; |
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index cffe1d666ba1..54d207d3a31c 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c | |||
| @@ -389,6 +389,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) | |||
| 389 | if (br->dev->needed_headroom < dev->needed_headroom) | 389 | if (br->dev->needed_headroom < dev->needed_headroom) |
| 390 | br->dev->needed_headroom = dev->needed_headroom; | 390 | br->dev->needed_headroom = dev->needed_headroom; |
| 391 | 391 | ||
| 392 | if (br_fdb_insert(br, p, dev->dev_addr, 0)) | ||
| 393 | netdev_err(dev, "failed insert local address bridge forwarding table\n"); | ||
| 394 | |||
| 392 | spin_lock_bh(&br->lock); | 395 | spin_lock_bh(&br->lock); |
| 393 | changed_addr = br_stp_recalculate_bridge_id(br); | 396 | changed_addr = br_stp_recalculate_bridge_id(br); |
| 394 | 397 | ||
| @@ -404,9 +407,6 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) | |||
| 404 | 407 | ||
| 405 | dev_set_mtu(br->dev, br_min_mtu(br)); | 408 | dev_set_mtu(br->dev, br_min_mtu(br)); |
| 406 | 409 | ||
| 407 | if (br_fdb_insert(br, p, dev->dev_addr, 0)) | ||
| 408 | netdev_err(dev, "failed insert local address bridge forwarding table\n"); | ||
| 409 | |||
| 410 | kobject_uevent(&p->kobj, KOBJ_ADD); | 410 | kobject_uevent(&p->kobj, KOBJ_ADD); |
| 411 | 411 | ||
| 412 | return 0; | 412 | return 0; |
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index bf8dc7d308d6..28d544627422 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c | |||
| @@ -77,7 +77,7 @@ int br_handle_frame_finish(struct sk_buff *skb) | |||
| 77 | /* insert into forwarding database after filtering to avoid spoofing */ | 77 | /* insert into forwarding database after filtering to avoid spoofing */ |
| 78 | br = p->br; | 78 | br = p->br; |
| 79 | if (p->flags & BR_LEARNING) | 79 | if (p->flags & BR_LEARNING) |
| 80 | br_fdb_update(br, p, eth_hdr(skb)->h_source, vid); | 80 | br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, false); |
| 81 | 81 | ||
| 82 | if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) && | 82 | if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) && |
| 83 | br_multicast_rcv(br, p, skb, vid)) | 83 | br_multicast_rcv(br, p, skb, vid)) |
| @@ -148,7 +148,7 @@ static int br_handle_local_finish(struct sk_buff *skb) | |||
| 148 | 148 | ||
| 149 | br_vlan_get_tag(skb, &vid); | 149 | br_vlan_get_tag(skb, &vid); |
| 150 | if (p->flags & BR_LEARNING) | 150 | if (p->flags & BR_LEARNING) |
| 151 | br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid); | 151 | br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid, false); |
| 152 | return 0; /* process further */ | 152 | return 0; /* process further */ |
| 153 | } | 153 | } |
| 154 | 154 | ||
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index fcd12333c59b..3ba11bc99b65 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h | |||
| @@ -104,6 +104,7 @@ struct net_bridge_fdb_entry | |||
| 104 | mac_addr addr; | 104 | mac_addr addr; |
| 105 | unsigned char is_local; | 105 | unsigned char is_local; |
| 106 | unsigned char is_static; | 106 | unsigned char is_static; |
| 107 | unsigned char added_by_user; | ||
| 107 | __u16 vlan_id; | 108 | __u16 vlan_id; |
| 108 | }; | 109 | }; |
| 109 | 110 | ||
| @@ -370,6 +371,9 @@ static inline void br_netpoll_disable(struct net_bridge_port *p) | |||
| 370 | int br_fdb_init(void); | 371 | int br_fdb_init(void); |
| 371 | void br_fdb_fini(void); | 372 | void br_fdb_fini(void); |
| 372 | void br_fdb_flush(struct net_bridge *br); | 373 | void br_fdb_flush(struct net_bridge *br); |
| 374 | void br_fdb_find_delete_local(struct net_bridge *br, | ||
| 375 | const struct net_bridge_port *p, | ||
| 376 | const unsigned char *addr, u16 vid); | ||
| 373 | void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr); | 377 | void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr); |
| 374 | void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr); | 378 | void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr); |
| 375 | void br_fdb_cleanup(unsigned long arg); | 379 | void br_fdb_cleanup(unsigned long arg); |
| @@ -383,8 +387,7 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long count, | |||
| 383 | int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | 387 | int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, |
| 384 | const unsigned char *addr, u16 vid); | 388 | const unsigned char *addr, u16 vid); |
| 385 | void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, | 389 | void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, |
| 386 | const unsigned char *addr, u16 vid); | 390 | const unsigned char *addr, u16 vid, bool added_by_user); |
| 387 | int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vid); | ||
| 388 | 391 | ||
| 389 | int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], | 392 | int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], |
| 390 | struct net_device *dev, const unsigned char *addr); | 393 | struct net_device *dev, const unsigned char *addr); |
| @@ -584,6 +587,7 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br, | |||
| 584 | int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags); | 587 | int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags); |
| 585 | int br_vlan_delete(struct net_bridge *br, u16 vid); | 588 | int br_vlan_delete(struct net_bridge *br, u16 vid); |
| 586 | void br_vlan_flush(struct net_bridge *br); | 589 | void br_vlan_flush(struct net_bridge *br); |
| 590 | bool br_vlan_find(struct net_bridge *br, u16 vid); | ||
| 587 | int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); | 591 | int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); |
| 588 | int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); | 592 | int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); |
| 589 | int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); | 593 | int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); |
| @@ -665,6 +669,11 @@ static inline void br_vlan_flush(struct net_bridge *br) | |||
| 665 | { | 669 | { |
| 666 | } | 670 | } |
| 667 | 671 | ||
| 672 | static inline bool br_vlan_find(struct net_bridge *br, u16 vid) | ||
| 673 | { | ||
| 674 | return false; | ||
| 675 | } | ||
| 676 | |||
| 668 | static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) | 677 | static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) |
| 669 | { | 678 | { |
| 670 | return -EOPNOTSUPP; | 679 | return -EOPNOTSUPP; |
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index 656a6f3e40de..189ba1e7d851 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c | |||
| @@ -194,6 +194,8 @@ void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr) | |||
| 194 | 194 | ||
| 195 | wasroot = br_is_root_bridge(br); | 195 | wasroot = br_is_root_bridge(br); |
| 196 | 196 | ||
| 197 | br_fdb_change_mac_address(br, addr); | ||
| 198 | |||
| 197 | memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN); | 199 | memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN); |
| 198 | memcpy(br->bridge_id.addr, addr, ETH_ALEN); | 200 | memcpy(br->bridge_id.addr, addr, ETH_ALEN); |
| 199 | memcpy(br->dev->dev_addr, addr, ETH_ALEN); | 201 | memcpy(br->dev->dev_addr, addr, ETH_ALEN); |
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 4ca4d0a0151c..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; |
| @@ -295,6 +293,25 @@ void br_vlan_flush(struct net_bridge *br) | |||
| 295 | __vlan_flush(pv); | 293 | __vlan_flush(pv); |
| 296 | } | 294 | } |
| 297 | 295 | ||
| 296 | bool br_vlan_find(struct net_bridge *br, u16 vid) | ||
| 297 | { | ||
| 298 | struct net_port_vlans *pv; | ||
| 299 | bool found = false; | ||
| 300 | |||
| 301 | rcu_read_lock(); | ||
| 302 | pv = rcu_dereference(br->vlan_info); | ||
| 303 | |||
| 304 | if (!pv) | ||
| 305 | goto out; | ||
| 306 | |||
| 307 | if (test_bit(vid, pv->vlan_bitmap)) | ||
| 308 | found = true; | ||
| 309 | |||
| 310 | out: | ||
| 311 | rcu_read_unlock(); | ||
| 312 | return found; | ||
| 313 | } | ||
| 314 | |||
| 298 | int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val) | 315 | int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val) |
| 299 | { | 316 | { |
| 300 | if (!rtnl_trylock()) | 317 | if (!rtnl_trylock()) |
| @@ -359,9 +376,7 @@ int nbp_vlan_delete(struct net_bridge_port *port, u16 vid) | |||
| 359 | if (!pv) | 376 | if (!pv) |
| 360 | return -EINVAL; | 377 | return -EINVAL; |
| 361 | 378 | ||
| 362 | spin_lock_bh(&port->br->hash_lock); | 379 | br_fdb_find_delete_local(port->br, port, port->dev->dev_addr, vid); |
| 363 | fdb_delete_by_addr(port->br, port->dev->dev_addr, vid); | ||
| 364 | spin_unlock_bh(&port->br->hash_lock); | ||
| 365 | 380 | ||
| 366 | return __vlan_del(pv, vid); | 381 | return __vlan_del(pv, vid); |
| 367 | } | 382 | } |
