aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge/br_fdb.c
diff options
context:
space:
mode:
authorVlad Yasevich <vyasevic@redhat.com>2013-02-13 07:00:18 -0500
committerDavid S. Miller <davem@davemloft.net>2013-02-13 19:42:16 -0500
commit1690be63a27b20ae65c792729a44f5970561ffa4 (patch)
treec0c86bc471e24207ee61b544c8683d84fc43d105 /net/bridge/br_fdb.c
parentb0e9a30dd669a844bb4f74515f8bcd307018ffd0 (diff)
bridge: Add vlan support to static neighbors
When a user adds bridge neighbors, allow him to specify VLAN id. If the VLAN id is not specified, the neighbor will be added for VLANs currently in the ports filter list. If no VLANs are configured on the port, we use vlan 0 and only add 1 entry. Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> Acked-by: Jitendra Kalsaria <jitendra.kalsaria@qlogic.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.c148
1 files changed, 131 insertions, 17 deletions
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 276a52254606..4b75ad43aa85 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -505,6 +505,10 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
505 ci.ndm_refcnt = 0; 505 ci.ndm_refcnt = 0;
506 if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci)) 506 if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci))
507 goto nla_put_failure; 507 goto nla_put_failure;
508
509 if (nla_put(skb, NDA_VLAN, sizeof(u16), &fdb->vlan_id))
510 goto nla_put_failure;
511
508 return nlmsg_end(skb, nlh); 512 return nlmsg_end(skb, nlh);
509 513
510nla_put_failure: 514nla_put_failure:
@@ -516,6 +520,7 @@ static inline size_t fdb_nlmsg_size(void)
516{ 520{
517 return NLMSG_ALIGN(sizeof(struct ndmsg)) 521 return NLMSG_ALIGN(sizeof(struct ndmsg))
518 + nla_total_size(ETH_ALEN) /* NDA_LLADDR */ 522 + nla_total_size(ETH_ALEN) /* NDA_LLADDR */
523 + nla_total_size(sizeof(u16)) /* NDA_VLAN */
519 + nla_total_size(sizeof(struct nda_cacheinfo)); 524 + nla_total_size(sizeof(struct nda_cacheinfo));
520} 525}
521 526
@@ -617,6 +622,25 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
617 return 0; 622 return 0;
618} 623}
619 624
625static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge_port *p,
626 const unsigned char *addr, u16 nlh_flags, u16 vid)
627{
628 int err = 0;
629
630 if (ndm->ndm_flags & NTF_USE) {
631 rcu_read_lock();
632 br_fdb_update(p->br, p, addr, vid);
633 rcu_read_unlock();
634 } else {
635 spin_lock_bh(&p->br->hash_lock);
636 err = fdb_add_entry(p, addr, ndm->ndm_state,
637 nlh_flags, vid);
638 spin_unlock_bh(&p->br->hash_lock);
639 }
640
641 return err;
642}
643
620/* Add new permanent fdb entry with RTM_NEWNEIGH */ 644/* Add new permanent fdb entry with RTM_NEWNEIGH */
621int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], 645int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
622 struct net_device *dev, 646 struct net_device *dev,
@@ -624,12 +648,29 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
624{ 648{
625 struct net_bridge_port *p; 649 struct net_bridge_port *p;
626 int err = 0; 650 int err = 0;
651 struct net_port_vlans *pv;
652 unsigned short vid = VLAN_N_VID;
627 653
628 if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) { 654 if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) {
629 pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state); 655 pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state);
630 return -EINVAL; 656 return -EINVAL;
631 } 657 }
632 658
659 if (tb[NDA_VLAN]) {
660 if (nla_len(tb[NDA_VLAN]) != sizeof(unsigned short)) {
661 pr_info("bridge: RTM_NEWNEIGH with invalid vlan\n");
662 return -EINVAL;
663 }
664
665 vid = nla_get_u16(tb[NDA_VLAN]);
666
667 if (vid >= VLAN_N_VID) {
668 pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",
669 vid);
670 return -EINVAL;
671 }
672 }
673
633 p = br_port_get_rtnl(dev); 674 p = br_port_get_rtnl(dev);
634 if (p == NULL) { 675 if (p == NULL) {
635 pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n", 676 pr_info("bridge: RTM_NEWNEIGH %s not a bridge port\n",
@@ -637,41 +678,90 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
637 return -EINVAL; 678 return -EINVAL;
638 } 679 }
639 680
640 if (ndm->ndm_flags & NTF_USE) { 681 pv = nbp_get_vlan_info(p);
641 rcu_read_lock(); 682 if (vid != VLAN_N_VID) {
642 br_fdb_update(p->br, p, addr, 0); 683 if (!pv || !test_bit(vid, pv->vlan_bitmap)) {
643 rcu_read_unlock(); 684 pr_info("bridge: RTM_NEWNEIGH with unconfigured "
685 "vlan %d on port %s\n", vid, dev->name);
686 return -EINVAL;
687 }
688
689 /* VID was specified, so use it. */
690 err = __br_fdb_add(ndm, p, addr, nlh_flags, vid);
644 } else { 691 } else {
645 spin_lock_bh(&p->br->hash_lock); 692 if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) {
646 err = fdb_add_entry(p, addr, ndm->ndm_state, nlh_flags, 693 err = __br_fdb_add(ndm, p, addr, nlh_flags, 0);
647 0); 694 goto out;
648 spin_unlock_bh(&p->br->hash_lock); 695 }
696
697 /* We have vlans configured on this port and user didn't
698 * specify a VLAN. To be nice, add/update entry for every
699 * vlan on this port.
700 */
701 vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN);
702 while (vid < BR_VLAN_BITMAP_LEN) {
703 err = __br_fdb_add(ndm, p, addr, nlh_flags, vid);
704 if (err)
705 goto out;
706 vid = find_next_bit(pv->vlan_bitmap,
707 BR_VLAN_BITMAP_LEN, vid+1);
708 }
649 } 709 }
650 710
711out:
651 return err; 712 return err;
652} 713}
653 714
654static int fdb_delete_by_addr(struct net_bridge_port *p, const u8 *addr) 715static int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr,
716 u16 vlan)
655{ 717{
656 struct net_bridge *br = p->br; 718 struct hlist_head *head = &br->hash[br_mac_hash(addr, vlan)];
657 struct hlist_head *head = &br->hash[br_mac_hash(addr, 0)];
658 struct net_bridge_fdb_entry *fdb; 719 struct net_bridge_fdb_entry *fdb;
659 720
660 fdb = fdb_find(head, addr, 0); 721 fdb = fdb_find(head, addr, vlan);
661 if (!fdb) 722 if (!fdb)
662 return -ENOENT; 723 return -ENOENT;
663 724
664 fdb_delete(p->br, fdb); 725 fdb_delete(br, fdb);
665 return 0; 726 return 0;
666} 727}
667 728
729static int __br_fdb_delete(struct net_bridge_port *p,
730 const unsigned char *addr, u16 vid)
731{
732 int err;
733
734 spin_lock_bh(&p->br->hash_lock);
735 err = fdb_delete_by_addr(p->br, addr, vid);
736 spin_unlock_bh(&p->br->hash_lock);
737
738 return err;
739}
740
668/* Remove neighbor entry with RTM_DELNEIGH */ 741/* Remove neighbor entry with RTM_DELNEIGH */
669int br_fdb_delete(struct ndmsg *ndm, struct net_device *dev, 742int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
743 struct net_device *dev,
670 const unsigned char *addr) 744 const unsigned char *addr)
671{ 745{
672 struct net_bridge_port *p; 746 struct net_bridge_port *p;
673 int err; 747 int err;
748 struct net_port_vlans *pv;
749 unsigned short vid = VLAN_N_VID;
674 750
751 if (tb[NDA_VLAN]) {
752 if (nla_len(tb[NDA_VLAN]) != sizeof(unsigned short)) {
753 pr_info("bridge: RTM_NEWNEIGH with invalid vlan\n");
754 return -EINVAL;
755 }
756
757 vid = nla_get_u16(tb[NDA_VLAN]);
758
759 if (vid >= VLAN_N_VID) {
760 pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",
761 vid);
762 return -EINVAL;
763 }
764 }
675 p = br_port_get_rtnl(dev); 765 p = br_port_get_rtnl(dev);
676 if (p == NULL) { 766 if (p == NULL) {
677 pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n", 767 pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n",
@@ -679,9 +769,33 @@ int br_fdb_delete(struct ndmsg *ndm, struct net_device *dev,
679 return -EINVAL; 769 return -EINVAL;
680 } 770 }
681 771
682 spin_lock_bh(&p->br->hash_lock); 772 pv = nbp_get_vlan_info(p);
683 err = fdb_delete_by_addr(p, addr); 773 if (vid != VLAN_N_VID) {
684 spin_unlock_bh(&p->br->hash_lock); 774 if (!pv || !test_bit(vid, pv->vlan_bitmap)) {
775 pr_info("bridge: RTM_DELNEIGH with unconfigured "
776 "vlan %d on port %s\n", vid, dev->name);
777 return -EINVAL;
778 }
685 779
780 err = __br_fdb_delete(p, addr, vid);
781 } else {
782 if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) {
783 err = __br_fdb_delete(p, addr, 0);
784 goto out;
785 }
786
787 /* We have vlans configured on this port and user didn't
788 * specify a VLAN. To be nice, add/update entry for every
789 * vlan on this port.
790 */
791 err = -ENOENT;
792 vid = find_first_bit(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN);
793 while (vid < BR_VLAN_BITMAP_LEN) {
794 err &= __br_fdb_delete(p, addr, vid);
795 vid = find_next_bit(pv->vlan_bitmap,
796 BR_VLAN_BITMAP_LEN, vid+1);
797 }
798 }
799out:
686 return err; 800 return err;
687} 801}