diff options
-rw-r--r-- | net/bridge/br_mdb.c | 175 | ||||
-rw-r--r-- | net/bridge/br_multicast.c | 30 | ||||
-rw-r--r-- | net/bridge/br_private.h | 2 |
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 | ||
80 | static 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 | |||
80 | static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb, | 127 | static 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); |
148 | skip: | 175 | skip: |
@@ -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 | ||
514 | void 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 | |||
525 | void 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 | |||
515 | static int br_multicast_add_group(struct net_bridge *br, | 535 | static 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); |
703 | void br_mdb_init(void); | 703 | void br_mdb_init(void); |
704 | void br_mdb_uninit(void); | 704 | void br_mdb_uninit(void); |
705 | void br_multicast_host_join(struct net_bridge_mdb_entry *mp, bool notify); | ||
706 | void 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)) |