diff options
Diffstat (limited to 'net/bridge/br_fdb.c')
-rw-r--r-- | net/bridge/br_fdb.c | 61 |
1 files changed, 46 insertions, 15 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; |