In order to process IGMPV3/MLDv2_MODE_IS_INCLUDE/EXCLUDE report types we
need some new helpers which allow us to set/clear flags for all current
entries and later delete marked entries after the report sources have been
processed.

v3: add IPv6/MLDv2 support
v2: drop flag helpers and directly do flag bit operations

Signed-off-by: Nikolay Aleksandrov <niko...@cumulusnetworks.com>
---
 net/bridge/br_multicast.c | 126 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)

diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 98600a08114e..634415e8c50f 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -1229,6 +1229,21 @@ void br_multicast_disable_port(struct net_bridge_port 
*port)
        spin_unlock(&br->multicast_lock);
 }
 
+static int __grp_src_delete_marked(struct net_bridge_port_group *pg)
+{
+       struct net_bridge_group_src *ent;
+       struct hlist_node *tmp;
+       int deleted = 0;
+
+       hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
+               if (ent->flags & BR_SGRP_F_DELETE) {
+                       br_multicast_del_group_src(ent);
+                       deleted++;
+               }
+
+       return deleted;
+}
+
 /* State          Msg type      New state                Actions
  * INCLUDE (A)    IS_IN (B)     INCLUDE (A+B)            (B)=GMI
  * INCLUDE (A)    ALLOW (B)     INCLUDE (A+B)            (B)=GMI
@@ -1263,6 +1278,101 @@ static bool br_multicast_isinc_allow(struct 
net_bridge_port_group *pg,
        return changed;
 }
 
+/* State          Msg type      New state                Actions
+ * INCLUDE (A)    IS_EX (B)     EXCLUDE (A*B,B-A)        (B-A)=0
+ *                                                       Delete (A-B)
+ *                                                       Group Timer=GMI
+ */
+static void __grp_src_isexc_incl(struct net_bridge_port_group *pg,
+                                void *srcs, u32 nsrcs, size_t src_size)
+{
+       struct net_bridge_group_src *ent;
+       struct br_ip src_ip;
+       u32 src_idx;
+
+       hlist_for_each_entry(ent, &pg->src_list, node)
+               ent->flags |= BR_SGRP_F_DELETE;
+
+       memset(&src_ip, 0, sizeof(src_ip));
+       src_ip.proto = pg->addr.proto;
+       for (src_idx = 0; src_idx < nsrcs; src_idx++) {
+               memcpy(&src_ip.u, srcs, src_size);
+               ent = br_multicast_find_group_src(pg, &src_ip);
+               if (ent)
+                       ent->flags &= ~BR_SGRP_F_DELETE;
+               else
+                       br_multicast_new_group_src(pg, &src_ip);
+               srcs += src_size;
+       }
+
+       __grp_src_delete_marked(pg);
+}
+
+/* State          Msg type      New state                Actions
+ * EXCLUDE (X,Y)  IS_EX (A)     EXCLUDE (A-Y,Y*A)        (A-X-Y)=GMI
+ *                                                       Delete (X-A)
+ *                                                       Delete (Y-A)
+ *                                                       Group Timer=GMI
+ */
+static bool __grp_src_isexc_excl(struct net_bridge_port_group *pg,
+                                void *srcs, u32 nsrcs, size_t src_size)
+{
+       struct net_bridge *br = pg->port->br;
+       struct net_bridge_group_src *ent;
+       unsigned long now = jiffies;
+       bool changed = false;
+       struct br_ip src_ip;
+       u32 src_idx;
+
+       hlist_for_each_entry(ent, &pg->src_list, node)
+               ent->flags |= BR_SGRP_F_DELETE;
+
+       memset(&src_ip, 0, sizeof(src_ip));
+       src_ip.proto = pg->addr.proto;
+       for (src_idx = 0; src_idx < nsrcs; src_idx++) {
+               memcpy(&src_ip.u, srcs, src_size);
+               ent = br_multicast_find_group_src(pg, &src_ip);
+               if (ent) {
+                       ent->flags &= ~BR_SGRP_F_DELETE;
+               } else {
+                       ent = br_multicast_new_group_src(pg, &src_ip);
+                       if (ent) {
+                               mod_timer(&ent->timer,
+                                         now + br_multicast_gmi(br));
+                               changed = true;
+                       }
+               }
+               srcs += src_size;
+       }
+
+       if (__grp_src_delete_marked(pg))
+               changed = true;
+
+       return changed;
+}
+
+static bool br_multicast_isexc(struct net_bridge_port_group *pg,
+                              void *srcs, u32 nsrcs, size_t src_size)
+{
+       struct net_bridge *br = pg->port->br;
+       bool changed = false;
+
+       switch (pg->filter_mode) {
+       case MCAST_INCLUDE:
+               __grp_src_isexc_incl(pg, srcs, nsrcs, src_size);
+               changed = true;
+               break;
+       case MCAST_EXCLUDE:
+               changed = __grp_src_isexc_excl(pg, srcs, nsrcs, src_size);
+               break;
+       }
+
+       pg->filter_mode = MCAST_EXCLUDE;
+       mod_timer(&pg->timer, jiffies + br_multicast_gmi(br));
+
+       return changed;
+}
+
 static struct net_bridge_port_group *
 br_multicast_find_port(struct net_bridge_mdb_entry *mp,
                       struct net_bridge_port *p,
@@ -1360,6 +1470,14 @@ static int br_ip4_multicast_igmp3_report(struct 
net_bridge *br,
                        changed = br_multicast_isinc_allow(pg, grec->grec_src,
                                                           nsrcs, 
sizeof(__be32));
                        break;
+               case IGMPV3_MODE_IS_INCLUDE:
+                       changed = br_multicast_isinc_allow(pg, grec->grec_src, 
nsrcs,
+                                                          sizeof(__be32));
+                       break;
+               case IGMPV3_MODE_IS_EXCLUDE:
+                       changed = br_multicast_isexc(pg, grec->grec_src, nsrcs,
+                                                    sizeof(__be32));
+                       break;
                }
                if (changed)
                        br_mdb_notify(br->dev, mdst, pg, RTM_NEWMDB);
@@ -1466,6 +1584,14 @@ static int br_ip6_multicast_mld2_report(struct 
net_bridge *br,
                                                           nsrcs,
                                                           sizeof(struct 
in6_addr));
                        break;
+               case MLD2_MODE_IS_INCLUDE:
+                       changed = br_multicast_isinc_allow(pg, grec->grec_src, 
nsrcs,
+                                                          sizeof(struct 
in6_addr));
+                       break;
+               case MLD2_MODE_IS_EXCLUDE:
+                       changed = br_multicast_isexc(pg, grec->grec_src, nsrcs,
+                                                    sizeof(struct in6_addr));
+                       break;
                }
                if (changed)
                        br_mdb_notify(br->dev, mdst, pg, RTM_NEWMDB);
-- 
2.25.4

Reply via email to