OSDN Git Service

net: bridge: mdb: allow add/delete for host-joined groups
authorNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Sat, 17 Aug 2019 11:22:13 +0000 (14:22 +0300)
committerDavid S. Miller <davem@davemloft.net>
Sat, 17 Aug 2019 19:36:57 +0000 (12:36 -0700)
Currently this is needed only for user-space compatibility, so similar
object adds/deletes as the dumped ones would succeed. Later it can be
used for L2 mcast MAC add/delete.

v3: fix compiler warning (DaveM)
v2: don't send a notification when used from user-space, arm the group
    timer if no ports are left after host entry del

Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/bridge/br_mdb.c
net/bridge/br_multicast.c
net/bridge/br_private.h

index 9852734..4459463 100644 (file)
@@ -616,6 +616,19 @@ static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
                        return err;
        }
 
+       /* host join */
+       if (!port) {
+               /* don't allow any flags for host-joined groups */
+               if (state)
+                       return -EINVAL;
+               if (mp->host_joined)
+                       return -EEXIST;
+
+               br_multicast_host_join(mp, false);
+
+               return 0;
+       }
+
        for (pp = &mp->ports;
             (p = mlock_dereference(*pp, br)) != NULL;
             pp = &p->next) {
@@ -640,19 +653,21 @@ static int __br_mdb_add(struct net *net, struct net_bridge *br,
 {
        struct br_ip ip;
        struct net_device *dev;
-       struct net_bridge_port *p;
+       struct net_bridge_port *p = NULL;
        int ret;
 
        if (!netif_running(br->dev) || !br_opt_get(br, BROPT_MULTICAST_ENABLED))
                return -EINVAL;
 
-       dev = __dev_get_by_index(net, entry->ifindex);
-       if (!dev)
-               return -ENODEV;
+       if (entry->ifindex != br->dev->ifindex) {
+               dev = __dev_get_by_index(net, entry->ifindex);
+               if (!dev)
+                       return -ENODEV;
 
-       p = br_port_get_rtnl(dev);
-       if (!p || p->br != br || p->state == BR_STATE_DISABLED)
-               return -EINVAL;
+               p = br_port_get_rtnl(dev);
+               if (!p || p->br != br || p->state == BR_STATE_DISABLED)
+                       return -EINVAL;
+       }
 
        __mdb_entry_to_br_ip(entry, &ip);
 
@@ -667,9 +682,9 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
 {
        struct net *net = sock_net(skb->sk);
        struct net_bridge_vlan_group *vg;
+       struct net_bridge_port *p = NULL;
        struct net_device *dev, *pdev;
        struct br_mdb_entry *entry;
-       struct net_bridge_port *p;
        struct net_bridge_vlan *v;
        struct net_bridge *br;
        int err;
@@ -680,15 +695,19 @@ static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
 
        br = netdev_priv(dev);
 
-       pdev = __dev_get_by_index(net, entry->ifindex);
-       if (!pdev)
-               return -ENODEV;
+       if (entry->ifindex != br->dev->ifindex) {
+               pdev = __dev_get_by_index(net, entry->ifindex);
+               if (!pdev)
+                       return -ENODEV;
 
-       p = br_port_get_rtnl(pdev);
-       if (!p || p->br != br || p->state == BR_STATE_DISABLED)
-               return -EINVAL;
+               p = br_port_get_rtnl(pdev);
+               if (!p || p->br != br || p->state == BR_STATE_DISABLED)
+                       return -EINVAL;
+               vg = nbp_vlan_group(p);
+       } else {
+               vg = br_vlan_group(br);
+       }
 
-       vg = nbp_vlan_group(p);
        /* If vlan filtering is enabled and VLAN is not specified
         * install mdb entry on all vlans configured on the port.
         */
@@ -727,6 +746,15 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
        if (!mp)
                goto unlock;
 
+       /* host leave */
+       if (entry->ifindex == mp->br->dev->ifindex && mp->host_joined) {
+               br_multicast_host_leave(mp, false);
+               err = 0;
+               if (!mp->ports && netif_running(br->dev))
+                       mod_timer(&mp->timer, jiffies);
+               goto unlock;
+       }
+
        for (pp = &mp->ports;
             (p = mlock_dereference(*pp, br)) != NULL;
             pp = &p->next) {
@@ -759,9 +787,9 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
 {
        struct net *net = sock_net(skb->sk);
        struct net_bridge_vlan_group *vg;
+       struct net_bridge_port *p = NULL;
        struct net_device *dev, *pdev;
        struct br_mdb_entry *entry;
-       struct net_bridge_port *p;
        struct net_bridge_vlan *v;
        struct net_bridge *br;
        int err;
@@ -772,15 +800,19 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
 
        br = netdev_priv(dev);
 
-       pdev = __dev_get_by_index(net, entry->ifindex);
-       if (!pdev)
-               return -ENODEV;
+       if (entry->ifindex != br->dev->ifindex) {
+               pdev = __dev_get_by_index(net, entry->ifindex);
+               if (!pdev)
+                       return -ENODEV;
 
-       p = br_port_get_rtnl(pdev);
-       if (!p || p->br != br || p->state == BR_STATE_DISABLED)
-               return -EINVAL;
+               p = br_port_get_rtnl(pdev);
+               if (!p || p->br != br || p->state == BR_STATE_DISABLED)
+                       return -EINVAL;
+               vg = nbp_vlan_group(p);
+       } else {
+               vg = br_vlan_group(br);
+       }
 
-       vg = nbp_vlan_group(p);
        /* If vlan filtering is enabled and VLAN is not specified
         * delete mdb entry on all vlans configured on the port.
         */
index 9b379e1..ad12fe3 100644 (file)
@@ -148,8 +148,7 @@ static void br_multicast_group_expired(struct timer_list *t)
        if (!netif_running(br->dev) || timer_pending(&mp->timer))
                goto out;
 
-       mp->host_joined = false;
-       br_mdb_notify(br->dev, NULL, &mp->addr, RTM_DELMDB, 0);
+       br_multicast_host_leave(mp, true);
 
        if (mp->ports)
                goto out;
@@ -512,6 +511,27 @@ static bool br_port_group_equal(struct net_bridge_port_group *p,
        return ether_addr_equal(src, p->eth_addr);
 }
 
+void br_multicast_host_join(struct net_bridge_mdb_entry *mp, bool notify)
+{
+       if (!mp->host_joined) {
+               mp->host_joined = true;
+               if (notify)
+                       br_mdb_notify(mp->br->dev, NULL, &mp->addr,
+                                     RTM_NEWMDB, 0);
+       }
+       mod_timer(&mp->timer, jiffies + mp->br->multicast_membership_interval);
+}
+
+void br_multicast_host_leave(struct net_bridge_mdb_entry *mp, bool notify)
+{
+       if (!mp->host_joined)
+               return;
+
+       mp->host_joined = false;
+       if (notify)
+               br_mdb_notify(mp->br->dev, NULL, &mp->addr, RTM_DELMDB, 0);
+}
+
 static int br_multicast_add_group(struct net_bridge *br,
                                  struct net_bridge_port *port,
                                  struct br_ip *group,
@@ -534,11 +554,7 @@ static int br_multicast_add_group(struct net_bridge *br,
                goto err;
 
        if (!port) {
-               if (!mp->host_joined) {
-                       mp->host_joined = true;
-                       br_mdb_notify(br->dev, NULL, &mp->addr, RTM_NEWMDB, 0);
-               }
-               mod_timer(&mp->timer, now + br->multicast_membership_interval);
+               br_multicast_host_join(mp, true);
                goto out;
        }
 
index b7a4942..ce2ab14 100644 (file)
@@ -702,6 +702,8 @@ void br_multicast_get_stats(const struct net_bridge *br,
                            struct br_mcast_stats *dest);
 void br_mdb_init(void);
 void br_mdb_uninit(void);
+void br_multicast_host_join(struct net_bridge_mdb_entry *mp, bool notify);
+void br_multicast_host_leave(struct net_bridge_mdb_entry *mp, bool notify);
 
 #define mlock_dereference(X, br) \
        rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock))