aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge
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
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')
-rw-r--r--net/bridge/br_device.c2
-rw-r--r--net/bridge/br_fdb.c69
-rw-r--r--net/bridge/br_input.c9
-rw-r--r--net/bridge/br_private.h7
4 files changed, 52 insertions, 35 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 9509139da49c..d5f1d3fd4b28 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -71,7 +71,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
71 br_multicast_deliver(mdst, skb); 71 br_multicast_deliver(mdst, skb);
72 else 72 else
73 br_flood_deliver(br, skb); 73 br_flood_deliver(br, skb);
74 } else if ((dst = __br_fdb_get(br, dest)) != NULL) 74 } else if ((dst = __br_fdb_get(br, dest, vid)) != NULL)
75 br_deliver(dst->dst, skb); 75 br_deliver(dst->dst, skb);
76 else 76 else
77 br_flood_deliver(br, skb); 77 br_flood_deliver(br, skb);
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
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index a63f227ad963..480330151898 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -75,7 +75,7 @@ int br_handle_frame_finish(struct sk_buff *skb)
75 75
76 /* insert into forwarding database after filtering to avoid spoofing */ 76 /* insert into forwarding database after filtering to avoid spoofing */
77 br = p->br; 77 br = p->br;
78 br_fdb_update(br, p, eth_hdr(skb)->h_source); 78 br_fdb_update(br, p, eth_hdr(skb)->h_source, vid);
79 79
80 if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) && 80 if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) &&
81 br_multicast_rcv(br, p, skb)) 81 br_multicast_rcv(br, p, skb))
@@ -110,7 +110,8 @@ int br_handle_frame_finish(struct sk_buff *skb)
110 skb2 = skb; 110 skb2 = skb;
111 111
112 br->dev->stats.multicast++; 112 br->dev->stats.multicast++;
113 } else if ((dst = __br_fdb_get(br, dest)) && dst->is_local) { 113 } else if ((dst = __br_fdb_get(br, dest, vid)) &&
114 dst->is_local) {
114 skb2 = skb; 115 skb2 = skb;
115 /* Do not forward the packet since it's local. */ 116 /* Do not forward the packet since it's local. */
116 skb = NULL; 117 skb = NULL;
@@ -138,8 +139,10 @@ drop:
138static int br_handle_local_finish(struct sk_buff *skb) 139static int br_handle_local_finish(struct sk_buff *skb)
139{ 140{
140 struct net_bridge_port *p = br_port_get_rcu(skb->dev); 141 struct net_bridge_port *p = br_port_get_rcu(skb->dev);
142 u16 vid = 0;
141 143
142 br_fdb_update(p->br, p, eth_hdr(skb)->h_source); 144 br_vlan_get_tag(skb, &vid);
145 br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid);
143 return 0; /* process further */ 146 return 0; /* process further */
144} 147}
145 148
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 1ae6395a0369..f4ae87b5aa6e 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -88,6 +88,7 @@ struct net_bridge_fdb_entry
88 mac_addr addr; 88 mac_addr addr;
89 unsigned char is_local; 89 unsigned char is_local;
90 unsigned char is_static; 90 unsigned char is_static;
91 __u16 vlan_id;
91}; 92};
92 93
93struct net_bridge_port_group { 94struct net_bridge_port_group {
@@ -373,7 +374,8 @@ extern void br_fdb_cleanup(unsigned long arg);
373extern void br_fdb_delete_by_port(struct net_bridge *br, 374extern void br_fdb_delete_by_port(struct net_bridge *br,
374 const struct net_bridge_port *p, int do_all); 375 const struct net_bridge_port *p, int do_all);
375extern struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, 376extern struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
376 const unsigned char *addr); 377 const unsigned char *addr,
378 __u16 vid);
377extern int br_fdb_test_addr(struct net_device *dev, unsigned char *addr); 379extern int br_fdb_test_addr(struct net_device *dev, unsigned char *addr);
378extern int br_fdb_fillbuf(struct net_bridge *br, void *buf, 380extern int br_fdb_fillbuf(struct net_bridge *br, void *buf,
379 unsigned long count, unsigned long off); 381 unsigned long count, unsigned long off);
@@ -382,7 +384,8 @@ extern int br_fdb_insert(struct net_bridge *br,
382 const unsigned char *addr); 384 const unsigned char *addr);
383extern void br_fdb_update(struct net_bridge *br, 385extern void br_fdb_update(struct net_bridge *br,
384 struct net_bridge_port *source, 386 struct net_bridge_port *source,
385 const unsigned char *addr); 387 const unsigned char *addr,
388 u16 vid);
386 389
387extern int br_fdb_delete(struct ndmsg *ndm, 390extern int br_fdb_delete(struct ndmsg *ndm,
388 struct net_device *dev, 391 struct net_device *dev,