aboutsummaryrefslogtreecommitdiffstats
path: root/net
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
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')
-rw-r--r--net/bridge/br_fdb.c148
-rw-r--r--net/bridge/br_private.h6
-rw-r--r--net/core/rtnetlink.c26
3 files changed, 151 insertions, 29 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}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 22915c8e9961..799dbb37e5a2 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -388,7 +388,7 @@ extern void br_fdb_update(struct net_bridge *br,
388 const unsigned char *addr, 388 const unsigned char *addr,
389 u16 vid); 389 u16 vid);
390 390
391extern int br_fdb_delete(struct ndmsg *ndm, 391extern int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
392 struct net_device *dev, 392 struct net_device *dev,
393 const unsigned char *addr); 393 const unsigned char *addr);
394extern int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], 394extern int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[],
@@ -577,13 +577,13 @@ extern void nbp_vlan_flush(struct net_bridge_port *port);
577static inline struct net_port_vlans *br_get_vlan_info( 577static inline struct net_port_vlans *br_get_vlan_info(
578 const struct net_bridge *br) 578 const struct net_bridge *br)
579{ 579{
580 return rcu_dereference(br->vlan_info); 580 return rcu_dereference_rtnl(br->vlan_info);
581} 581}
582 582
583static inline struct net_port_vlans *nbp_get_vlan_info( 583static inline struct net_port_vlans *nbp_get_vlan_info(
584 const struct net_bridge_port *p) 584 const struct net_bridge_port *p)
585{ 585{
586 return rcu_dereference(p->vlan_info); 586 return rcu_dereference_rtnl(p->vlan_info);
587} 587}
588 588
589/* Since bridge now depends on 8021Q module, but the time bridge sees the 589/* Since bridge now depends on 8021Q module, but the time bridge sees the
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index f3a112ec86d5..d8aa20f6a46e 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2119,13 +2119,17 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
2119{ 2119{
2120 struct net *net = sock_net(skb->sk); 2120 struct net *net = sock_net(skb->sk);
2121 struct ndmsg *ndm; 2121 struct ndmsg *ndm;
2122 struct nlattr *llattr; 2122 struct nlattr *tb[NDA_MAX+1];
2123 struct net_device *dev; 2123 struct net_device *dev;
2124 int err = -EINVAL; 2124 int err = -EINVAL;
2125 __u8 *addr; 2125 __u8 *addr;
2126 2126
2127 if (nlmsg_len(nlh) < sizeof(*ndm)) 2127 if (!capable(CAP_NET_ADMIN))
2128 return -EINVAL; 2128 return -EPERM;
2129
2130 err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
2131 if (err < 0)
2132 return err;
2129 2133
2130 ndm = nlmsg_data(nlh); 2134 ndm = nlmsg_data(nlh);
2131 if (ndm->ndm_ifindex == 0) { 2135 if (ndm->ndm_ifindex == 0) {
@@ -2139,13 +2143,17 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
2139 return -ENODEV; 2143 return -ENODEV;
2140 } 2144 }
2141 2145
2142 llattr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_LLADDR); 2146 if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) {
2143 if (llattr == NULL || nla_len(llattr) != ETH_ALEN) { 2147 pr_info("PF_BRIDGE: RTM_DELNEIGH with invalid address\n");
2144 pr_info("PF_BRIGDE: RTM_DELNEIGH with invalid address\n"); 2148 return -EINVAL;
2149 }
2150
2151 addr = nla_data(tb[NDA_LLADDR]);
2152 if (!is_valid_ether_addr(addr)) {
2153 pr_info("PF_BRIDGE: RTM_DELNEIGH with invalid ether address\n");
2145 return -EINVAL; 2154 return -EINVAL;
2146 } 2155 }
2147 2156
2148 addr = nla_data(llattr);
2149 err = -EOPNOTSUPP; 2157 err = -EOPNOTSUPP;
2150 2158
2151 /* Support fdb on master device the net/bridge default case */ 2159 /* Support fdb on master device the net/bridge default case */
@@ -2155,7 +2163,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
2155 const struct net_device_ops *ops = br_dev->netdev_ops; 2163 const struct net_device_ops *ops = br_dev->netdev_ops;
2156 2164
2157 if (ops->ndo_fdb_del) 2165 if (ops->ndo_fdb_del)
2158 err = ops->ndo_fdb_del(ndm, dev, addr); 2166 err = ops->ndo_fdb_del(ndm, tb, dev, addr);
2159 2167
2160 if (err) 2168 if (err)
2161 goto out; 2169 goto out;
@@ -2165,7 +2173,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
2165 2173
2166 /* Embedded bridge, macvlan, and any other device support */ 2174 /* Embedded bridge, macvlan, and any other device support */
2167 if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_del) { 2175 if ((ndm->ndm_flags & NTF_SELF) && dev->netdev_ops->ndo_fdb_del) {
2168 err = dev->netdev_ops->ndo_fdb_del(ndm, dev, addr); 2176 err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr);
2169 2177
2170 if (!err) { 2178 if (!err) {
2171 rtnl_fdb_notify(dev, addr, RTM_DELNEIGH); 2179 rtnl_fdb_notify(dev, addr, RTM_DELNEIGH);