aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge/br_fdb.c
diff options
context:
space:
mode:
authorVlad Yasevich <vyasevic@redhat.com>2013-02-13 07:00:16 -0500
committerDavid S. Miller <davem@davemloft.net>2013-02-13 19:42:15 -0500
commit2ba071ecb6d41ce172f9ccb3996f28cb337b3576 (patch)
treeab957f8a004394d4d68c32dfbf823cc1a428ab3e /net/bridge/br_fdb.c
parent552406c488ec2cf1aaf8b5bd24d1750c9fd6d8cc (diff)
bridge: Add vlan to unicast fdb entries
This patch adds vlan to unicast fdb entries that are created for learned addresses (not the manually configured ones). It adds vlan id into the hash mix and uses vlan as an addditional parameter for an entry match. Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge/br_fdb.c')
-rw-r--r--net/bridge/br_fdb.c69
1 files changed, 40 insertions, 29 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index d9576e6de2b8..276a52254606 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -23,6 +23,7 @@
23#include <linux/slab.h> 23#include <linux/slab.h>
24#include <linux/atomic.h> 24#include <linux/atomic.h>
25#include <asm/unaligned.h> 25#include <asm/unaligned.h>
26#include <linux/if_vlan.h>
26#include "br_private.h" 27#include "br_private.h"
27 28
28static struct kmem_cache *br_fdb_cache __read_mostly; 29static struct kmem_cache *br_fdb_cache __read_mostly;
@@ -67,11 +68,11 @@ static inline int has_expired(const struct net_bridge *br,
67 time_before_eq(fdb->updated + hold_time(br), jiffies); 68 time_before_eq(fdb->updated + hold_time(br), jiffies);
68} 69}
69 70
70static inline int br_mac_hash(const unsigned char *mac) 71static inline int br_mac_hash(const unsigned char *mac, __u16 vid)
71{ 72{
72 /* use 1 byte of OUI cnd 3 bytes of NIC */ 73 /* use 1 byte of OUI and 3 bytes of NIC */
73 u32 key = get_unaligned((u32 *)(mac + 2)); 74 u32 key = get_unaligned((u32 *)(mac + 2));
74 return jhash_1word(key, fdb_salt) & (BR_HASH_SIZE - 1); 75 return jhash_2words(key, vid, fdb_salt) & (BR_HASH_SIZE - 1);
75} 76}
76 77
77static void fdb_rcu_free(struct rcu_head *head) 78static void fdb_rcu_free(struct rcu_head *head)
@@ -132,7 +133,7 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
132 struct net_bridge_fdb_entry *f; 133 struct net_bridge_fdb_entry *f;
133 134
134 /* If old entry was unassociated with any port, then delete it. */ 135 /* If old entry was unassociated with any port, then delete it. */
135 f = __br_fdb_get(br, br->dev->dev_addr); 136 f = __br_fdb_get(br, br->dev->dev_addr, 0);
136 if (f && f->is_local && !f->dst) 137 if (f && f->is_local && !f->dst)
137 fdb_delete(br, f); 138 fdb_delete(br, f);
138 139
@@ -231,13 +232,16 @@ void br_fdb_delete_by_port(struct net_bridge *br,
231 232
232/* No locking or refcounting, assumes caller has rcu_read_lock */ 233/* No locking or refcounting, assumes caller has rcu_read_lock */
233struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, 234struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
234 const unsigned char *addr) 235 const unsigned char *addr,
236 __u16 vid)
235{ 237{
236 struct hlist_node *h; 238 struct hlist_node *h;
237 struct net_bridge_fdb_entry *fdb; 239 struct net_bridge_fdb_entry *fdb;
238 240
239 hlist_for_each_entry_rcu(fdb, h, &br->hash[br_mac_hash(addr)], hlist) { 241 hlist_for_each_entry_rcu(fdb, h,
240 if (ether_addr_equal(fdb->addr.addr, addr)) { 242 &br->hash[br_mac_hash(addr, vid)], hlist) {
243 if (ether_addr_equal(fdb->addr.addr, addr) &&
244 fdb->vlan_id == vid) {
241 if (unlikely(has_expired(br, fdb))) 245 if (unlikely(has_expired(br, fdb)))
242 break; 246 break;
243 return fdb; 247 return fdb;
@@ -261,7 +265,7 @@ int br_fdb_test_addr(struct net_device *dev, unsigned char *addr)
261 if (!port) 265 if (!port)
262 ret = 0; 266 ret = 0;
263 else { 267 else {
264 fdb = __br_fdb_get(port->br, addr); 268 fdb = __br_fdb_get(port->br, addr, 0);
265 ret = fdb && fdb->dst && fdb->dst->dev != dev && 269 ret = fdb && fdb->dst && fdb->dst->dev != dev &&
266 fdb->dst->state == BR_STATE_FORWARDING; 270 fdb->dst->state == BR_STATE_FORWARDING;
267 } 271 }
@@ -325,26 +329,30 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf,
325} 329}
326 330
327static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, 331static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head,
328 const unsigned char *addr) 332 const unsigned char *addr,
333 __u16 vid)
329{ 334{
330 struct hlist_node *h; 335 struct hlist_node *h;
331 struct net_bridge_fdb_entry *fdb; 336 struct net_bridge_fdb_entry *fdb;
332 337
333 hlist_for_each_entry(fdb, h, head, hlist) { 338 hlist_for_each_entry(fdb, h, head, hlist) {
334 if (ether_addr_equal(fdb->addr.addr, addr)) 339 if (ether_addr_equal(fdb->addr.addr, addr) &&
340 fdb->vlan_id == vid)
335 return fdb; 341 return fdb;
336 } 342 }
337 return NULL; 343 return NULL;
338} 344}
339 345
340static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head, 346static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head,
341 const unsigned char *addr) 347 const unsigned char *addr,
348 __u16 vid)
342{ 349{
343 struct hlist_node *h; 350 struct hlist_node *h;
344 struct net_bridge_fdb_entry *fdb; 351 struct net_bridge_fdb_entry *fdb;
345 352
346 hlist_for_each_entry_rcu(fdb, h, head, hlist) { 353 hlist_for_each_entry_rcu(fdb, h, head, hlist) {
347 if (ether_addr_equal(fdb->addr.addr, addr)) 354 if (ether_addr_equal(fdb->addr.addr, addr) &&
355 fdb->vlan_id == vid)
348 return fdb; 356 return fdb;
349 } 357 }
350 return NULL; 358 return NULL;
@@ -352,7 +360,8 @@ static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head,
352 360
353static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, 361static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
354 struct net_bridge_port *source, 362 struct net_bridge_port *source,
355 const unsigned char *addr) 363 const unsigned char *addr,
364 __u16 vid)
356{ 365{
357 struct net_bridge_fdb_entry *fdb; 366 struct net_bridge_fdb_entry *fdb;
358 367
@@ -360,6 +369,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
360 if (fdb) { 369 if (fdb) {
361 memcpy(fdb->addr.addr, addr, ETH_ALEN); 370 memcpy(fdb->addr.addr, addr, ETH_ALEN);
362 fdb->dst = source; 371 fdb->dst = source;
372 fdb->vlan_id = vid;
363 fdb->is_local = 0; 373 fdb->is_local = 0;
364 fdb->is_static = 0; 374 fdb->is_static = 0;
365 fdb->updated = fdb->used = jiffies; 375 fdb->updated = fdb->used = jiffies;
@@ -371,13 +381,13 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
371static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, 381static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
372 const unsigned char *addr) 382 const unsigned char *addr)
373{ 383{
374 struct hlist_head *head = &br->hash[br_mac_hash(addr)]; 384 struct hlist_head *head = &br->hash[br_mac_hash(addr, 0)];
375 struct net_bridge_fdb_entry *fdb; 385 struct net_bridge_fdb_entry *fdb;
376 386
377 if (!is_valid_ether_addr(addr)) 387 if (!is_valid_ether_addr(addr))
378 return -EINVAL; 388 return -EINVAL;
379 389
380 fdb = fdb_find(head, addr); 390 fdb = fdb_find(head, addr, 0);
381 if (fdb) { 391 if (fdb) {
382 /* it is okay to have multiple ports with same 392 /* it is okay to have multiple ports with same
383 * address, just use the first one. 393 * address, just use the first one.
@@ -390,7 +400,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
390 fdb_delete(br, fdb); 400 fdb_delete(br, fdb);
391 } 401 }
392 402
393 fdb = fdb_create(head, source, addr); 403 fdb = fdb_create(head, source, addr, 0);
394 if (!fdb) 404 if (!fdb)
395 return -ENOMEM; 405 return -ENOMEM;
396 406
@@ -412,9 +422,9 @@ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
412} 422}
413 423
414void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, 424void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
415 const unsigned char *addr) 425 const unsigned char *addr, u16 vid)
416{ 426{
417 struct hlist_head *head = &br->hash[br_mac_hash(addr)]; 427 struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)];
418 struct net_bridge_fdb_entry *fdb; 428 struct net_bridge_fdb_entry *fdb;
419 429
420 /* some users want to always flood. */ 430 /* some users want to always flood. */
@@ -426,7 +436,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
426 source->state == BR_STATE_FORWARDING)) 436 source->state == BR_STATE_FORWARDING))
427 return; 437 return;
428 438
429 fdb = fdb_find_rcu(head, addr); 439 fdb = fdb_find_rcu(head, addr, vid);
430 if (likely(fdb)) { 440 if (likely(fdb)) {
431 /* attempt to update an entry for a local interface */ 441 /* attempt to update an entry for a local interface */
432 if (unlikely(fdb->is_local)) { 442 if (unlikely(fdb->is_local)) {
@@ -441,8 +451,8 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
441 } 451 }
442 } else { 452 } else {
443 spin_lock(&br->hash_lock); 453 spin_lock(&br->hash_lock);
444 if (likely(!fdb_find(head, addr))) { 454 if (likely(!fdb_find(head, addr, vid))) {
445 fdb = fdb_create(head, source, addr); 455 fdb = fdb_create(head, source, addr, vid);
446 if (fdb) 456 if (fdb)
447 fdb_notify(br, fdb, RTM_NEWNEIGH); 457 fdb_notify(br, fdb, RTM_NEWNEIGH);
448 } 458 }
@@ -571,18 +581,18 @@ out:
571 581
572/* Update (create or replace) forwarding database entry */ 582/* Update (create or replace) forwarding database entry */
573static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, 583static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
574 __u16 state, __u16 flags) 584 __u16 state, __u16 flags, __u16 vid)
575{ 585{
576 struct net_bridge *br = source->br; 586 struct net_bridge *br = source->br;
577 struct hlist_head *head = &br->hash[br_mac_hash(addr)]; 587 struct hlist_head *head = &br->hash[br_mac_hash(addr, vid)];
578 struct net_bridge_fdb_entry *fdb; 588 struct net_bridge_fdb_entry *fdb;
579 589
580 fdb = fdb_find(head, addr); 590 fdb = fdb_find(head, addr, vid);
581 if (fdb == NULL) { 591 if (fdb == NULL) {
582 if (!(flags & NLM_F_CREATE)) 592 if (!(flags & NLM_F_CREATE))
583 return -ENOENT; 593 return -ENOENT;
584 594
585 fdb = fdb_create(head, source, addr); 595 fdb = fdb_create(head, source, addr, vid);
586 if (!fdb) 596 if (!fdb)
587 return -ENOMEM; 597 return -ENOMEM;
588 fdb_notify(br, fdb, RTM_NEWNEIGH); 598 fdb_notify(br, fdb, RTM_NEWNEIGH);
@@ -629,11 +639,12 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
629 639
630 if (ndm->ndm_flags & NTF_USE) { 640 if (ndm->ndm_flags & NTF_USE) {
631 rcu_read_lock(); 641 rcu_read_lock();
632 br_fdb_update(p->br, p, addr); 642 br_fdb_update(p->br, p, addr, 0);
633 rcu_read_unlock(); 643 rcu_read_unlock();
634 } else { 644 } else {
635 spin_lock_bh(&p->br->hash_lock); 645 spin_lock_bh(&p->br->hash_lock);
636 err = fdb_add_entry(p, addr, ndm->ndm_state, nlh_flags); 646 err = fdb_add_entry(p, addr, ndm->ndm_state, nlh_flags,
647 0);
637 spin_unlock_bh(&p->br->hash_lock); 648 spin_unlock_bh(&p->br->hash_lock);
638 } 649 }
639 650
@@ -643,10 +654,10 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
643static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr) 654static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr)
644{ 655{
645 struct net_bridge *br = p->br; 656 struct net_bridge *br = p->br;
646 struct hlist_head *head = &br->hash[br_mac_hash(addr)]; 657 struct hlist_head *head = &br->hash[br_mac_hash(addr, 0)];
647 struct net_bridge_fdb_entry *fdb; 658 struct net_bridge_fdb_entry *fdb;
648 659
649 fdb = fdb_find(head, addr); 660 fdb = fdb_find(head, addr, 0);
650 if (!fdb) 661 if (!fdb)
651 return -ENOENT; 662 return -ENOENT;
652 663