Merge tag 'block-5.15-2021-09-05' of git://git.kernel.dk/linux-block
[linux-2.6-microblaze.git] / net / bridge / br_mdb.c
index 17a720b..0281453 100644 (file)
 
 #include "br_private.h"
 
-static bool br_rports_have_mc_router(struct net_bridge *br)
+static bool
+br_ip4_rports_get_timer(struct net_bridge_mcast_port *pmctx,
+                       unsigned long *timer)
+{
+       *timer = br_timer_value(&pmctx->ip4_mc_router_timer);
+       return !hlist_unhashed(&pmctx->ip4_rlist);
+}
+
+static bool
+br_ip6_rports_get_timer(struct net_bridge_mcast_port *pmctx,
+                       unsigned long *timer)
 {
 #if IS_ENABLED(CONFIG_IPV6)
-       return !hlist_empty(&br->ip4_mc_router_list) ||
-              !hlist_empty(&br->ip6_mc_router_list);
+       *timer = br_timer_value(&pmctx->ip6_mc_router_timer);
+       return !hlist_unhashed(&pmctx->ip6_rlist);
 #else
-       return !hlist_empty(&br->ip4_mc_router_list);
+       *timer = 0;
+       return false;
 #endif
 }
 
-static bool
-br_ip4_rports_get_timer(struct net_bridge_port *port, unsigned long *timer)
+static size_t __br_rports_one_size(void)
 {
-       *timer = br_timer_value(&port->ip4_mc_router_timer);
-       return !hlist_unhashed(&port->ip4_rlist);
+       return nla_total_size(sizeof(u32)) + /* MDBA_ROUTER_PORT */
+              nla_total_size(sizeof(u32)) + /* MDBA_ROUTER_PATTR_TIMER */
+              nla_total_size(sizeof(u8)) +  /* MDBA_ROUTER_PATTR_TYPE */
+              nla_total_size(sizeof(u32)) + /* MDBA_ROUTER_PATTR_INET_TIMER */
+              nla_total_size(sizeof(u32)) + /* MDBA_ROUTER_PATTR_INET6_TIMER */
+              nla_total_size(sizeof(u32));  /* MDBA_ROUTER_PATTR_VID */
 }
 
-static bool
-br_ip6_rports_get_timer(struct net_bridge_port *port, unsigned long *timer)
+size_t br_rports_size(const struct net_bridge_mcast *brmctx)
 {
+       struct net_bridge_mcast_port *pmctx;
+       size_t size = nla_total_size(0); /* MDBA_ROUTER */
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(pmctx, &brmctx->ip4_mc_router_list,
+                                ip4_rlist)
+               size += __br_rports_one_size();
+
 #if IS_ENABLED(CONFIG_IPV6)
-       *timer = br_timer_value(&port->ip6_mc_router_timer);
-       return !hlist_unhashed(&port->ip6_rlist);
-#else
-       *timer = 0;
-       return false;
+       hlist_for_each_entry_rcu(pmctx, &brmctx->ip6_mc_router_list,
+                                ip6_rlist)
+               size += __br_rports_one_size();
 #endif
+       rcu_read_unlock();
+
+       return size;
 }
 
-static int br_rports_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
-                              struct net_device *dev)
+int br_rports_fill_info(struct sk_buff *skb,
+                       const struct net_bridge_mcast *brmctx)
 {
-       struct net_bridge *br = netdev_priv(dev);
+       u16 vid = brmctx->vlan ? brmctx->vlan->vid : 0;
        bool have_ip4_mc_rtr, have_ip6_mc_rtr;
        unsigned long ip4_timer, ip6_timer;
        struct nlattr *nest, *port_nest;
        struct net_bridge_port *p;
 
-       if (!br->multicast_router)
-               return 0;
-
-       if (!br_rports_have_mc_router(br))
+       if (!brmctx->multicast_router || !br_rports_have_mc_router(brmctx))
                return 0;
 
        nest = nla_nest_start_noflag(skb, MDBA_ROUTER);
        if (nest == NULL)
                return -EMSGSIZE;
 
-       list_for_each_entry_rcu(p, &br->port_list, list) {
-               have_ip4_mc_rtr = br_ip4_rports_get_timer(p, &ip4_timer);
-               have_ip6_mc_rtr = br_ip6_rports_get_timer(p, &ip6_timer);
+       list_for_each_entry_rcu(p, &brmctx->br->port_list, list) {
+               struct net_bridge_mcast_port *pmctx;
+
+               if (vid) {
+                       struct net_bridge_vlan *v;
+
+                       v = br_vlan_find(nbp_vlan_group(p), vid);
+                       if (!v)
+                               continue;
+                       pmctx = &v->port_mcast_ctx;
+               } else {
+                       pmctx = &p->multicast_ctx;
+               }
+
+               have_ip4_mc_rtr = br_ip4_rports_get_timer(pmctx, &ip4_timer);
+               have_ip6_mc_rtr = br_ip6_rports_get_timer(pmctx, &ip6_timer);
 
                if (!have_ip4_mc_rtr && !have_ip6_mc_rtr)
                        continue;
@@ -79,13 +111,14 @@ static int br_rports_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
                    nla_put_u32(skb, MDBA_ROUTER_PATTR_TIMER,
                                max(ip4_timer, ip6_timer)) ||
                    nla_put_u8(skb, MDBA_ROUTER_PATTR_TYPE,
-                              p->multicast_router) ||
+                              p->multicast_ctx.multicast_router) ||
                    (have_ip4_mc_rtr &&
                     nla_put_u32(skb, MDBA_ROUTER_PATTR_INET_TIMER,
                                 ip4_timer)) ||
                    (have_ip6_mc_rtr &&
                     nla_put_u32(skb, MDBA_ROUTER_PATTR_INET6_TIMER,
-                                ip6_timer))) {
+                                ip6_timer)) ||
+                   (vid && nla_put_u16(skb, MDBA_ROUTER_PATTR_VID, vid))) {
                        nla_nest_cancel(skb, port_nest);
                        goto fail;
                }
@@ -240,7 +273,7 @@ static int __mdb_fill_info(struct sk_buff *skb,
 
        switch (mp->addr.proto) {
        case htons(ETH_P_IP):
-               dump_srcs_mode = !!(mp->br->multicast_igmp_version == 3);
+               dump_srcs_mode = !!(mp->br->multicast_ctx.multicast_igmp_version == 3);
                if (mp->addr.src.ip4) {
                        if (nla_put_in_addr(skb, MDBA_MDB_EATTR_SOURCE,
                                            mp->addr.src.ip4))
@@ -250,7 +283,7 @@ static int __mdb_fill_info(struct sk_buff *skb,
                break;
 #if IS_ENABLED(CONFIG_IPV6)
        case htons(ETH_P_IPV6):
-               dump_srcs_mode = !!(mp->br->multicast_mld_version == 2);
+               dump_srcs_mode = !!(mp->br->multicast_ctx.multicast_mld_version == 2);
                if (!ipv6_addr_any(&mp->addr.src.ip6)) {
                        if (nla_put_in6_addr(skb, MDBA_MDB_EATTR_SOURCE,
                                             &mp->addr.src.ip6))
@@ -390,6 +423,7 @@ static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
 
        for_each_netdev_rcu(net, dev) {
                if (dev->priv_flags & IFF_EBRIDGE) {
+                       struct net_bridge *br = netdev_priv(dev);
                        struct br_port_msg *bpm;
 
                        if (idx < s_idx)
@@ -406,7 +440,7 @@ static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
                        bpm->ifindex = dev->ifindex;
                        if (br_mdb_fill_info(skb, cb, dev) < 0)
                                goto out;
-                       if (br_rports_fill_info(skb, cb, dev) < 0)
+                       if (br_rports_fill_info(skb, &br->multicast_ctx) < 0)
                                goto out;
 
                        cb->args[1] = 0;
@@ -483,7 +517,7 @@ static size_t rtnl_mdb_nlmsg_size(struct net_bridge_port_group *pg)
                /* MDBA_MDB_EATTR_SOURCE */
                if (pg->key.addr.src.ip4)
                        nlmsg_size += nla_total_size(sizeof(__be32));
-               if (pg->key.port->br->multicast_igmp_version == 2)
+               if (pg->key.port->br->multicast_ctx.multicast_igmp_version == 2)
                        goto out;
                addr_size = sizeof(__be32);
                break;
@@ -492,7 +526,7 @@ static size_t rtnl_mdb_nlmsg_size(struct net_bridge_port_group *pg)
                /* MDBA_MDB_EATTR_SOURCE */
                if (!ipv6_addr_any(&pg->key.addr.src.ip6))
                        nlmsg_size += nla_total_size(sizeof(struct in6_addr));
-               if (pg->key.port->br->multicast_mld_version == 1)
+               if (pg->key.port->br->multicast_ctx.multicast_mld_version == 1)
                        goto out;
                addr_size = sizeof(struct in6_addr);
                break;
@@ -617,6 +651,9 @@ int br_mdb_replay(struct net_device *br_dev, struct net_device *dev,
 
        ASSERT_RTNL();
 
+       if (!nb)
+               return 0;
+
        if (!netif_is_bridge_master(br_dev) || !netif_is_bridge_port(dev))
                return -EINVAL;
 
@@ -686,7 +723,6 @@ out_free_mdb:
 
        return err;
 }
-EXPORT_SYMBOL_GPL(br_mdb_replay);
 
 static void br_mdb_switchdev_host_port(struct net_device *dev,
                                       struct net_device *lower_dev,
@@ -781,12 +817,12 @@ errout:
 
 static int nlmsg_populate_rtr_fill(struct sk_buff *skb,
                                   struct net_device *dev,
-                                  int ifindex, u32 pid,
+                                  int ifindex, u16 vid, u32 pid,
                                   u32 seq, int type, unsigned int flags)
 {
+       struct nlattr *nest, *port_nest;
        struct br_port_msg *bpm;
        struct nlmsghdr *nlh;
-       struct nlattr *nest;
 
        nlh = nlmsg_put(skb, pid, seq, type, sizeof(*bpm), 0);
        if (!nlh)
@@ -800,8 +836,18 @@ static int nlmsg_populate_rtr_fill(struct sk_buff *skb,
        if (!nest)
                goto cancel;
 
-       if (nla_put_u32(skb, MDBA_ROUTER_PORT, ifindex))
+       port_nest = nla_nest_start_noflag(skb, MDBA_ROUTER_PORT);
+       if (!port_nest)
+               goto end;
+       if (nla_put_nohdr(skb, sizeof(u32), &ifindex)) {
+               nla_nest_cancel(skb, port_nest);
+               goto end;
+       }
+       if (vid && nla_put_u16(skb, MDBA_ROUTER_PATTR_VID, vid)) {
+               nla_nest_cancel(skb, port_nest);
                goto end;
+       }
+       nla_nest_end(skb, port_nest);
 
        nla_nest_end(skb, nest);
        nlmsg_end(skb, nlh);
@@ -817,23 +863,28 @@ cancel:
 static inline size_t rtnl_rtr_nlmsg_size(void)
 {
        return NLMSG_ALIGN(sizeof(struct br_port_msg))
-               + nla_total_size(sizeof(__u32));
+               + nla_total_size(sizeof(__u32))
+               + nla_total_size(sizeof(u16));
 }
 
-void br_rtr_notify(struct net_device *dev, struct net_bridge_port *port,
+void br_rtr_notify(struct net_device *dev, struct net_bridge_mcast_port *pmctx,
                   int type)
 {
        struct net *net = dev_net(dev);
        struct sk_buff *skb;
        int err = -ENOBUFS;
        int ifindex;
+       u16 vid;
 
-       ifindex = port ? port->dev->ifindex : 0;
+       ifindex = pmctx ? pmctx->port->dev->ifindex : 0;
+       vid = pmctx && br_multicast_port_ctx_is_vlan(pmctx) ? pmctx->vlan->vid :
+                                                             0;
        skb = nlmsg_new(rtnl_rtr_nlmsg_size(), GFP_ATOMIC);
        if (!skb)
                goto errout;
 
-       err = nlmsg_populate_rtr_fill(skb, dev, ifindex, 0, 0, type, NTF_SELF);
+       err = nlmsg_populate_rtr_fill(skb, dev, ifindex, vid, 0, 0, type,
+                                     NTF_SELF);
        if (err < 0) {
                kfree_skb(skb);
                goto errout;
@@ -1004,14 +1055,47 @@ static int br_mdb_parse(struct sk_buff *skb, struct nlmsghdr *nlh,
        return 0;
 }
 
+static struct net_bridge_mcast *
+__br_mdb_choose_context(struct net_bridge *br,
+                       const struct br_mdb_entry *entry,
+                       struct netlink_ext_ack *extack)
+{
+       struct net_bridge_mcast *brmctx = NULL;
+       struct net_bridge_vlan *v;
+
+       if (!br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) {
+               brmctx = &br->multicast_ctx;
+               goto out;
+       }
+
+       if (!entry->vid) {
+               NL_SET_ERR_MSG_MOD(extack, "Cannot add an entry without a vlan when vlan snooping is enabled");
+               goto out;
+       }
+
+       v = br_vlan_find(br_vlan_group(br), entry->vid);
+       if (!v) {
+               NL_SET_ERR_MSG_MOD(extack, "Vlan is not configured");
+               goto out;
+       }
+       if (br_multicast_ctx_vlan_global_disabled(&v->br_mcast_ctx)) {
+               NL_SET_ERR_MSG_MOD(extack, "Vlan's multicast processing is disabled");
+               goto out;
+       }
+       brmctx = &v->br_mcast_ctx;
+out:
+       return brmctx;
+}
+
 static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
                            struct br_mdb_entry *entry,
                            struct nlattr **mdb_attrs,
                            struct netlink_ext_ack *extack)
 {
        struct net_bridge_mdb_entry *mp, *star_mp;
-       struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
+       struct net_bridge_port_group *p;
+       struct net_bridge_mcast *brmctx;
        struct br_ip group, star_group;
        unsigned long now = jiffies;
        unsigned char flags = 0;
@@ -1020,6 +1104,10 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
 
        __mdb_entry_to_br_ip(entry, &group, mdb_attrs);
 
+       brmctx = __br_mdb_choose_context(br, entry, extack);
+       if (!brmctx)
+               return -EINVAL;
+
        /* host join errors which can happen before creating the group */
        if (!port) {
                /* don't allow any flags for host-joined groups */
@@ -1053,7 +1141,7 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
                        return -EEXIST;
                }
 
-               br_multicast_host_join(mp, false);
+               br_multicast_host_join(brmctx, mp, false);
                br_mdb_notify(br->dev, mp, NULL, RTM_NEWMDB);
 
                return 0;
@@ -1084,14 +1172,15 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
        }
        rcu_assign_pointer(*pp, p);
        if (entry->state == MDB_TEMPORARY)
-               mod_timer(&p->timer, now + br->multicast_membership_interval);
+               mod_timer(&p->timer,
+                         now + brmctx->multicast_membership_interval);
        br_mdb_notify(br->dev, mp, p, RTM_NEWMDB);
        /* if we are adding a new EXCLUDE port group (*,G) it needs to be also
         * added to all S,G entries for proper replication, if we are adding
         * a new INCLUDE port (S,G) then all of *,G EXCLUDE ports need to be
         * added to it for proper replication
         */
-       if (br_multicast_should_handle_mode(br, group.proto)) {
+       if (br_multicast_should_handle_mode(brmctx, group.proto)) {
                switch (filter_mode) {
                case MCAST_EXCLUDE:
                        br_multicast_star_g_handle_mode(p, MCAST_EXCLUDE);