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