aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/bridge/br_mdb.c175
-rw-r--r--net/bridge/br_multicast.c30
-rw-r--r--net/bridge/br_private.h2
3 files changed, 142 insertions, 65 deletions
diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
index 428af1abf8cc..44594635a972 100644
--- a/net/bridge/br_mdb.c
+++ b/net/bridge/br_mdb.c
@@ -77,6 +77,53 @@ static void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip)
77#endif 77#endif
78} 78}
79 79
80static int __mdb_fill_info(struct sk_buff *skb,
81 struct net_bridge_mdb_entry *mp,
82 struct net_bridge_port_group *p)
83{
84 struct timer_list *mtimer;
85 struct nlattr *nest_ent;
86 struct br_mdb_entry e;
87 u8 flags = 0;
88 int ifindex;
89
90 memset(&e, 0, sizeof(e));
91 if (p) {
92 ifindex = p->port->dev->ifindex;
93 mtimer = &p->timer;
94 flags = p->flags;
95 } else {
96 ifindex = mp->br->dev->ifindex;
97 mtimer = &mp->timer;
98 }
99
100 __mdb_entry_fill_flags(&e, flags);
101 e.ifindex = ifindex;
102 e.vid = mp->addr.vid;
103 if (mp->addr.proto == htons(ETH_P_IP))
104 e.addr.u.ip4 = mp->addr.u.ip4;
105#if IS_ENABLED(CONFIG_IPV6)
106 if (mp->addr.proto == htons(ETH_P_IPV6))
107 e.addr.u.ip6 = mp->addr.u.ip6;
108#endif
109 e.addr.proto = mp->addr.proto;
110 nest_ent = nla_nest_start_noflag(skb,
111 MDBA_MDB_ENTRY_INFO);
112 if (!nest_ent)
113 return -EMSGSIZE;
114
115 if (nla_put_nohdr(skb, sizeof(e), &e) ||
116 nla_put_u32(skb,
117 MDBA_MDB_EATTR_TIMER,
118 br_timer_value(mtimer))) {
119 nla_nest_cancel(skb, nest_ent);
120 return -EMSGSIZE;
121 }
122 nla_nest_end(skb, nest_ent);
123
124 return 0;
125}
126
80static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb, 127static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
81 struct net_device *dev) 128 struct net_device *dev)
82{ 129{
@@ -95,7 +142,6 @@ static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
95 hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) { 142 hlist_for_each_entry_rcu(mp, &br->mdb_list, mdb_node) {
96 struct net_bridge_port_group *p; 143 struct net_bridge_port_group *p;
97 struct net_bridge_port_group __rcu **pp; 144 struct net_bridge_port_group __rcu **pp;
98 struct net_bridge_port *port;
99 145
100 if (idx < s_idx) 146 if (idx < s_idx)
101 goto skip; 147 goto skip;
@@ -106,43 +152,24 @@ static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
106 break; 152 break;
107 } 153 }
108 154
155 if (mp->host_joined) {
156 err = __mdb_fill_info(skb, mp, NULL);
157 if (err) {
158 nla_nest_cancel(skb, nest2);
159 break;
160 }
161 }
162
109 for (pp = &mp->ports; (p = rcu_dereference(*pp)) != NULL; 163 for (pp = &mp->ports; (p = rcu_dereference(*pp)) != NULL;
110 pp = &p->next) { 164 pp = &p->next) {
111 struct nlattr *nest_ent; 165 if (!p->port)
112 struct br_mdb_entry e;
113
114 port = p->port;
115 if (!port)
116 continue; 166 continue;
117 167
118 memset(&e, 0, sizeof(e)); 168 err = __mdb_fill_info(skb, mp, p);
119 e.ifindex = port->dev->ifindex; 169 if (err) {
120 e.vid = p->addr.vid;
121 __mdb_entry_fill_flags(&e, p->flags);
122 if (p->addr.proto == htons(ETH_P_IP))
123 e.addr.u.ip4 = p->addr.u.ip4;
124#if IS_ENABLED(CONFIG_IPV6)
125 if (p->addr.proto == htons(ETH_P_IPV6))
126 e.addr.u.ip6 = p->addr.u.ip6;
127#endif
128 e.addr.proto = p->addr.proto;
129 nest_ent = nla_nest_start_noflag(skb,
130 MDBA_MDB_ENTRY_INFO);
131 if (!nest_ent) {
132 nla_nest_cancel(skb, nest2);
133 err = -EMSGSIZE;
134 goto out;
135 }
136 if (nla_put_nohdr(skb, sizeof(e), &e) ||
137 nla_put_u32(skb,
138 MDBA_MDB_EATTR_TIMER,
139 br_timer_value(&p->timer))) {
140 nla_nest_cancel(skb, nest_ent);
141 nla_nest_cancel(skb, nest2); 170 nla_nest_cancel(skb, nest2);
142 err = -EMSGSIZE;
143 goto out; 171 goto out;
144 } 172 }
145 nla_nest_end(skb, nest_ent);
146 } 173 }
147 nla_nest_end(skb, nest2); 174 nla_nest_end(skb, nest2);
148skip: 175skip:
@@ -589,6 +616,19 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
589 return err; 616 return err;
590 } 617 }
591 618
619 /* host join */
620 if (!port) {
621 /* don't allow any flags for host-joined groups */
622 if (state)
623 return -EINVAL;
624 if (mp->host_joined)
625 return -EEXIST;
626
627 br_multicast_host_join(mp, false);
628
629 return 0;
630 }
631
592 for (pp = &mp->ports; 632 for (pp = &mp->ports;
593 (p = mlock_dereference(*pp, br)) != NULL; 633 (p = mlock_dereference(*pp, br)) != NULL;
594 pp = &p->next) { 634 pp = &p->next) {
@@ -613,19 +653,21 @@ static int __br_mdb_add(struct net *net, struct net_bridge *br,
613{ 653{
614 struct br_ip ip; 654 struct br_ip ip;
615 struct net_device *dev; 655 struct net_device *dev;
616 struct net_bridge_port *p; 656 struct net_bridge_port *p = NULL;
617 int ret; 657 int ret;
618 658
619 if (!netif_running(br->dev) || !br_opt_get(br, BROPT_MULTICAST_ENABLED)) 659 if (!netif_running(br->dev) || !br_opt_get(br, BROPT_MULTICAST_ENABLED))
620 return -EINVAL; 660 return -EINVAL;
621 661
622 dev = __dev_get_by_index(net, entry->ifindex); 662 if (entry->ifindex != br->dev->ifindex) {
623 if (!dev) 663 dev = __dev_get_by_index(net, entry->ifindex);
624 return -ENODEV; 664 if (!dev)
665 return -ENODEV;
625 666
626 p = br_port_get_rtnl(dev); 667 p = br_port_get_rtnl(dev);
627 if (!p || p->br != br || p->state == BR_STATE_DISABLED) 668 if (!p || p->br != br || p->state == BR_STATE_DISABLED)
628 return -EINVAL; 669 return -EINVAL;
670 }
629 671
630 __mdb_entry_to_br_ip(entry, &ip); 672 __mdb_entry_to_br_ip(entry, &ip);
631 673
@@ -640,9 +682,9 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
640{ 682{
641 struct net *net = sock_net(skb->sk); 683 struct net *net = sock_net(skb->sk);
642 struct net_bridge_vlan_group *vg; 684 struct net_bridge_vlan_group *vg;
685 struct net_bridge_port *p = NULL;
643 struct net_device *dev, *pdev; 686 struct net_device *dev, *pdev;
644 struct br_mdb_entry *entry; 687 struct br_mdb_entry *entry;
645 struct net_bridge_port *p;
646 struct net_bridge_vlan *v; 688 struct net_bridge_vlan *v;
647 struct net_bridge *br; 689 struct net_bridge *br;
648 int err; 690 int err;
@@ -653,18 +695,22 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
653 695
654 br = netdev_priv(dev); 696 br = netdev_priv(dev);
655 697
698 if (entry->ifindex != br->dev->ifindex) {
699 pdev = __dev_get_by_index(net, entry->ifindex);
700 if (!pdev)
701 return -ENODEV;
702
703 p = br_port_get_rtnl(pdev);
704 if (!p || p->br != br || p->state == BR_STATE_DISABLED)
705 return -EINVAL;
706 vg = nbp_vlan_group(p);
707 } else {
708 vg = br_vlan_group(br);
709 }
710
656 /* If vlan filtering is enabled and VLAN is not specified 711 /* If vlan filtering is enabled and VLAN is not specified
657 * install mdb entry on all vlans configured on the port. 712 * install mdb entry on all vlans configured on the port.
658 */ 713 */
659 pdev = __dev_get_by_index(net, entry->ifindex);
660 if (!pdev)
661 return -ENODEV;
662
663 p = br_port_get_rtnl(pdev);
664 if (!p || p->br != br || p->state == BR_STATE_DISABLED)
665 return -EINVAL;
666
667 vg = nbp_vlan_group(p);
668 if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) { 714 if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) {
669 list_for_each_entry(v, &vg->vlan_list, vlist) { 715 list_for_each_entry(v, &vg->vlan_list, vlist) {
670 entry->vid = v->vid; 716 entry->vid = v->vid;
@@ -700,6 +746,15 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
700 if (!mp) 746 if (!mp)
701 goto unlock; 747 goto unlock;
702 748
749 /* host leave */
750 if (entry->ifindex == mp->br->dev->ifindex && mp->host_joined) {
751 br_multicast_host_leave(mp, false);
752 err = 0;
753 if (!mp->ports && netif_running(br->dev))
754 mod_timer(&mp->timer, jiffies);
755 goto unlock;
756 }
757
703 for (pp = &mp->ports; 758 for (pp = &mp->ports;
704 (p = mlock_dereference(*pp, br)) != NULL; 759 (p = mlock_dereference(*pp, br)) != NULL;
705 pp = &p->next) { 760 pp = &p->next) {
@@ -732,9 +787,9 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
732{ 787{
733 struct net *net = sock_net(skb->sk); 788 struct net *net = sock_net(skb->sk);
734 struct net_bridge_vlan_group *vg; 789 struct net_bridge_vlan_group *vg;
790 struct net_bridge_port *p = NULL;
735 struct net_device *dev, *pdev; 791 struct net_device *dev, *pdev;
736 struct br_mdb_entry *entry; 792 struct br_mdb_entry *entry;
737 struct net_bridge_port *p;
738 struct net_bridge_vlan *v; 793 struct net_bridge_vlan *v;
739 struct net_bridge *br; 794 struct net_bridge *br;
740 int err; 795 int err;
@@ -745,18 +800,22 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
745 800
746 br = netdev_priv(dev); 801 br = netdev_priv(dev);
747 802
803 if (entry->ifindex != br->dev->ifindex) {
804 pdev = __dev_get_by_index(net, entry->ifindex);
805 if (!pdev)
806 return -ENODEV;
807
808 p = br_port_get_rtnl(pdev);
809 if (!p || p->br != br || p->state == BR_STATE_DISABLED)
810 return -EINVAL;
811 vg = nbp_vlan_group(p);
812 } else {
813 vg = br_vlan_group(br);
814 }
815
748 /* If vlan filtering is enabled and VLAN is not specified 816 /* If vlan filtering is enabled and VLAN is not specified
749 * delete mdb entry on all vlans configured on the port. 817 * delete mdb entry on all vlans configured on the port.
750 */ 818 */
751 pdev = __dev_get_by_index(net, entry->ifindex);
752 if (!pdev)
753 return -ENODEV;
754
755 p = br_port_get_rtnl(pdev);
756 if (!p || p->br != br || p->state == BR_STATE_DISABLED)
757 return -EINVAL;
758
759 vg = nbp_vlan_group(p);
760 if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) { 819 if (br_vlan_enabled(br->dev) && vg && entry->vid == 0) {
761 list_for_each_entry(v, &vg->vlan_list, vlist) { 820 list_for_each_entry(v, &vg->vlan_list, vlist) {
762 entry->vid = v->vid; 821 entry->vid = v->vid;
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 9b379e110129..ad12fe3fca8c 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -148,8 +148,7 @@ static void br_multicast_group_expired(struct timer_list *t)
148 if (!netif_running(br->dev) || timer_pending(&mp->timer)) 148 if (!netif_running(br->dev) || timer_pending(&mp->timer))
149 goto out; 149 goto out;
150 150
151 mp->host_joined = false; 151 br_multicast_host_leave(mp, true);
152 br_mdb_notify(br->dev, NULL, &mp->addr, RTM_DELMDB, 0);
153 152
154 if (mp->ports) 153 if (mp->ports)
155 goto out; 154 goto out;
@@ -512,6 +511,27 @@ static bool br_port_group_equal(struct net_bridge_port_group *p,
512 return ether_addr_equal(src, p->eth_addr); 511 return ether_addr_equal(src, p->eth_addr);
513} 512}
514 513
514void br_multicast_host_join(struct net_bridge_mdb_entry *mp, bool notify)
515{
516 if (!mp->host_joined) {
517 mp->host_joined = true;
518 if (notify)
519 br_mdb_notify(mp->br->dev, NULL, &mp->addr,
520 RTM_NEWMDB, 0);
521 }
522 mod_timer(&mp->timer, jiffies + mp->br->multicast_membership_interval);
523}
524
525void br_multicast_host_leave(struct net_bridge_mdb_entry *mp, bool notify)
526{
527 if (!mp->host_joined)
528 return;
529
530 mp->host_joined = false;
531 if (notify)
532 br_mdb_notify(mp->br->dev, NULL, &mp->addr, RTM_DELMDB, 0);
533}
534
515static int br_multicast_add_group(struct net_bridge *br, 535static int br_multicast_add_group(struct net_bridge *br,
516 struct net_bridge_port *port, 536 struct net_bridge_port *port,
517 struct br_ip *group, 537 struct br_ip *group,
@@ -534,11 +554,7 @@ static int br_multicast_add_group(struct net_bridge *br,
534 goto err; 554 goto err;
535 555
536 if (!port) { 556 if (!port) {
537 if (!mp->host_joined) { 557 br_multicast_host_join(mp, true);
538 mp->host_joined = true;
539 br_mdb_notify(br->dev, NULL, &mp->addr, RTM_NEWMDB, 0);
540 }
541 mod_timer(&mp->timer, now + br->multicast_membership_interval);
542 goto out; 558 goto out;
543 } 559 }
544 560
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index b7a4942ff1b3..ce2ab14ee605 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -702,6 +702,8 @@ void br_multicast_get_stats(const struct net_bridge *br,
702 struct br_mcast_stats *dest); 702 struct br_mcast_stats *dest);
703void br_mdb_init(void); 703void br_mdb_init(void);
704void br_mdb_uninit(void); 704void br_mdb_uninit(void);
705void br_multicast_host_join(struct net_bridge_mdb_entry *mp, bool notify);
706void br_multicast_host_leave(struct net_bridge_mdb_entry *mp, bool notify);
705 707
706#define mlock_dereference(X, br) \ 708#define mlock_dereference(X, br) \
707 rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock)) 709 rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock))