diff options
| -rw-r--r-- | net/bridge/br_device.c | 8 | ||||
| -rw-r--r-- | net/bridge/br_if.c | 3 | ||||
| -rw-r--r-- | net/bridge/br_private.h | 14 | ||||
| -rw-r--r-- | net/bridge/br_vlan.c | 118 |
4 files changed, 136 insertions, 7 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 659cac15c0df..ffd379db5938 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c | |||
| @@ -88,12 +88,17 @@ out: | |||
| 88 | static int br_dev_init(struct net_device *dev) | 88 | static int br_dev_init(struct net_device *dev) |
| 89 | { | 89 | { |
| 90 | struct net_bridge *br = netdev_priv(dev); | 90 | struct net_bridge *br = netdev_priv(dev); |
| 91 | int err; | ||
| 91 | 92 | ||
| 92 | br->stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); | 93 | br->stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); |
| 93 | if (!br->stats) | 94 | if (!br->stats) |
| 94 | return -ENOMEM; | 95 | return -ENOMEM; |
| 95 | 96 | ||
| 96 | return 0; | 97 | err = br_vlan_init(br); |
| 98 | if (err) | ||
| 99 | free_percpu(br->stats); | ||
| 100 | |||
| 101 | return err; | ||
| 97 | } | 102 | } |
| 98 | 103 | ||
| 99 | static int br_dev_open(struct net_device *dev) | 104 | static int br_dev_open(struct net_device *dev) |
| @@ -389,5 +394,4 @@ void br_dev_setup(struct net_device *dev) | |||
| 389 | br_netfilter_rtable_init(br); | 394 | br_netfilter_rtable_init(br); |
| 390 | br_stp_timer_init(br); | 395 | br_stp_timer_init(br); |
| 391 | br_multicast_init(br); | 396 | br_multicast_init(br); |
| 392 | br_vlan_init(br); | ||
| 393 | } | 397 | } |
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 7b7289ca2992..ed307db7a12b 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c | |||
| @@ -500,6 +500,9 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) | |||
| 500 | if (br_fdb_insert(br, p, dev->dev_addr, 0)) | 500 | if (br_fdb_insert(br, p, dev->dev_addr, 0)) |
| 501 | netdev_err(dev, "failed insert local address bridge forwarding table\n"); | 501 | netdev_err(dev, "failed insert local address bridge forwarding table\n"); |
| 502 | 502 | ||
| 503 | if (nbp_vlan_init(p)) | ||
| 504 | netdev_err(dev, "failed to initialize vlan filtering on this port\n"); | ||
| 505 | |||
| 503 | spin_lock_bh(&br->lock); | 506 | spin_lock_bh(&br->lock); |
| 504 | changed_addr = br_stp_recalculate_bridge_id(br); | 507 | changed_addr = br_stp_recalculate_bridge_id(br); |
| 505 | 508 | ||
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index f671561b3053..d8cbaa694227 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h | |||
| @@ -605,12 +605,13 @@ bool br_vlan_find(struct net_bridge *br, u16 vid); | |||
| 605 | void br_recalculate_fwd_mask(struct net_bridge *br); | 605 | void br_recalculate_fwd_mask(struct net_bridge *br); |
| 606 | int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); | 606 | int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); |
| 607 | int br_vlan_set_proto(struct net_bridge *br, unsigned long val); | 607 | int br_vlan_set_proto(struct net_bridge *br, unsigned long val); |
| 608 | void br_vlan_init(struct net_bridge *br); | 608 | int br_vlan_init(struct net_bridge *br); |
| 609 | int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val); | 609 | int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val); |
| 610 | int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); | 610 | int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); |
| 611 | int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); | 611 | int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); |
| 612 | void nbp_vlan_flush(struct net_bridge_port *port); | 612 | void nbp_vlan_flush(struct net_bridge_port *port); |
| 613 | bool nbp_vlan_find(struct net_bridge_port *port, u16 vid); | 613 | bool nbp_vlan_find(struct net_bridge_port *port, u16 vid); |
| 614 | int nbp_vlan_init(struct net_bridge_port *port); | ||
| 614 | 615 | ||
| 615 | static inline struct net_port_vlans *br_get_vlan_info( | 616 | static inline struct net_port_vlans *br_get_vlan_info( |
| 616 | const struct net_bridge *br) | 617 | const struct net_bridge *br) |
| @@ -643,6 +644,9 @@ static inline int br_vlan_get_tag(const struct sk_buff *skb, u16 *vid) | |||
| 643 | 644 | ||
| 644 | static inline u16 br_get_pvid(const struct net_port_vlans *v) | 645 | static inline u16 br_get_pvid(const struct net_port_vlans *v) |
| 645 | { | 646 | { |
| 647 | if (!v) | ||
| 648 | return 0; | ||
| 649 | |||
| 646 | smp_rmb(); | 650 | smp_rmb(); |
| 647 | return v->pvid; | 651 | return v->pvid; |
| 648 | } | 652 | } |
| @@ -703,8 +707,9 @@ static inline void br_recalculate_fwd_mask(struct net_bridge *br) | |||
| 703 | { | 707 | { |
| 704 | } | 708 | } |
| 705 | 709 | ||
| 706 | static inline void br_vlan_init(struct net_bridge *br) | 710 | static inline int br_vlan_init(struct net_bridge *br) |
| 707 | { | 711 | { |
| 712 | return 0; | ||
| 708 | } | 713 | } |
| 709 | 714 | ||
| 710 | static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) | 715 | static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags) |
| @@ -737,6 +742,11 @@ static inline bool nbp_vlan_find(struct net_bridge_port *port, u16 vid) | |||
| 737 | return false; | 742 | return false; |
| 738 | } | 743 | } |
| 739 | 744 | ||
| 745 | static inline int nbp_vlan_init(struct net_bridge_port *port) | ||
| 746 | { | ||
| 747 | return 0; | ||
| 748 | } | ||
| 749 | |||
| 740 | static inline u16 br_vlan_get_tag(const struct sk_buff *skb, u16 *tag) | 750 | static inline u16 br_vlan_get_tag(const struct sk_buff *skb, u16 *tag) |
| 741 | { | 751 | { |
| 742 | return 0; | 752 | return 0; |
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index e11c9932e706..150048fb99b0 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c | |||
| @@ -499,12 +499,110 @@ err_filt: | |||
| 499 | goto unlock; | 499 | goto unlock; |
| 500 | } | 500 | } |
| 501 | 501 | ||
| 502 | static bool vlan_default_pvid(struct net_port_vlans *pv, u16 vid) | ||
| 503 | { | ||
| 504 | return pv && vid == pv->pvid && test_bit(vid, pv->untagged_bitmap); | ||
| 505 | } | ||
| 506 | |||
| 507 | static void br_vlan_disable_default_pvid(struct net_bridge *br) | ||
| 508 | { | ||
| 509 | struct net_bridge_port *p; | ||
| 510 | u16 pvid = br->default_pvid; | ||
| 511 | |||
| 512 | /* Disable default_pvid on all ports where it is still | ||
| 513 | * configured. | ||
| 514 | */ | ||
| 515 | if (vlan_default_pvid(br_get_vlan_info(br), pvid)) | ||
| 516 | br_vlan_delete(br, pvid); | ||
| 517 | |||
| 518 | list_for_each_entry(p, &br->port_list, list) { | ||
| 519 | if (vlan_default_pvid(nbp_get_vlan_info(p), pvid)) | ||
| 520 | nbp_vlan_delete(p, pvid); | ||
| 521 | } | ||
| 522 | |||
| 523 | br->default_pvid = 0; | ||
| 524 | } | ||
| 525 | |||
| 526 | static int __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid) | ||
| 527 | { | ||
| 528 | struct net_bridge_port *p; | ||
| 529 | u16 old_pvid; | ||
| 530 | int err = 0; | ||
| 531 | unsigned long *changed; | ||
| 532 | |||
| 533 | changed = kcalloc(BITS_TO_LONGS(BR_MAX_PORTS), sizeof(unsigned long), | ||
| 534 | GFP_KERNEL); | ||
| 535 | if (!changed) | ||
| 536 | return -ENOMEM; | ||
| 537 | |||
| 538 | old_pvid = br->default_pvid; | ||
| 539 | |||
| 540 | /* Update default_pvid config only if we do not conflict with | ||
| 541 | * user configuration. | ||
| 542 | */ | ||
| 543 | if ((!old_pvid || vlan_default_pvid(br_get_vlan_info(br), old_pvid)) && | ||
| 544 | !br_vlan_find(br, pvid)) { | ||
| 545 | err = br_vlan_add(br, pvid, | ||
| 546 | BRIDGE_VLAN_INFO_PVID | | ||
| 547 | BRIDGE_VLAN_INFO_UNTAGGED); | ||
| 548 | if (err) | ||
| 549 | goto out; | ||
| 550 | br_vlan_delete(br, old_pvid); | ||
| 551 | set_bit(0, changed); | ||
| 552 | } | ||
| 553 | |||
| 554 | list_for_each_entry(p, &br->port_list, list) { | ||
| 555 | /* Update default_pvid config only if we do not conflict with | ||
| 556 | * user configuration. | ||
| 557 | */ | ||
| 558 | if ((old_pvid && | ||
| 559 | !vlan_default_pvid(nbp_get_vlan_info(p), old_pvid)) || | ||
| 560 | nbp_vlan_find(p, pvid)) | ||
| 561 | continue; | ||
| 562 | |||
| 563 | err = nbp_vlan_add(p, pvid, | ||
| 564 | BRIDGE_VLAN_INFO_PVID | | ||
| 565 | BRIDGE_VLAN_INFO_UNTAGGED); | ||
| 566 | if (err) | ||
| 567 | goto err_port; | ||
| 568 | nbp_vlan_delete(p, old_pvid); | ||
| 569 | set_bit(p->port_no, changed); | ||
| 570 | } | ||
| 571 | |||
| 572 | br->default_pvid = pvid; | ||
| 573 | |||
| 574 | out: | ||
| 575 | kfree(changed); | ||
| 576 | return err; | ||
| 577 | |||
| 578 | err_port: | ||
| 579 | list_for_each_entry_continue_reverse(p, &br->port_list, list) { | ||
| 580 | if (!test_bit(p->port_no, changed)) | ||
| 581 | continue; | ||
| 582 | |||
| 583 | if (old_pvid) | ||
| 584 | nbp_vlan_add(p, old_pvid, | ||
| 585 | BRIDGE_VLAN_INFO_PVID | | ||
| 586 | BRIDGE_VLAN_INFO_UNTAGGED); | ||
| 587 | nbp_vlan_delete(p, pvid); | ||
| 588 | } | ||
| 589 | |||
| 590 | if (test_bit(0, changed)) { | ||
| 591 | if (old_pvid) | ||
| 592 | br_vlan_add(br, old_pvid, | ||
| 593 | BRIDGE_VLAN_INFO_PVID | | ||
| 594 | BRIDGE_VLAN_INFO_UNTAGGED); | ||
| 595 | br_vlan_delete(br, pvid); | ||
| 596 | } | ||
| 597 | goto out; | ||
| 598 | } | ||
| 599 | |||
| 502 | int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val) | 600 | int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val) |
| 503 | { | 601 | { |
| 504 | u16 pvid = val; | 602 | u16 pvid = val; |
| 505 | int err = 0; | 603 | int err = 0; |
| 506 | 604 | ||
| 507 | if (!val || val >= VLAN_VID_MASK) | 605 | if (val >= VLAN_VID_MASK) |
| 508 | return -EINVAL; | 606 | return -EINVAL; |
| 509 | 607 | ||
| 510 | if (!rtnl_trylock()) | 608 | if (!rtnl_trylock()) |
| @@ -520,17 +618,22 @@ int br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val) | |||
| 520 | goto unlock; | 618 | goto unlock; |
| 521 | } | 619 | } |
| 522 | 620 | ||
| 523 | br->default_pvid = pvid; | 621 | if (!pvid) |
| 622 | br_vlan_disable_default_pvid(br); | ||
| 623 | else | ||
| 624 | err = __br_vlan_set_default_pvid(br, pvid); | ||
| 524 | 625 | ||
| 525 | unlock: | 626 | unlock: |
| 526 | rtnl_unlock(); | 627 | rtnl_unlock(); |
| 527 | return err; | 628 | return err; |
| 528 | } | 629 | } |
| 529 | 630 | ||
| 530 | void br_vlan_init(struct net_bridge *br) | 631 | int br_vlan_init(struct net_bridge *br) |
| 531 | { | 632 | { |
| 532 | br->vlan_proto = htons(ETH_P_8021Q); | 633 | br->vlan_proto = htons(ETH_P_8021Q); |
| 533 | br->default_pvid = 1; | 634 | br->default_pvid = 1; |
| 635 | return br_vlan_add(br, 1, | ||
| 636 | BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_UNTAGGED); | ||
| 534 | } | 637 | } |
| 535 | 638 | ||
| 536 | /* Must be protected by RTNL. | 639 | /* Must be protected by RTNL. |
| @@ -622,3 +725,12 @@ out: | |||
| 622 | rcu_read_unlock(); | 725 | rcu_read_unlock(); |
| 623 | return found; | 726 | return found; |
| 624 | } | 727 | } |
| 728 | |||
| 729 | int nbp_vlan_init(struct net_bridge_port *p) | ||
| 730 | { | ||
| 731 | return p->br->default_pvid ? | ||
| 732 | nbp_vlan_add(p, p->br->default_pvid, | ||
| 733 | BRIDGE_VLAN_INFO_PVID | | ||
| 734 | BRIDGE_VLAN_INFO_UNTAGGED) : | ||
| 735 | 0; | ||
| 736 | } | ||
