IGMP: Improved handling of (*,G) join and leave 49/15749/3
authorNeale Ranns <nranns@cisco.com>
Tue, 6 Nov 2018 13:51:58 +0000 (05:51 -0800)
committerDamjan Marion <dmarion@me.com>
Tue, 6 Nov 2018 17:43:26 +0000 (17:43 +0000)
Change-Id: I48a92035b58d83420eb3eed3f05a75ba283543c2
Signed-off-by: Neale Ranns <nranns@cisco.com>
src/plugins/igmp/igmp_report.c
test/test_igmp.py

index 7c08f34..2e73ee1 100644 (file)
@@ -53,35 +53,48 @@ igmp_group_mk_source_list (const igmp_membership_group_v3_t * r)
 }
 
 static void
-igmp_handle_group_update (igmp_config_t * config,
-                         const igmp_membership_group_v3_t * igmp_group)
+igmp_handle_group_exclude (igmp_config_t * config,
+                          const igmp_membership_group_v3_t * igmp_group)
 {
-  ip46_address_t *src, *srcs;
-  igmp_group_t *group;
   ip46_address_t key = {
     .ip4 = igmp_group->group_address,
   };
+  u16 n;
 
-  srcs = igmp_group_mk_source_list (igmp_group);
-  group = igmp_group_lookup (config, &key);
-
-  IGMP_DBG (" ..group-update: %U (%U, %U)",
-           format_vnet_sw_if_index_name,
-           vnet_get_main (), config->sw_if_index,
-           format_igmp_key, &key, format_igmp_src_addr_list, srcs);
+  /*
+   * treat an exclude all sources as a *,G join
+   */
+  n = clib_net_to_host_u16 (igmp_group->n_src_addresses);
 
-  if (NULL == group)
+  if (0 == n)
     {
-      group = igmp_group_alloc (config, &key, IGMP_FILTER_MODE_INCLUDE);
-    }
+      ip46_address_t *src, *srcs;
+      igmp_group_t *group;
 
-  /* create or update all sources */
-  vec_foreach (src, srcs)
-  {
-    igmp_group_src_update (group, src, IGMP_MODE_ROUTER);
-  }
+      group = igmp_group_lookup (config, &key);
+      srcs = igmp_group_mk_source_list (igmp_group);
 
-  vec_free (srcs);
+      IGMP_DBG (" ..group-update: %U (*, %U)",
+               format_vnet_sw_if_index_name,
+               vnet_get_main (), config->sw_if_index, format_igmp_key, &key);
+
+      if (NULL == group)
+       {
+         group = igmp_group_alloc (config, &key, IGMP_FILTER_MODE_INCLUDE);
+       }
+      vec_foreach (src, srcs)
+      {
+       igmp_group_src_update (group, src, IGMP_MODE_ROUTER);
+      }
+
+      vec_free (srcs);
+    }
+  else
+    {
+      IGMP_DBG (" ..group-update: %U (*, %U) source exclude ignored",
+               format_vnet_sw_if_index_name,
+               vnet_get_main (), config->sw_if_index, format_igmp_key, &key);
+    }
 }
 
 static void
@@ -131,6 +144,46 @@ igmp_handle_group_block (igmp_config_t * config,
   vec_free (srcs);
 }
 
+static void
+igmp_handle_group_update (igmp_config_t * config,
+                         const igmp_membership_group_v3_t * igmp_group)
+{
+  ip46_address_t *src, *srcs;
+  igmp_group_t *group;
+  ip46_address_t key = {
+    .ip4 = igmp_group->group_address,
+  };
+
+  /*
+   * treat a TO_INC({}) as a (*,G) leave
+   */
+  if (0 == clib_net_to_host_u16 (igmp_group->n_src_addresses))
+    {
+      return (igmp_handle_group_block (config, igmp_group));
+    }
+
+  srcs = igmp_group_mk_source_list (igmp_group);
+  group = igmp_group_lookup (config, &key);
+
+  IGMP_DBG (" ..group-update: %U (%U, %U)",
+           format_vnet_sw_if_index_name,
+           vnet_get_main (), config->sw_if_index,
+           format_igmp_key, &key, format_igmp_src_addr_list, srcs);
+
+  if (NULL == group)
+    {
+      group = igmp_group_alloc (config, &key, IGMP_FILTER_MODE_INCLUDE);
+    }
+
+  /* create or update all sources */
+  vec_foreach (src, srcs)
+  {
+    igmp_group_src_update (group, src, IGMP_MODE_ROUTER);
+  }
+
+  vec_free (srcs);
+}
+
 static void
 igmp_handle_group (igmp_config_t * config,
                   const igmp_membership_group_v3_t * igmp_group)
@@ -151,6 +204,7 @@ igmp_handle_group (igmp_config_t * config,
       break;
     case IGMP_MEMBERSHIP_GROUP_mode_is_exclude:
     case IGMP_MEMBERSHIP_GROUP_change_to_exclude:
+      igmp_handle_group_exclude (config, igmp_group);
       break;
       /*
        * all other types ignored
index da2fa9a..c1452c9 100644 (file)
@@ -629,7 +629,7 @@ class TestIgmp(VppTestCase):
         self.assertFalse(self.vapi.igmp_dump())
 
         #
-        # A (*,G) host report
+        # a TO_EX({}) / IN_EX({}) is treated like a (*,G) join
         #
         p_j = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
                IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1,
@@ -637,13 +637,78 @@ class TestIgmp(VppTestCase):
                                     option="router_alert")]) /
                IGMPv3(type="Version 3 Membership Report") /
                IGMPv3mr(numgrp=1) /
-               IGMPv3gr(rtype="Allow New Sources", maddr="239.1.1.2"))
+               IGMPv3gr(rtype="Change To Exclude Mode", maddr="239.1.1.2"))
 
         self.send(self.pg0, p_j)
 
         self.assertTrue(wait_for_igmp_event(self, 1, self.pg0,
                                             "239.1.1.2", "0.0.0.0", 1))
 
+        p_j = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+               IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1,
+                  options=[IPOption(copy_flag=1, optclass="control",
+                                    option="router_alert")]) /
+               IGMPv3(type="Version 3 Membership Report") /
+               IGMPv3mr(numgrp=1) /
+               IGMPv3gr(rtype="Mode Is Exclude", maddr="239.1.1.3"))
+
+        self.send(self.pg0, p_j)
+
+        self.assertTrue(wait_for_igmp_event(self, 1, self.pg0,
+                                            "239.1.1.3", "0.0.0.0", 1))
+
+        #
+        # A 'allow sourcees' for {} should be ignored as it should
+        # never be sent.
+        #
+        p_j = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+               IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1,
+                  options=[IPOption(copy_flag=1, optclass="control",
+                                    option="router_alert")]) /
+               IGMPv3(type="Version 3 Membership Report") /
+               IGMPv3mr(numgrp=1) /
+               IGMPv3gr(rtype="Allow New Sources", maddr="239.1.1.4"))
+
+        self.send(self.pg0, p_j)
+
+        dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
+        self.assertTrue(find_igmp_state(dump, self.pg0,
+                                        "239.1.1.2", "0.0.0.0"))
+        self.assertTrue(find_igmp_state(dump, self.pg0,
+                                        "239.1.1.3", "0.0.0.0"))
+        self.assertFalse(find_igmp_state(dump, self.pg0,
+                                         "239.1.1.4", "0.0.0.0"))
+
+        #
+        # a TO_IN({}) and IS_IN({}) are treated like a (*,G) leave
+        #
+        self.vapi.cli("set logging class igmp level debug")
+        p_l = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+               IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1,
+                  options=[IPOption(copy_flag=1, optclass="control",
+                                    option="router_alert")]) /
+               IGMPv3(type="Version 3 Membership Report") /
+               IGMPv3mr(numgrp=1) /
+               IGMPv3gr(rtype="Change To Include Mode", maddr="239.1.1.2"))
+
+        self.send(self.pg0, p_l)
+        self.assertTrue(wait_for_igmp_event(self, 2, self.pg0,
+                                            "239.1.1.2", "0.0.0.0", 0))
+
+        p_l = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+               IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0, ttl=1,
+                  options=[IPOption(copy_flag=1, optclass="control",
+                                    option="router_alert")]) /
+               IGMPv3(type="Version 3 Membership Report") /
+               IGMPv3mr(numgrp=1) /
+               IGMPv3gr(rtype="Mode Is Include", maddr="239.1.1.3"))
+
+        self.send(self.pg0, p_l)
+
+        self.assertTrue(wait_for_igmp_event(self, 2, self.pg0,
+                                            "239.1.1.3", "0.0.0.0", 0))
+        self.assertFalse(self.vapi.igmp_dump(self.pg0.sw_if_index))
+
         #
         # disable router config
         #