aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorToshiaki Makita <makita.toshiaki@lab.ntt.co.jp>2014-02-07 02:48:23 -0500
committerDavid S. Miller <davem@davemloft.net>2014-02-10 17:34:33 -0500
commit960b589f86c74ce582922fcb996103271081f4de (patch)
tree709073b8448afd5f468dca5586f309a86a774e54 /net
parent2b292fb4a57dc233e298a84196d33be0bc3828e4 (diff)
bridge: Properly check if local fdb entry can be deleted in br_fdb_change_mac_address
br_fdb_change_mac_address() doesn't check if the local entry has the same address as any of bridge ports. Although I'm not sure when it is beneficial, current implementation allow the bridge device to receive any mac address of its ports. To preserve this behavior, we have to check if the mac address of the entry being deleted is identical to that of any port. As this check is almost the same as that in br_fdb_changeaddr(), create a common function fdb_delete_local() and call it from br_fdb_changeadddr() and br_fdb_change_mac_address(). Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp> Acked-by: Vlad Yasevich <vyasevic@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/bridge/br_fdb.c57
1 files changed, 32 insertions, 25 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index b4005f5b28f4..15bf13d0b543 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -89,6 +89,34 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
89 call_rcu(&f->rcu, fdb_rcu_free); 89 call_rcu(&f->rcu, fdb_rcu_free);
90} 90}
91 91
92/* Delete a local entry if no other port had the same address. */
93static void fdb_delete_local(struct net_bridge *br,
94 const struct net_bridge_port *p,
95 struct net_bridge_fdb_entry *f)
96{
97 const unsigned char *addr = f->addr.addr;
98 u16 vid = f->vlan_id;
99 struct net_bridge_port *op;
100
101 /* Maybe another port has same hw addr? */
102 list_for_each_entry(op, &br->port_list, list) {
103 if (op != p && ether_addr_equal(op->dev->dev_addr, addr) &&
104 (!vid || nbp_vlan_find(op, vid))) {
105 f->dst = op;
106 return;
107 }
108 }
109
110 /* Maybe bridge device has same hw addr? */
111 if (p && ether_addr_equal(br->dev->dev_addr, addr) &&
112 (!vid || br_vlan_find(br, vid))) {
113 f->dst = NULL;
114 return;
115 }
116
117 fdb_delete(br, f);
118}
119
92void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) 120void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
93{ 121{
94 struct net_bridge *br = p->br; 122 struct net_bridge *br = p->br;
@@ -107,30 +135,9 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
107 135
108 f = hlist_entry(h, struct net_bridge_fdb_entry, hlist); 136 f = hlist_entry(h, struct net_bridge_fdb_entry, hlist);
109 if (f->dst == p && f->is_local && !f->added_by_user) { 137 if (f->dst == p && f->is_local && !f->added_by_user) {
110 /* maybe another port has same hw addr? */
111 struct net_bridge_port *op;
112 u16 vid = f->vlan_id;
113 list_for_each_entry(op, &br->port_list, list) {
114 if (op != p &&
115 ether_addr_equal(op->dev->dev_addr,
116 f->addr.addr) &&
117 (!vid || nbp_vlan_find(op, vid))) {
118 f->dst = op;
119 goto skip_delete;
120 }
121 }
122
123 /* maybe bridge device has same hw addr? */
124 if (ether_addr_equal(br->dev->dev_addr,
125 f->addr.addr) &&
126 (!vid || br_vlan_find(br, vid))) {
127 f->dst = NULL;
128 goto skip_delete;
129 }
130
131 /* delete old one */ 138 /* delete old one */
132 fdb_delete(br, f); 139 fdb_delete_local(br, p, f);
133skip_delete: 140
134 /* if this port has no vlan information 141 /* if this port has no vlan information
135 * configured, we can safely be done at 142 * configured, we can safely be done at
136 * this point. 143 * this point.
@@ -168,7 +175,7 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
168 /* If old entry was unassociated with any port, then delete it. */ 175 /* If old entry was unassociated with any port, then delete it. */
169 f = __br_fdb_get(br, br->dev->dev_addr, 0); 176 f = __br_fdb_get(br, br->dev->dev_addr, 0);
170 if (f && f->is_local && !f->dst) 177 if (f && f->is_local && !f->dst)
171 fdb_delete(br, f); 178 fdb_delete_local(br, NULL, f);
172 179
173 fdb_insert(br, NULL, newaddr, 0); 180 fdb_insert(br, NULL, newaddr, 0);
174 181
@@ -183,7 +190,7 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
183 for_each_set_bit_from(vid, pv->vlan_bitmap, VLAN_N_VID) { 190 for_each_set_bit_from(vid, pv->vlan_bitmap, VLAN_N_VID) {
184 f = __br_fdb_get(br, br->dev->dev_addr, vid); 191 f = __br_fdb_get(br, br->dev->dev_addr, vid);
185 if (f && f->is_local && !f->dst) 192 if (f && f->is_local && !f->dst)
186 fdb_delete(br, f); 193 fdb_delete_local(br, NULL, f);
187 fdb_insert(br, NULL, newaddr, vid); 194 fdb_insert(br, NULL, newaddr, vid);
188 } 195 }
189} 196}