Blame view
net/bridge/br_fdb.c
27.1 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 |
/* * Forwarding database * Linux ethernet bridge * * Authors: * Lennert Buytenhek <buytenh@gnu.org> * |
1da177e4c Linux-2.6.12-rc2 |
8 9 10 11 12 13 14 15 |
* This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include <linux/kernel.h> #include <linux/init.h> |
82524746c rcu: split list.h... |
16 |
#include <linux/rculist.h> |
1da177e4c Linux-2.6.12-rc2 |
17 18 19 20 21 |
#include <linux/spinlock.h> #include <linux/times.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/jhash.h> |
3f8909231 bridge: simpler h... |
22 |
#include <linux/random.h> |
5a0e3ad6a include cleanup: ... |
23 |
#include <linux/slab.h> |
60063497a atomic: use <linu... |
24 |
#include <linux/atomic.h> |
3f8909231 bridge: simpler h... |
25 |
#include <asm/unaligned.h> |
2ba071ecb bridge: Add vlan ... |
26 |
#include <linux/if_vlan.h> |
b4ad7baa0 bridge: del exter... |
27 |
#include <net/switchdev.h> |
b74fd306e bridge: fdb add a... |
28 |
#include <trace/events/bridge.h> |
1da177e4c Linux-2.6.12-rc2 |
29 |
#include "br_private.h" |
e18b890bb [PATCH] slab: rem... |
30 |
static struct kmem_cache *br_fdb_cache __read_mostly; |
1da177e4c Linux-2.6.12-rc2 |
31 |
static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, |
bc9a25d21 bridge: Add vlan ... |
32 |
const unsigned char *addr, u16 vid); |
31e8a49c1 bridge: rearrange... |
33 34 |
static void fdb_notify(struct net_bridge *br, const struct net_bridge_fdb_entry *, int); |
1da177e4c Linux-2.6.12-rc2 |
35 |
|
3f8909231 bridge: simpler h... |
36 |
static u32 fdb_salt __read_mostly; |
87a596e0b bridge: check kme... |
37 |
int __init br_fdb_init(void) |
1da177e4c Linux-2.6.12-rc2 |
38 39 40 41 |
{ br_fdb_cache = kmem_cache_create("bridge_fdb_cache", sizeof(struct net_bridge_fdb_entry), 0, |
20c2df83d mm: Remove slab d... |
42 |
SLAB_HWCACHE_ALIGN, NULL); |
87a596e0b bridge: check kme... |
43 44 |
if (!br_fdb_cache) return -ENOMEM; |
3f8909231 bridge: simpler h... |
45 |
get_random_bytes(&fdb_salt, sizeof(fdb_salt)); |
87a596e0b bridge: check kme... |
46 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
47 |
} |
73afc9069 [BRIDGE]: Section... |
48 |
void br_fdb_fini(void) |
1da177e4c Linux-2.6.12-rc2 |
49 50 51 52 53 54 55 56 |
{ kmem_cache_destroy(br_fdb_cache); } /* if topology_changing then use forward_delay (default 15 sec) * otherwise keep longer (default 5 minutes) */ |
3f8909231 bridge: simpler h... |
57 |
static inline unsigned long hold_time(const struct net_bridge *br) |
1da177e4c Linux-2.6.12-rc2 |
58 59 60 |
{ return br->topology_change ? br->forward_delay : br->ageing_time; } |
3f8909231 bridge: simpler h... |
61 |
static inline int has_expired(const struct net_bridge *br, |
1da177e4c Linux-2.6.12-rc2 |
62 63 |
const struct net_bridge_fdb_entry *fdb) { |
eda7a5e88 bridge: don't ind... |
64 |
return !fdb->is_static && !fdb->added_by_external_learn && |
7cd8861ab bridge: track las... |
65 |
time_before_eq(fdb->updated + hold_time(br), jiffies); |
1da177e4c Linux-2.6.12-rc2 |
66 |
} |
2ba071ecb bridge: Add vlan ... |
67 |
static inline int br_mac_hash(const unsigned char *mac, __u16 vid) |
1da177e4c Linux-2.6.12-rc2 |
68 |
{ |
2ba071ecb bridge: Add vlan ... |
69 |
/* use 1 byte of OUI and 3 bytes of NIC */ |
3f8909231 bridge: simpler h... |
70 |
u32 key = get_unaligned((u32 *)(mac + 2)); |
2ba071ecb bridge: Add vlan ... |
71 |
return jhash_2words(key, vid, fdb_salt) & (BR_HASH_SIZE - 1); |
1da177e4c Linux-2.6.12-rc2 |
72 |
} |
da6782927 bridge: Simplify ... |
73 74 75 76 77 78 |
static void fdb_rcu_free(struct rcu_head *head) { struct net_bridge_fdb_entry *ent = container_of(head, struct net_bridge_fdb_entry, rcu); kmem_cache_free(br_fdb_cache, ent); } |
bfd0aeac5 bridge: fdb: conv... |
79 80 81 82 83 |
static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head, const unsigned char *addr, __u16 vid) { struct net_bridge_fdb_entry *f; |
410b3d48f bridge: fdb: add ... |
84 |
WARN_ON_ONCE(!rcu_read_lock_held()); |
bfd0aeac5 bridge: fdb: conv... |
85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
hlist_for_each_entry_rcu(f, head, hlist) if (ether_addr_equal(f->addr.addr, addr) && f->vlan_id == vid) break; return f; } /* requires bridge hash_lock */ static struct net_bridge_fdb_entry *br_fdb_find(struct net_bridge *br, const unsigned char *addr, __u16 vid) { struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; struct net_bridge_fdb_entry *fdb; |
d12c91769 bridge: resolve a... |
99 |
lockdep_assert_held_once(&br->hash_lock); |
410b3d48f bridge: fdb: add ... |
100 |
|
bfd0aeac5 bridge: fdb: conv... |
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
rcu_read_lock(); fdb = fdb_find_rcu(head, addr, vid); rcu_read_unlock(); return fdb; } struct net_bridge_fdb_entry *br_fdb_find_rcu(struct net_bridge *br, const unsigned char *addr, __u16 vid) { struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; return fdb_find_rcu(head, addr, vid); } |
145beee8d bridge: Add addre... |
116 117 118 119 120 |
/* When a static FDB entry is added, the mac address from the entry is * added to the bridge private HW address list and all required ports * are then updated with the new information. * Called under RTNL. */ |
020ec6ba2 bridge: rename fd... |
121 |
static void fdb_add_hw_addr(struct net_bridge *br, const unsigned char *addr) |
145beee8d bridge: Add addre... |
122 123 |
{ int err; |
a3f5ee71c bridge: use list_... |
124 |
struct net_bridge_port *p; |
145beee8d bridge: Add addre... |
125 126 127 128 129 130 131 132 133 134 135 136 137 |
ASSERT_RTNL(); list_for_each_entry(p, &br->port_list, list) { if (!br_promisc_port(p)) { err = dev_uc_add(p->dev, addr); if (err) goto undo; } } return; undo: |
a3f5ee71c bridge: use list_... |
138 139 140 |
list_for_each_entry_continue_reverse(p, &br->port_list, list) { if (!br_promisc_port(p)) dev_uc_del(p->dev, addr); |
145beee8d bridge: Add addre... |
141 142 143 144 145 146 147 148 |
} } /* When a static FDB entry is deleted, the HW address from that entry is * also removed from the bridge private HW address list and updates all * the ports with needed information. * Called under RTNL. */ |
020ec6ba2 bridge: rename fd... |
149 |
static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr) |
145beee8d bridge: Add addre... |
150 151 152 153 154 155 156 157 158 159 |
{ struct net_bridge_port *p; ASSERT_RTNL(); list_for_each_entry(p, &br->port_list, list) { if (!br_promisc_port(p)) dev_uc_del(p->dev, addr); } } |
31e8a49c1 bridge: rearrange... |
160 |
static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) |
1da177e4c Linux-2.6.12-rc2 |
161 |
{ |
b74fd306e bridge: fdb add a... |
162 |
trace_fdb_delete(br, f); |
145beee8d bridge: Add addre... |
163 |
if (f->is_static) |
020ec6ba2 bridge: rename fd... |
164 |
fdb_del_hw_addr(br, f->addr.addr); |
145beee8d bridge: Add addre... |
165 |
|
f7cdee8a7 bridge: move to w... |
166 |
hlist_del_init_rcu(&f->hlist); |
31e8a49c1 bridge: rearrange... |
167 |
fdb_notify(br, f, RTM_DELNEIGH); |
da6782927 bridge: Simplify ... |
168 |
call_rcu(&f->rcu, fdb_rcu_free); |
1da177e4c Linux-2.6.12-rc2 |
169 |
} |
960b589f8 bridge: Properly ... |
170 171 172 173 174 175 |
/* Delete a local entry if no other port had the same address. */ static void fdb_delete_local(struct net_bridge *br, const struct net_bridge_port *p, struct net_bridge_fdb_entry *f) { const unsigned char *addr = f->addr.addr; |
2594e9064 bridge: vlan: add... |
176 177 |
struct net_bridge_vlan_group *vg; const struct net_bridge_vlan *v; |
960b589f8 bridge: Properly ... |
178 |
struct net_bridge_port *op; |
2594e9064 bridge: vlan: add... |
179 |
u16 vid = f->vlan_id; |
960b589f8 bridge: Properly ... |
180 181 182 |
/* Maybe another port has same hw addr? */ list_for_each_entry(op, &br->port_list, list) { |
2594e9064 bridge: vlan: add... |
183 |
vg = nbp_vlan_group(op); |
960b589f8 bridge: Properly ... |
184 |
if (op != p && ether_addr_equal(op->dev->dev_addr, addr) && |
2594e9064 bridge: vlan: add... |
185 |
(!vid || br_vlan_find(vg, vid))) { |
960b589f8 bridge: Properly ... |
186 |
f->dst = op; |
a778e6d1a bridge: Properly ... |
187 |
f->added_by_user = 0; |
960b589f8 bridge: Properly ... |
188 189 190 |
return; } } |
2594e9064 bridge: vlan: add... |
191 192 |
vg = br_vlan_group(br); v = br_vlan_find(vg, vid); |
960b589f8 bridge: Properly ... |
193 194 |
/* Maybe bridge device has same hw addr? */ if (p && ether_addr_equal(br->dev->dev_addr, addr) && |
2594e9064 bridge: vlan: add... |
195 |
(!vid || (v && br_vlan_should_use(v)))) { |
960b589f8 bridge: Properly ... |
196 |
f->dst = NULL; |
a778e6d1a bridge: Properly ... |
197 |
f->added_by_user = 0; |
960b589f8 bridge: Properly ... |
198 199 200 201 202 |
return; } fdb_delete(br, f); } |
424bb9c97 bridge: Properly ... |
203 204 205 206 |
void br_fdb_find_delete_local(struct net_bridge *br, const struct net_bridge_port *p, const unsigned char *addr, u16 vid) { |
424bb9c97 bridge: Properly ... |
207 208 209 |
struct net_bridge_fdb_entry *f; spin_lock_bh(&br->hash_lock); |
bfd0aeac5 bridge: fdb: conv... |
210 |
f = br_fdb_find(br, addr, vid); |
424bb9c97 bridge: Properly ... |
211 212 213 214 |
if (f && f->is_local && !f->added_by_user && f->dst == p) fdb_delete_local(br, p, f); spin_unlock_bh(&br->hash_lock); } |
1da177e4c Linux-2.6.12-rc2 |
215 216 |
void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) { |
2594e9064 bridge: vlan: add... |
217 |
struct net_bridge_vlan_group *vg; |
1da177e4c Linux-2.6.12-rc2 |
218 |
struct net_bridge *br = p->br; |
2594e9064 bridge: vlan: add... |
219 |
struct net_bridge_vlan *v; |
1da177e4c Linux-2.6.12-rc2 |
220 |
int i; |
9d6f229fc [NET] BRIDGE: Fix... |
221 |
|
1da177e4c Linux-2.6.12-rc2 |
222 |
spin_lock_bh(&br->hash_lock); |
2594e9064 bridge: vlan: add... |
223 |
vg = nbp_vlan_group(p); |
1da177e4c Linux-2.6.12-rc2 |
224 225 226 227 228 229 230 |
/* Search all chains since old address/hash is unknown */ for (i = 0; i < BR_HASH_SIZE; i++) { struct hlist_node *h; hlist_for_each(h, &br->hash[i]) { struct net_bridge_fdb_entry *f; f = hlist_entry(h, struct net_bridge_fdb_entry, hlist); |
a5642ab47 bridge: Fix the w... |
231 |
if (f->dst == p && f->is_local && !f->added_by_user) { |
1da177e4c Linux-2.6.12-rc2 |
232 |
/* delete old one */ |
960b589f8 bridge: Properly ... |
233 |
fdb_delete_local(br, p, f); |
bc9a25d21 bridge: Add vlan ... |
234 235 236 237 |
/* if this port has no vlan information * configured, we can safely be done at * this point. */ |
2594e9064 bridge: vlan: add... |
238 |
if (!vg || !vg->num_vlans) |
2836882fe bridge: Fix the w... |
239 |
goto insert; |
1da177e4c Linux-2.6.12-rc2 |
240 241 242 |
} } } |
1da177e4c Linux-2.6.12-rc2 |
243 |
|
2836882fe bridge: Fix the w... |
244 245 246 |
insert: /* insert new address, may fail if invalid address or dup. */ fdb_insert(br, p, newaddr, 0); |
2594e9064 bridge: vlan: add... |
247 |
if (!vg || !vg->num_vlans) |
2836882fe bridge: Fix the w... |
248 249 250 251 252 253 |
goto done; /* Now add entries for every VLAN configured on the port. * This function runs under RTNL so the bitmap will not change * from under us. */ |
2594e9064 bridge: vlan: add... |
254 255 |
list_for_each_entry(v, &vg->vlan_list, vlist) fdb_insert(br, p, newaddr, v->vid); |
2836882fe bridge: Fix the w... |
256 |
|
bc9a25d21 bridge: Add vlan ... |
257 |
done: |
1da177e4c Linux-2.6.12-rc2 |
258 259 |
spin_unlock_bh(&br->hash_lock); } |
435988133 bridge: add local... |
260 261 |
void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) { |
2594e9064 bridge: vlan: add... |
262 |
struct net_bridge_vlan_group *vg; |
435988133 bridge: add local... |
263 |
struct net_bridge_fdb_entry *f; |
2594e9064 bridge: vlan: add... |
264 |
struct net_bridge_vlan *v; |
435988133 bridge: add local... |
265 |
|
ac4c88688 bridge: Prevent p... |
266 |
spin_lock_bh(&br->hash_lock); |
435988133 bridge: add local... |
267 |
/* If old entry was unassociated with any port, then delete it. */ |
bfd0aeac5 bridge: fdb: conv... |
268 |
f = br_fdb_find(br, br->dev->dev_addr, 0); |
7bb90c371 bridge: Fix probl... |
269 |
if (f && f->is_local && !f->dst && !f->added_by_user) |
960b589f8 bridge: Properly ... |
270 |
fdb_delete_local(br, NULL, f); |
435988133 bridge: add local... |
271 |
|
bc9a25d21 bridge: Add vlan ... |
272 |
fdb_insert(br, NULL, newaddr, 0); |
2594e9064 bridge: vlan: add... |
273 274 275 |
vg = br_vlan_group(br); if (!vg || !vg->num_vlans) goto out; |
bc9a25d21 bridge: Add vlan ... |
276 277 278 279 |
/* Now remove and add entries for every VLAN configured on the * bridge. This function runs under RTNL so the bitmap will not * change from under us. */ |
2594e9064 bridge: vlan: add... |
280 |
list_for_each_entry(v, &vg->vlan_list, vlist) { |
0b148def4 bridge: Don't ins... |
281 282 |
if (!br_vlan_should_use(v)) continue; |
bfd0aeac5 bridge: fdb: conv... |
283 |
f = br_fdb_find(br, br->dev->dev_addr, v->vid); |
7bb90c371 bridge: Fix probl... |
284 |
if (f && f->is_local && !f->dst && !f->added_by_user) |
960b589f8 bridge: Properly ... |
285 |
fdb_delete_local(br, NULL, f); |
2594e9064 bridge: vlan: add... |
286 |
fdb_insert(br, NULL, newaddr, v->vid); |
bc9a25d21 bridge: Add vlan ... |
287 |
} |
ac4c88688 bridge: Prevent p... |
288 289 |
out: spin_unlock_bh(&br->hash_lock); |
435988133 bridge: add local... |
290 |
} |
f7cdee8a7 bridge: move to w... |
291 |
void br_fdb_cleanup(struct work_struct *work) |
1da177e4c Linux-2.6.12-rc2 |
292 |
{ |
f7cdee8a7 bridge: move to w... |
293 294 |
struct net_bridge *br = container_of(work, struct net_bridge, gc_work.work); |
1da177e4c Linux-2.6.12-rc2 |
295 |
unsigned long delay = hold_time(br); |
f7cdee8a7 bridge: move to w... |
296 297 |
unsigned long work_delay = delay; unsigned long now = jiffies; |
1da177e4c Linux-2.6.12-rc2 |
298 |
int i; |
1da177e4c Linux-2.6.12-rc2 |
299 300 |
for (i = 0; i < BR_HASH_SIZE; i++) { struct net_bridge_fdb_entry *f; |
b67bfe0d4 hlist: drop the n... |
301 |
struct hlist_node *n; |
1da177e4c Linux-2.6.12-rc2 |
302 |
|
f7cdee8a7 bridge: move to w... |
303 304 305 306 |
if (!br->hash[i].first) continue; spin_lock_bh(&br->hash_lock); |
b67bfe0d4 hlist: drop the n... |
307 |
hlist_for_each_entry_safe(f, n, &br->hash[i], hlist) { |
071f77226 [BRIDGE]: Reduce ... |
308 |
unsigned long this_timer; |
f7cdee8a7 bridge: move to w... |
309 |
|
3fcf90111 Revert "bridge: L... |
310 |
if (f->is_static) |
071f77226 [BRIDGE]: Reduce ... |
311 |
continue; |
dcd45e064 bridge: don't age... |
312 313 |
if (f->added_by_external_learn) continue; |
7cd8861ab bridge: track las... |
314 |
this_timer = f->updated + delay; |
f7cdee8a7 bridge: move to w... |
315 316 317 |
if (time_after(this_timer, now)) work_delay = min(work_delay, this_timer - now); else |
31e8a49c1 bridge: rearrange... |
318 |
fdb_delete(br, f); |
1da177e4c Linux-2.6.12-rc2 |
319 |
} |
f7cdee8a7 bridge: move to w... |
320 321 |
spin_unlock_bh(&br->hash_lock); cond_resched(); |
1da177e4c Linux-2.6.12-rc2 |
322 |
} |
1da177e4c Linux-2.6.12-rc2 |
323 |
|
f7cdee8a7 bridge: move to w... |
324 325 326 |
/* Cleanup minimum 10 milliseconds apart */ work_delay = max_t(unsigned long, work_delay, msecs_to_jiffies(10)); mod_delayed_work(system_long_wq, &br->gc_work, work_delay); |
1da177e4c Linux-2.6.12-rc2 |
327 |
} |
9cf637473 bridge: add sysfs... |
328 329 330 331 332 333 334 335 |
/* Completely flush all dynamic entries in forwarding database.*/ void br_fdb_flush(struct net_bridge *br) { int i; spin_lock_bh(&br->hash_lock); for (i = 0; i < BR_HASH_SIZE; i++) { struct net_bridge_fdb_entry *f; |
b67bfe0d4 hlist: drop the n... |
336 337 |
struct hlist_node *n; hlist_for_each_entry_safe(f, n, &br->hash[i], hlist) { |
9cf637473 bridge: add sysfs... |
338 |
if (!f->is_static) |
31e8a49c1 bridge: rearrange... |
339 |
fdb_delete(br, f); |
9cf637473 bridge: add sysfs... |
340 341 342 343 |
} } spin_unlock_bh(&br->hash_lock); } |
1a620698c [BRIDGE]: flush f... |
344 |
|
25985edce Fix common misspe... |
345 |
/* Flush all entries referring to a specific port. |
9cf637473 bridge: add sysfs... |
346 |
* if do_all is set also flush static entries |
1ea2d020b bridge: vlan: flu... |
347 |
* if vid is set delete all entries that match the vlan_id |
9cf637473 bridge: add sysfs... |
348 |
*/ |
1a620698c [BRIDGE]: flush f... |
349 350 |
void br_fdb_delete_by_port(struct net_bridge *br, const struct net_bridge_port *p, |
1ea2d020b bridge: vlan: flu... |
351 |
u16 vid, |
1a620698c [BRIDGE]: flush f... |
352 |
int do_all) |
1da177e4c Linux-2.6.12-rc2 |
353 354 355 356 357 358 |
{ int i; spin_lock_bh(&br->hash_lock); for (i = 0; i < BR_HASH_SIZE; i++) { struct hlist_node *h, *g; |
9d6f229fc [NET] BRIDGE: Fix... |
359 |
|
1da177e4c Linux-2.6.12-rc2 |
360 361 362 |
hlist_for_each_safe(h, g, &br->hash[i]) { struct net_bridge_fdb_entry *f = hlist_entry(h, struct net_bridge_fdb_entry, hlist); |
9d6f229fc [NET] BRIDGE: Fix... |
363 |
if (f->dst != p) |
1da177e4c Linux-2.6.12-rc2 |
364 |
continue; |
1ea2d020b bridge: vlan: flu... |
365 366 367 |
if (!do_all) if (f->is_static || (vid && f->vlan_id != vid)) continue; |
1da177e4c Linux-2.6.12-rc2 |
368 |
|
a778e6d1a bridge: Properly ... |
369 370 371 372 |
if (f->is_local) fdb_delete_local(br, p, f); else fdb_delete(br, f); |
1da177e4c Linux-2.6.12-rc2 |
373 374 375 376 |
} } spin_unlock_bh(&br->hash_lock); } |
e6373c4c0 net:bridge: use I... |
377 |
#if IS_ENABLED(CONFIG_ATM_LANE) |
da6782927 bridge: Simplify ... |
378 379 380 |
/* Interface used by ATM LANE hook to test * if an addr is on some other bridge port */ int br_fdb_test_addr(struct net_device *dev, unsigned char *addr) |
1da177e4c Linux-2.6.12-rc2 |
381 382 |
{ struct net_bridge_fdb_entry *fdb; |
b5ed54e94 bridge: fix RCU r... |
383 |
struct net_bridge_port *port; |
da6782927 bridge: Simplify ... |
384 |
int ret; |
1da177e4c Linux-2.6.12-rc2 |
385 |
rcu_read_lock(); |
b5ed54e94 bridge: fix RCU r... |
386 387 388 389 |
port = br_port_get_rcu(dev); if (!port) ret = 0; else { |
bfd0aeac5 bridge: fdb: conv... |
390 |
fdb = br_fdb_find_rcu(port->br, addr, 0); |
435988133 bridge: add local... |
391 |
ret = fdb && fdb->dst && fdb->dst->dev != dev && |
b5ed54e94 bridge: fix RCU r... |
392 393 |
fdb->dst->state == BR_STATE_FORWARDING; } |
1da177e4c Linux-2.6.12-rc2 |
394 |
rcu_read_unlock(); |
1da177e4c Linux-2.6.12-rc2 |
395 |
|
da6782927 bridge: Simplify ... |
396 |
return ret; |
1da177e4c Linux-2.6.12-rc2 |
397 |
} |
da6782927 bridge: Simplify ... |
398 |
#endif /* CONFIG_ATM_LANE */ |
1da177e4c Linux-2.6.12-rc2 |
399 400 |
/* |
9d6f229fc [NET] BRIDGE: Fix... |
401 |
* Fill buffer with forwarding table records in |
1da177e4c Linux-2.6.12-rc2 |
402 403 404 405 406 407 408 |
* the API format. */ int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long maxnum, unsigned long skip) { struct __fdb_entry *fe = buf; int i, num = 0; |
1da177e4c Linux-2.6.12-rc2 |
409 410 411 412 413 414 |
struct net_bridge_fdb_entry *f; memset(buf, 0, maxnum*sizeof(struct __fdb_entry)); rcu_read_lock(); for (i = 0; i < BR_HASH_SIZE; i++) { |
b67bfe0d4 hlist: drop the n... |
415 |
hlist_for_each_entry_rcu(f, &br->hash[i], hlist) { |
1da177e4c Linux-2.6.12-rc2 |
416 417 |
if (num >= maxnum) goto out; |
9d6f229fc [NET] BRIDGE: Fix... |
418 |
if (has_expired(br, f)) |
1da177e4c Linux-2.6.12-rc2 |
419 |
continue; |
435988133 bridge: add local... |
420 421 422 |
/* ignore pseudo entry for local MAC address */ if (!f->dst) continue; |
1da177e4c Linux-2.6.12-rc2 |
423 424 425 426 427 428 429 |
if (skip) { --skip; continue; } /* convert from internal format to API */ memcpy(fe->mac_addr, f->addr.addr, ETH_ALEN); |
ae4f8fca4 bridge: forwardin... |
430 431 |
/* due to ABI compat need to split into hi/lo */ |
1da177e4c Linux-2.6.12-rc2 |
432 |
fe->port_no = f->dst->port_no; |
ae4f8fca4 bridge: forwardin... |
433 |
fe->port_hi = f->dst->port_no >> 8; |
1da177e4c Linux-2.6.12-rc2 |
434 435 |
fe->is_local = f->is_local; if (!f->is_static) |
a399a8053 time: jiffies_del... |
436 |
fe->ageing_timer_value = jiffies_delta_to_clock_t(jiffies - f->updated); |
1da177e4c Linux-2.6.12-rc2 |
437 438 439 440 441 442 443 444 445 446 |
++fe; ++num; } } out: rcu_read_unlock(); return num; } |
1da177e4c Linux-2.6.12-rc2 |
447 448 |
static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, struct net_bridge_port *source, |
2ba071ecb bridge: Add vlan ... |
449 |
const unsigned char *addr, |
b7af1472a bridge: set is_lo... |
450 451 452 |
__u16 vid, unsigned char is_local, unsigned char is_static) |
1da177e4c Linux-2.6.12-rc2 |
453 454 455 456 457 458 |
{ struct net_bridge_fdb_entry *fdb; fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC); if (fdb) { memcpy(fdb->addr.addr, addr, ETH_ALEN); |
1da177e4c Linux-2.6.12-rc2 |
459 |
fdb->dst = source; |
2ba071ecb bridge: Add vlan ... |
460 |
fdb->vlan_id = vid; |
b7af1472a bridge: set is_lo... |
461 462 |
fdb->is_local = is_local; fdb->is_static = is_static; |
a5642ab47 bridge: Fix the w... |
463 |
fdb->added_by_user = 0; |
cf6b8e1ee bridge: add API t... |
464 |
fdb->added_by_external_learn = 0; |
9fe8bcec0 net: bridge: Rece... |
465 |
fdb->offloaded = 0; |
7cd8861ab bridge: track las... |
466 |
fdb->updated = fdb->used = jiffies; |
1158f762e bridge: Don't put... |
467 |
hlist_add_head_rcu(&fdb->hlist, head); |
1da177e4c Linux-2.6.12-rc2 |
468 469 470 471 472 |
} return fdb; } static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, |
bc9a25d21 bridge: Add vlan ... |
473 |
const unsigned char *addr, u16 vid) |
1da177e4c Linux-2.6.12-rc2 |
474 |
{ |
bc9a25d21 bridge: Add vlan ... |
475 |
struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; |
1da177e4c Linux-2.6.12-rc2 |
476 477 478 479 |
struct net_bridge_fdb_entry *fdb; if (!is_valid_ether_addr(addr)) return -EINVAL; |
bfd0aeac5 bridge: fdb: conv... |
480 |
fdb = br_fdb_find(br, addr, vid); |
1da177e4c Linux-2.6.12-rc2 |
481 |
if (fdb) { |
9d6f229fc [NET] BRIDGE: Fix... |
482 |
/* it is okay to have multiple ports with same |
1da177e4c Linux-2.6.12-rc2 |
483 484 |
* address, just use the first one. */ |
9d6f229fc [NET] BRIDGE: Fix... |
485 |
if (fdb->is_local) |
1da177e4c Linux-2.6.12-rc2 |
486 |
return 0; |
de1dfeefe bridge: add addre... |
487 488 489 |
br_warn(br, "adding interface %s with same address as a received packet (addr:%pM, vlan:%u) ", source ? source->dev->name : br->dev->name, addr, vid); |
31e8a49c1 bridge: rearrange... |
490 |
fdb_delete(br, fdb); |
9d6f229fc [NET] BRIDGE: Fix... |
491 |
} |
1da177e4c Linux-2.6.12-rc2 |
492 |
|
b7af1472a bridge: set is_lo... |
493 |
fdb = fdb_create(head, source, addr, vid, 1, 1); |
03e9b64b8 bridge: change ar... |
494 |
if (!fdb) |
1da177e4c Linux-2.6.12-rc2 |
495 |
return -ENOMEM; |
020ec6ba2 bridge: rename fd... |
496 |
fdb_add_hw_addr(br, addr); |
31e8a49c1 bridge: rearrange... |
497 |
fdb_notify(br, fdb, RTM_NEWNEIGH); |
1da177e4c Linux-2.6.12-rc2 |
498 499 |
return 0; } |
03e9b64b8 bridge: change ar... |
500 |
/* Add entry for local address of interface */ |
1da177e4c Linux-2.6.12-rc2 |
501 |
int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, |
bc9a25d21 bridge: Add vlan ... |
502 |
const unsigned char *addr, u16 vid) |
1da177e4c Linux-2.6.12-rc2 |
503 504 505 506 |
{ int ret; spin_lock_bh(&br->hash_lock); |
bc9a25d21 bridge: Add vlan ... |
507 |
ret = fdb_insert(br, source, addr, vid); |
1da177e4c Linux-2.6.12-rc2 |
508 509 510 511 512 |
spin_unlock_bh(&br->hash_lock); return ret; } void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, |
a5642ab47 bridge: Fix the w... |
513 |
const unsigned char *addr, u16 vid, bool added_by_user) |
1da177e4c Linux-2.6.12-rc2 |
514 |
{ |
2ba071ecb bridge: Add vlan ... |
515 |
struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; |
1da177e4c Linux-2.6.12-rc2 |
516 |
struct net_bridge_fdb_entry *fdb; |
c65c7a306 bridge: notify us... |
517 |
bool fdb_modified = false; |
1da177e4c Linux-2.6.12-rc2 |
518 519 520 521 |
/* some users want to always flood. */ if (hold_time(br) == 0) return; |
df1c0b846 [BRIDGE]: Packets... |
522 523 524 525 |
/* ignore packets unless we are using this port */ if (!(source->state == BR_STATE_LEARNING || source->state == BR_STATE_FORWARDING)) return; |
2ba071ecb bridge: Add vlan ... |
526 |
fdb = fdb_find_rcu(head, addr, vid); |
1da177e4c Linux-2.6.12-rc2 |
527 528 529 |
if (likely(fdb)) { /* attempt to update an entry for a local interface */ if (unlikely(fdb->is_local)) { |
9d6f229fc [NET] BRIDGE: Fix... |
530 |
if (net_ratelimit()) |
de1dfeefe bridge: add addre... |
531 532 533 |
br_warn(br, "received packet on %s with own address as source address (addr:%pM, vlan:%u) ", source->dev->name, addr, vid); |
1da177e4c Linux-2.6.12-rc2 |
534 |
} else { |
ca6d4480f bridge: avoid unn... |
535 |
unsigned long now = jiffies; |
1da177e4c Linux-2.6.12-rc2 |
536 |
/* fastpath: update of existing entry */ |
c65c7a306 bridge: notify us... |
537 538 539 |
if (unlikely(source != fdb->dst)) { fdb->dst = source; fdb_modified = true; |
58073b32b net: bridge: Fix ... |
540 541 542 |
/* Take over HW learned entry */ if (unlikely(fdb->added_by_external_learn)) fdb->added_by_external_learn = 0; |
c65c7a306 bridge: notify us... |
543 |
} |
ca6d4480f bridge: avoid unn... |
544 545 |
if (now != fdb->updated) fdb->updated = now; |
a5642ab47 bridge: Fix the w... |
546 547 |
if (unlikely(added_by_user)) fdb->added_by_user = 1; |
e3cfddd57 bridge: add trace... |
548 549 |
if (unlikely(fdb_modified)) { trace_br_fdb_update(br, source, addr, vid, added_by_user); |
c65c7a306 bridge: notify us... |
550 |
fdb_notify(br, fdb, RTM_NEWNEIGH); |
e3cfddd57 bridge: add trace... |
551 |
} |
1da177e4c Linux-2.6.12-rc2 |
552 553 |
} } else { |
f8ae737de [BRIDGE]: forward... |
554 |
spin_lock(&br->hash_lock); |
bfd0aeac5 bridge: fdb: conv... |
555 |
if (likely(!fdb_find_rcu(head, addr, vid))) { |
b7af1472a bridge: set is_lo... |
556 |
fdb = fdb_create(head, source, addr, vid, 0, 0); |
a5642ab47 bridge: Fix the w... |
557 558 559 |
if (fdb) { if (unlikely(added_by_user)) fdb->added_by_user = 1; |
e3cfddd57 bridge: add trace... |
560 |
trace_br_fdb_update(br, source, addr, vid, added_by_user); |
31e8a49c1 bridge: rearrange... |
561 |
fdb_notify(br, fdb, RTM_NEWNEIGH); |
a5642ab47 bridge: Fix the w... |
562 |
} |
f58ee4e1a bridge: refactor ... |
563 |
} |
1da177e4c Linux-2.6.12-rc2 |
564 565 566 |
/* else we lose race and someone else inserts * it first, don't bother updating */ |
f8ae737de [BRIDGE]: forward... |
567 |
spin_unlock(&br->hash_lock); |
1da177e4c Linux-2.6.12-rc2 |
568 |
} |
1da177e4c Linux-2.6.12-rc2 |
569 |
} |
b078f0df6 bridge: add netli... |
570 |
|
3741873b4 bridge: allow add... |
571 572 |
static int fdb_to_nud(const struct net_bridge *br, const struct net_bridge_fdb_entry *fdb) |
b078f0df6 bridge: add netli... |
573 574 575 576 577 |
{ if (fdb->is_local) return NUD_PERMANENT; else if (fdb->is_static) return NUD_NOARP; |
3741873b4 bridge: allow add... |
578 |
else if (has_expired(br, fdb)) |
b078f0df6 bridge: add netli... |
579 580 581 582 |
return NUD_STALE; else return NUD_REACHABLE; } |
31e8a49c1 bridge: rearrange... |
583 |
static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br, |
b078f0df6 bridge: add netli... |
584 |
const struct net_bridge_fdb_entry *fdb, |
15e473046 netlink: Rename p... |
585 |
u32 portid, u32 seq, int type, unsigned int flags) |
b078f0df6 bridge: add netli... |
586 587 588 589 590 |
{ unsigned long now = jiffies; struct nda_cacheinfo ci; struct nlmsghdr *nlh; struct ndmsg *ndm; |
15e473046 netlink: Rename p... |
591 |
nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags); |
b078f0df6 bridge: add netli... |
592 593 |
if (nlh == NULL) return -EMSGSIZE; |
b078f0df6 bridge: add netli... |
594 595 596 597 |
ndm = nlmsg_data(nlh); ndm->ndm_family = AF_BRIDGE; ndm->ndm_pad1 = 0; ndm->ndm_pad2 = 0; |
9fe8bcec0 net: bridge: Rece... |
598 |
ndm->ndm_flags = 0; |
b078f0df6 bridge: add netli... |
599 |
ndm->ndm_type = 0; |
435988133 bridge: add local... |
600 |
ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex; |
3741873b4 bridge: allow add... |
601 |
ndm->ndm_state = fdb_to_nud(br, fdb); |
b078f0df6 bridge: add netli... |
602 |
|
9fe8bcec0 net: bridge: Rece... |
603 604 605 606 |
if (fdb->offloaded) ndm->ndm_flags |= NTF_OFFLOADED; if (fdb->added_by_external_learn) ndm->ndm_flags |= NTF_EXT_LEARNED; |
2eb812e65 bridge: Stop usin... |
607 608 |
if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->addr)) goto nla_put_failure; |
41c389d72 bridge: Add bridg... |
609 610 |
if (nla_put_u32(skb, NDA_MASTER, br->dev->ifindex)) goto nla_put_failure; |
b078f0df6 bridge: add netli... |
611 612 613 614 |
ci.ndm_used = jiffies_to_clock_t(now - fdb->used); ci.ndm_confirmed = 0; ci.ndm_updated = jiffies_to_clock_t(now - fdb->updated); ci.ndm_refcnt = 0; |
2eb812e65 bridge: Stop usin... |
615 616 |
if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci)) goto nla_put_failure; |
1690be63a bridge: Add vlan ... |
617 |
|
47fab41ab bridge: Don't inc... |
618 |
if (fdb->vlan_id && nla_put(skb, NDA_VLAN, sizeof(u16), &fdb->vlan_id)) |
1690be63a bridge: Add vlan ... |
619 |
goto nla_put_failure; |
053c095a8 netlink: make nlm... |
620 621 |
nlmsg_end(skb, nlh); return 0; |
b078f0df6 bridge: add netli... |
622 623 624 625 626 627 628 629 630 631 |
nla_put_failure: nlmsg_cancel(skb, nlh); return -EMSGSIZE; } static inline size_t fdb_nlmsg_size(void) { return NLMSG_ALIGN(sizeof(struct ndmsg)) + nla_total_size(ETH_ALEN) /* NDA_LLADDR */ |
41c389d72 bridge: Add bridg... |
632 |
+ nla_total_size(sizeof(u32)) /* NDA_MASTER */ |
1690be63a bridge: Add vlan ... |
633 |
+ nla_total_size(sizeof(u16)) /* NDA_VLAN */ |
b078f0df6 bridge: add netli... |
634 635 |
+ nla_total_size(sizeof(struct nda_cacheinfo)); } |
31e8a49c1 bridge: rearrange... |
636 637 |
static void fdb_notify(struct net_bridge *br, const struct net_bridge_fdb_entry *fdb, int type) |
b078f0df6 bridge: add netli... |
638 |
{ |
31e8a49c1 bridge: rearrange... |
639 |
struct net *net = dev_net(br->dev); |
b078f0df6 bridge: add netli... |
640 641 |
struct sk_buff *skb; int err = -ENOBUFS; |
6b26b51b1 net: bridge: Add ... |
642 |
br_switchdev_fdb_notify(fdb, type); |
b078f0df6 bridge: add netli... |
643 644 645 |
skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC); if (skb == NULL) goto errout; |
31e8a49c1 bridge: rearrange... |
646 |
err = fdb_fill_info(skb, br, fdb, 0, 0, type, 0); |
b078f0df6 bridge: add netli... |
647 648 649 650 651 652 653 654 655 |
if (err < 0) { /* -EMSGSIZE implies BUG in fdb_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); kfree_skb(skb); goto errout; } rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); return; errout: |
87e823b3d bridge: remove un... |
656 |
rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); |
b078f0df6 bridge: add netli... |
657 658 659 |
} /* Dump information about entries, in response to GETNEIGH */ |
77162022a net: add generic ... |
660 661 662 |
int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, |
5d5eacb34 bridge: fdb dumpi... |
663 |
struct net_device *filter_dev, |
d297653dd rtnetlink: fdb du... |
664 |
int *idx) |
b078f0df6 bridge: add netli... |
665 |
{ |
77162022a net: add generic ... |
666 |
struct net_bridge *br = netdev_priv(dev); |
d297653dd rtnetlink: fdb du... |
667 |
int err = 0; |
77162022a net: add generic ... |
668 |
int i; |
b078f0df6 bridge: add netli... |
669 |
|
77162022a net: add generic ... |
670 671 |
if (!(dev->priv_flags & IFF_EBRIDGE)) goto out; |
b078f0df6 bridge: add netli... |
672 |
|
d297653dd rtnetlink: fdb du... |
673 674 675 676 677 |
if (!filter_dev) { err = ndo_dflt_fdb_dump(skb, cb, dev, NULL, idx); if (err < 0) goto out; } |
6cb69742d net: Do not call ... |
678 |
|
77162022a net: add generic ... |
679 |
for (i = 0; i < BR_HASH_SIZE; i++) { |
77162022a net: add generic ... |
680 |
struct net_bridge_fdb_entry *f; |
b078f0df6 bridge: add netli... |
681 |
|
b67bfe0d4 hlist: drop the n... |
682 |
hlist_for_each_entry_rcu(f, &br->hash[i], hlist) { |
472681d57 net: ndo_fdb_dump... |
683 |
|
d297653dd rtnetlink: fdb du... |
684 |
if (*idx < cb->args[2]) |
77162022a net: add generic ... |
685 |
goto skip; |
5e6d24358 bridge: netlink d... |
686 687 688 689 |
if (filter_dev && (!f->dst || f->dst->dev != filter_dev)) { if (filter_dev != dev) goto skip; |
6cb69742d net: Do not call ... |
690 |
/* !f->dst is a special case for bridge |
5e6d24358 bridge: netlink d... |
691 692 693 694 695 696 697 |
* It means the MAC belongs to the bridge * Therefore need a little more filtering * we only want to dump the !f->dst case */ if (f->dst) goto skip; } |
6cb69742d net: Do not call ... |
698 699 |
if (!filter_dev && f->dst) goto skip; |
5d5eacb34 bridge: fdb dumpi... |
700 |
|
472681d57 net: ndo_fdb_dump... |
701 702 703 704 705 |
err = fdb_fill_info(skb, br, f, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWNEIGH, NLM_F_MULTI); |
d297653dd rtnetlink: fdb du... |
706 707 |
if (err < 0) goto out; |
b078f0df6 bridge: add netli... |
708 |
skip: |
d297653dd rtnetlink: fdb du... |
709 |
*idx += 1; |
b078f0df6 bridge: add netli... |
710 711 |
} } |
b078f0df6 bridge: add netli... |
712 |
|
77162022a net: add generic ... |
713 |
out: |
d297653dd rtnetlink: fdb du... |
714 |
return err; |
b078f0df6 bridge: add netli... |
715 |
} |
36fd2b63e bridge: allow cre... |
716 |
|
292d13989 bridge: add NTF_U... |
717 |
/* Update (create or replace) forwarding database entry */ |
7bb90c371 bridge: Fix probl... |
718 719 |
static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, const __u8 *addr, __u16 state, __u16 flags, __u16 vid) |
36fd2b63e bridge: allow cre... |
720 |
{ |
2ba071ecb bridge: Add vlan ... |
721 |
struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)]; |
36fd2b63e bridge: allow cre... |
722 |
struct net_bridge_fdb_entry *fdb; |
b0a397fb3 bridge: Add fdb d... |
723 |
bool modified = false; |
36fd2b63e bridge: allow cre... |
724 |
|
eb8d7baae bridge: skip fdb ... |
725 |
/* If the port cannot learn allow only local and static entries */ |
7bb90c371 bridge: Fix probl... |
726 |
if (source && !(state & NUD_PERMANENT) && !(state & NUD_NOARP) && |
eb8d7baae bridge: skip fdb ... |
727 728 729 |
!(source->state == BR_STATE_LEARNING || source->state == BR_STATE_FORWARDING)) return -EPERM; |
7bb90c371 bridge: Fix probl... |
730 731 732 733 734 735 |
if (!source && !(state & NUD_PERMANENT)) { pr_info("bridge: RTM_NEWNEIGH %s without NUD_PERMANENT ", br->dev->name); return -EINVAL; } |
bfd0aeac5 bridge: fdb: conv... |
736 |
fdb = br_fdb_find(br, addr, vid); |
64af1bac9 bridge: allow upd... |
737 738 739 |
if (fdb == NULL) { if (!(flags & NLM_F_CREATE)) return -ENOENT; |
36fd2b63e bridge: allow cre... |
740 |
|
b7af1472a bridge: set is_lo... |
741 |
fdb = fdb_create(head, source, addr, vid, 0, 0); |
64af1bac9 bridge: allow upd... |
742 743 |
if (!fdb) return -ENOMEM; |
b0a397fb3 bridge: Add fdb d... |
744 745 |
modified = true; |
64af1bac9 bridge: allow upd... |
746 747 748 |
} else { if (flags & NLM_F_EXCL) return -EEXIST; |
b0a397fb3 bridge: Add fdb d... |
749 750 751 752 753 |
if (fdb->dst != source) { fdb->dst = source; modified = true; } |
292d13989 bridge: add NTF_U... |
754 |
} |
3741873b4 bridge: allow add... |
755 |
if (fdb_to_nud(br, fdb) != state) { |
145beee8d bridge: Add addre... |
756 757 758 759 |
if (state & NUD_PERMANENT) { fdb->is_local = 1; if (!fdb->is_static) { fdb->is_static = 1; |
020ec6ba2 bridge: rename fd... |
760 |
fdb_add_hw_addr(br, addr); |
145beee8d bridge: Add addre... |
761 762 763 764 765 |
} } else if (state & NUD_NOARP) { fdb->is_local = 0; if (!fdb->is_static) { fdb->is_static = 1; |
020ec6ba2 bridge: rename fd... |
766 |
fdb_add_hw_addr(br, addr); |
145beee8d bridge: Add addre... |
767 768 |
} } else { |
292d13989 bridge: add NTF_U... |
769 |
fdb->is_local = 0; |
145beee8d bridge: Add addre... |
770 771 |
if (fdb->is_static) { fdb->is_static = 0; |
020ec6ba2 bridge: rename fd... |
772 |
fdb_del_hw_addr(br, addr); |
145beee8d bridge: Add addre... |
773 774 |
} } |
64af1bac9 bridge: allow upd... |
775 |
|
b0a397fb3 bridge: Add fdb d... |
776 777 |
modified = true; } |
a5642ab47 bridge: Fix the w... |
778 |
fdb->added_by_user = 1; |
b0a397fb3 bridge: Add fdb d... |
779 780 781 782 |
fdb->used = jiffies; if (modified) { fdb->updated = jiffies; |
31e8a49c1 bridge: rearrange... |
783 |
fdb_notify(br, fdb, RTM_NEWNEIGH); |
64af1bac9 bridge: allow upd... |
784 |
} |
36fd2b63e bridge: allow cre... |
785 |
|
36fd2b63e bridge: allow cre... |
786 787 |
return 0; } |
7bb90c371 bridge: Fix probl... |
788 789 790 |
static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br, struct net_bridge_port *p, const unsigned char *addr, u16 nlh_flags, u16 vid) |
1690be63a bridge: Add vlan ... |
791 792 793 794 |
{ int err = 0; if (ndm->ndm_flags & NTF_USE) { |
7bb90c371 bridge: Fix probl... |
795 796 797 798 799 800 |
if (!p) { pr_info("bridge: RTM_NEWNEIGH %s with NTF_USE is not supported ", br->dev->name); return -EINVAL; } |
c4c832f89 bridge: disable s... |
801 |
local_bh_disable(); |
1690be63a bridge: Add vlan ... |
802 |
rcu_read_lock(); |
7bb90c371 bridge: Fix probl... |
803 |
br_fdb_update(br, p, addr, vid, true); |
1690be63a bridge: Add vlan ... |
804 |
rcu_read_unlock(); |
c4c832f89 bridge: disable s... |
805 |
local_bh_enable(); |
eb100e0e2 net: bridge: allo... |
806 807 |
} else if (ndm->ndm_flags & NTF_EXT_LEARNED) { err = br_fdb_external_learn_add(br, p, addr, vid); |
1690be63a bridge: Add vlan ... |
808 |
} else { |
7bb90c371 bridge: Fix probl... |
809 810 |
spin_lock_bh(&br->hash_lock); err = fdb_add_entry(br, p, addr, ndm->ndm_state, |
1690be63a bridge: Add vlan ... |
811 |
nlh_flags, vid); |
7bb90c371 bridge: Fix probl... |
812 |
spin_unlock_bh(&br->hash_lock); |
1690be63a bridge: Add vlan ... |
813 814 815 816 |
} return err; } |
36fd2b63e bridge: allow cre... |
817 |
/* Add new permanent fdb entry with RTM_NEWNEIGH */ |
edc7d5732 netlink: add attr... |
818 819 |
int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, |
f6f6424ba net: make vid as ... |
820 |
const unsigned char *addr, u16 vid, u16 nlh_flags) |
36fd2b63e bridge: allow cre... |
821 |
{ |
2594e9064 bridge: vlan: add... |
822 |
struct net_bridge_vlan_group *vg; |
3741873b4 bridge: allow add... |
823 |
struct net_bridge_port *p = NULL; |
2594e9064 bridge: vlan: add... |
824 |
struct net_bridge_vlan *v; |
3741873b4 bridge: allow add... |
825 |
struct net_bridge *br = NULL; |
77162022a net: add generic ... |
826 |
int err = 0; |
36fd2b63e bridge: allow cre... |
827 |
|
b74fd306e bridge: fdb add a... |
828 |
trace_br_fdb_add(ndm, dev, addr, vid, nlh_flags); |
292d13989 bridge: add NTF_U... |
829 830 831 832 833 |
if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) { pr_info("bridge: RTM_NEWNEIGH with invalid state %#x ", ndm->ndm_state); return -EINVAL; } |
537f7f849 bridge: check for... |
834 835 836 837 838 |
if (is_zero_ether_addr(addr)) { pr_info("bridge: RTM_NEWNEIGH with invalid ether address "); return -EINVAL; } |
3741873b4 bridge: allow add... |
839 840 841 842 843 844 845 846 847 848 849 |
if (dev->priv_flags & IFF_EBRIDGE) { br = netdev_priv(dev); vg = br_vlan_group(br); } else { p = br_port_get_rtnl(dev); if (!p) { pr_info("bridge: RTM_NEWNEIGH %s not a bridge port ", dev->name); return -EINVAL; } |
7bb90c371 bridge: Fix probl... |
850 |
br = p->br; |
3741873b4 bridge: allow add... |
851 |
vg = nbp_vlan_group(p); |
36fd2b63e bridge: allow cre... |
852 |
} |
f6f6424ba net: make vid as ... |
853 |
if (vid) { |
2594e9064 bridge: vlan: add... |
854 |
v = br_vlan_find(vg, vid); |
3741873b4 bridge: allow add... |
855 856 857 |
if (!v || !br_vlan_should_use(v)) { pr_info("bridge: RTM_NEWNEIGH with unconfigured vlan %d on %s ", vid, dev->name); |
1690be63a bridge: Add vlan ... |
858 859 860 861 |
return -EINVAL; } /* VID was specified, so use it. */ |
7bb90c371 bridge: Fix probl... |
862 |
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, vid); |
292d13989 bridge: add NTF_U... |
863 |
} else { |
7bb90c371 bridge: Fix probl... |
864 |
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, 0); |
2594e9064 bridge: vlan: add... |
865 |
if (err || !vg || !vg->num_vlans) |
1690be63a bridge: Add vlan ... |
866 |
goto out; |
1690be63a bridge: Add vlan ... |
867 868 869 870 871 |
/* We have vlans configured on this port and user didn't * specify a VLAN. To be nice, add/update entry for every * vlan on this port. */ |
2594e9064 bridge: vlan: add... |
872 |
list_for_each_entry(v, &vg->vlan_list, vlist) { |
3741873b4 bridge: allow add... |
873 874 |
if (!br_vlan_should_use(v)) continue; |
7bb90c371 bridge: Fix probl... |
875 |
err = __br_fdb_add(ndm, br, p, addr, nlh_flags, v->vid); |
1690be63a bridge: Add vlan ... |
876 877 |
if (err) goto out; |
1690be63a bridge: Add vlan ... |
878 |
} |
292d13989 bridge: add NTF_U... |
879 |
} |
36fd2b63e bridge: allow cre... |
880 |
|
1690be63a bridge: Add vlan ... |
881 |
out: |
36fd2b63e bridge: allow cre... |
882 883 |
return err; } |
5019ab50f bridge: fdb: conv... |
884 885 |
static int fdb_delete_by_addr_and_port(struct net_bridge *br, const struct net_bridge_port *p, |
8c86f967d bridge: make br_f... |
886 |
const u8 *addr, u16 vlan) |
36fd2b63e bridge: allow cre... |
887 |
{ |
36fd2b63e bridge: allow cre... |
888 |
struct net_bridge_fdb_entry *fdb; |
bfd0aeac5 bridge: fdb: conv... |
889 |
fdb = br_fdb_find(br, addr, vlan); |
8c86f967d bridge: make br_f... |
890 |
if (!fdb || fdb->dst != p) |
36fd2b63e bridge: allow cre... |
891 |
return -ENOENT; |
1690be63a bridge: Add vlan ... |
892 |
fdb_delete(br, fdb); |
5019ab50f bridge: fdb: conv... |
893 |
|
36fd2b63e bridge: allow cre... |
894 895 |
return 0; } |
5019ab50f bridge: fdb: conv... |
896 897 |
static int __br_fdb_delete(struct net_bridge *br, const struct net_bridge_port *p, |
1690be63a bridge: Add vlan ... |
898 899 900 |
const unsigned char *addr, u16 vid) { int err; |
5019ab50f bridge: fdb: conv... |
901 902 903 |
spin_lock_bh(&br->hash_lock); err = fdb_delete_by_addr_and_port(br, p, addr, vid); spin_unlock_bh(&br->hash_lock); |
1690be63a bridge: Add vlan ... |
904 905 906 |
return err; } |
36fd2b63e bridge: allow cre... |
907 |
/* Remove neighbor entry with RTM_DELNEIGH */ |
1690be63a bridge: Add vlan ... |
908 909 |
int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, |
f6f6424ba net: make vid as ... |
910 |
const unsigned char *addr, u16 vid) |
36fd2b63e bridge: allow cre... |
911 |
{ |
2594e9064 bridge: vlan: add... |
912 |
struct net_bridge_vlan_group *vg; |
3741873b4 bridge: allow add... |
913 |
struct net_bridge_port *p = NULL; |
2594e9064 bridge: vlan: add... |
914 |
struct net_bridge_vlan *v; |
5019ab50f bridge: fdb: conv... |
915 |
struct net_bridge *br; |
36fd2b63e bridge: allow cre... |
916 |
int err; |
1690be63a bridge: Add vlan ... |
917 |
|
3741873b4 bridge: allow add... |
918 919 920 921 922 923 924 925 926 927 928 929 |
if (dev->priv_flags & IFF_EBRIDGE) { br = netdev_priv(dev); vg = br_vlan_group(br); } else { p = br_port_get_rtnl(dev); if (!p) { pr_info("bridge: RTM_DELNEIGH %s not a bridge port ", dev->name); return -EINVAL; } vg = nbp_vlan_group(p); |
5019ab50f bridge: fdb: conv... |
930 |
br = p->br; |
36fd2b63e bridge: allow cre... |
931 |
} |
f6f6424ba net: make vid as ... |
932 |
if (vid) { |
2594e9064 bridge: vlan: add... |
933 934 |
v = br_vlan_find(vg, vid); if (!v) { |
3741873b4 bridge: allow add... |
935 936 |
pr_info("bridge: RTM_DELNEIGH with unconfigured vlan %d on %s ", vid, dev->name); |
1690be63a bridge: Add vlan ... |
937 938 |
return -EINVAL; } |
36fd2b63e bridge: allow cre... |
939 |
|
5019ab50f bridge: fdb: conv... |
940 |
err = __br_fdb_delete(br, p, addr, vid); |
1690be63a bridge: Add vlan ... |
941 |
} else { |
25d3b493a bridge: Fix inabi... |
942 |
err = -ENOENT; |
5019ab50f bridge: fdb: conv... |
943 |
err &= __br_fdb_delete(br, p, addr, 0); |
2594e9064 bridge: vlan: add... |
944 |
if (!vg || !vg->num_vlans) |
5019ab50f bridge: fdb: conv... |
945 |
return err; |
1690be63a bridge: Add vlan ... |
946 |
|
3741873b4 bridge: allow add... |
947 948 949 |
list_for_each_entry(v, &vg->vlan_list, vlist) { if (!br_vlan_should_use(v)) continue; |
5019ab50f bridge: fdb: conv... |
950 |
err &= __br_fdb_delete(br, p, addr, v->vid); |
3741873b4 bridge: allow add... |
951 |
} |
1690be63a bridge: Add vlan ... |
952 |
} |
5019ab50f bridge: fdb: conv... |
953 |
|
36fd2b63e bridge: allow cre... |
954 955 |
return err; } |
8db24af71 bridge: Add funct... |
956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 |
int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p) { struct net_bridge_fdb_entry *fdb, *tmp; int i; int err; ASSERT_RTNL(); for (i = 0; i < BR_HASH_SIZE; i++) { hlist_for_each_entry(fdb, &br->hash[i], hlist) { /* We only care for static entries */ if (!fdb->is_static) continue; err = dev_uc_add(p->dev, fdb->addr.addr); if (err) goto rollback; } } return 0; rollback: for (i = 0; i < BR_HASH_SIZE; i++) { hlist_for_each_entry(tmp, &br->hash[i], hlist) { /* If we reached the fdb that failed, we can stop */ if (tmp == fdb) break; /* We only care for static entries */ if (!tmp->is_static) continue; dev_uc_del(p->dev, tmp->addr.addr); } } return err; } void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p) { struct net_bridge_fdb_entry *fdb; int i; ASSERT_RTNL(); for (i = 0; i < BR_HASH_SIZE; i++) { hlist_for_each_entry_rcu(fdb, &br->hash[i], hlist) { /* We only care for static entries */ if (!fdb->is_static) continue; dev_uc_del(p->dev, fdb->addr.addr); } } } |
cf6b8e1ee bridge: add API t... |
1012 |
|
3aeb66176 net: replace br_f... |
1013 |
int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, |
cf6b8e1ee bridge: add API t... |
1014 1015 |
const unsigned char *addr, u16 vid) { |
cf6b8e1ee bridge: add API t... |
1016 |
struct net_bridge_fdb_entry *fdb; |
7597b266c bridge: allow ext... |
1017 1018 |
struct hlist_head *head; bool modified = false; |
cf6b8e1ee bridge: add API t... |
1019 |
int err = 0; |
b74fd306e bridge: fdb add a... |
1020 |
trace_br_fdb_external_learn_add(br, p, addr, vid); |
cf6b8e1ee bridge: add API t... |
1021 1022 1023 |
spin_lock_bh(&br->hash_lock); head = &br->hash[br_mac_hash(addr, vid)]; |
bfd0aeac5 bridge: fdb: conv... |
1024 |
fdb = br_fdb_find(br, addr, vid); |
cf6b8e1ee bridge: add API t... |
1025 |
if (!fdb) { |
b7af1472a bridge: set is_lo... |
1026 |
fdb = fdb_create(head, p, addr, vid, 0, 0); |
cf6b8e1ee bridge: add API t... |
1027 1028 1029 1030 1031 1032 |
if (!fdb) { err = -ENOMEM; goto err_unlock; } fdb->added_by_external_learn = 1; fdb_notify(br, fdb, RTM_NEWNEIGH); |
7597b266c bridge: allow ext... |
1033 |
} else { |
cf6b8e1ee bridge: add API t... |
1034 |
fdb->updated = jiffies; |
7597b266c bridge: allow ext... |
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 |
if (fdb->dst != p) { fdb->dst = p; modified = true; } if (fdb->added_by_external_learn) { /* Refresh entry */ fdb->used = jiffies; } else if (!fdb->added_by_user) { /* Take over SW learned entry */ fdb->added_by_external_learn = 1; modified = true; } if (modified) fdb_notify(br, fdb, RTM_NEWNEIGH); |
cf6b8e1ee bridge: add API t... |
1052 1053 1054 1055 |
} err_unlock: spin_unlock_bh(&br->hash_lock); |
cf6b8e1ee bridge: add API t... |
1056 1057 1058 |
return err; } |
cf6b8e1ee bridge: add API t... |
1059 |
|
3aeb66176 net: replace br_f... |
1060 |
int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p, |
cf6b8e1ee bridge: add API t... |
1061 1062 |
const unsigned char *addr, u16 vid) { |
cf6b8e1ee bridge: add API t... |
1063 1064 |
struct net_bridge_fdb_entry *fdb; int err = 0; |
cf6b8e1ee bridge: add API t... |
1065 |
spin_lock_bh(&br->hash_lock); |
bfd0aeac5 bridge: fdb: conv... |
1066 |
fdb = br_fdb_find(br, addr, vid); |
cf6b8e1ee bridge: add API t... |
1067 1068 1069 1070 1071 1072 |
if (fdb && fdb->added_by_external_learn) fdb_delete(br, fdb); else err = -ENOENT; spin_unlock_bh(&br->hash_lock); |
cf6b8e1ee bridge: add API t... |
1073 1074 1075 |
return err; } |
9fe8bcec0 net: bridge: Rece... |
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 |
void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p, const unsigned char *addr, u16 vid) { struct net_bridge_fdb_entry *fdb; spin_lock_bh(&br->hash_lock); fdb = br_fdb_find(br, addr, vid); if (fdb) fdb->offloaded = 1; spin_unlock_bh(&br->hash_lock); } |