diff options
-rw-r--r-- | net/bridge/br_device.c | 2 | ||||
-rw-r--r-- | net/bridge/br_fdb.c | 69 | ||||
-rw-r--r-- | net/bridge/br_input.c | 9 | ||||
-rw-r--r-- | net/bridge/br_private.h | 7 |
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 | ||
28 | static struct kmem_cache *br_fdb_cache __read_mostly; | 29 | static 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 | ||
70 | static inline int br_mac_hash(const unsigned char *mac) | 71 | static 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 | ||
77 | static void fdb_rcu_free(struct rcu_head *head) | 78 | static 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 */ |
233 | struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, | 234 | struct 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 | ||
327 | static struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head, | 331 | static 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 | ||
340 | static struct net_bridge_fdb_entry *fdb_find_rcu(struct hlist_head *head, | 346 | static 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 | ||
353 | static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, | 361 | static 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, | |||
371 | static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, | 381 | static 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 | ||
414 | void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, | 424 | void 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 */ |
573 | static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, | 583 | static 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[], | |||
643 | static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr) | 654 | static 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: | |||
138 | static int br_handle_local_finish(struct sk_buff *skb) | 139 | static 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 | ||
93 | struct net_bridge_port_group { | 94 | struct net_bridge_port_group { |
@@ -373,7 +374,8 @@ extern void br_fdb_cleanup(unsigned long arg); | |||
373 | extern void br_fdb_delete_by_port(struct net_bridge *br, | 374 | extern 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); |
375 | extern struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, | 376 | extern 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); | ||
377 | extern int br_fdb_test_addr(struct net_device *dev, unsigned char *addr); | 379 | extern int br_fdb_test_addr(struct net_device *dev, unsigned char *addr); |
378 | extern int br_fdb_fillbuf(struct net_bridge *br, void *buf, | 380 | extern 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); |
383 | extern void br_fdb_update(struct net_bridge *br, | 385 | extern 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 | ||
387 | extern int br_fdb_delete(struct ndmsg *ndm, | 390 | extern int br_fdb_delete(struct ndmsg *ndm, |
388 | struct net_device *dev, | 391 | struct net_device *dev, |