def test_igmp_proxy_device(self): """ IGMP proxy device """ self.pg2.admin_down() self.pg2.unconfig_ip4() self.pg2.set_table_ip4(0) self.pg2.config_ip4() self.pg2.admin_up() self.vapi.cli('test igmp timers query 10 src 3 leave 1') # enable IGMP self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 1, IGMP_MODE.ROUTER) self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 1, IGMP_MODE.ROUTER) # create IGMP proxy device self.vapi.igmp_proxy_device_add_del(0, self.pg0.sw_if_index, 1) self.vapi.igmp_proxy_device_add_del_interface(0, self.pg1.sw_if_index, 1) self.vapi.igmp_proxy_device_add_del_interface(0, self.pg2.sw_if_index, 1) # send join on pg1. join should be proxied by pg0 p_j = self._create_igmpv3_pck(self.pg1, "Allow New Sources", "239.1.1.1", ["10.1.1.1", "10.1.1.2"]) self.send(self.pg1, p_j) capture = self.pg0.get_capture(1, timeout=1) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.1", "10.1.1.2"]), "Allow New Sources")]) self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32)) # send join on pg2. join should be proxied by pg0. # the group should contain only 10.1.1.3 as # 10.1.1.1 was already reported p_j = self._create_igmpv3_pck(self.pg2, "Allow New Sources", "239.1.1.1", ["10.1.1.1", "10.1.1.3"]) self.send(self.pg2, p_j) capture = self.pg0.get_capture(1, timeout=1) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.3"]), "Allow New Sources")]) self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32)) # send leave on pg2. leave for 10.1.1.3 should be proxyed # as pg2 was the only interface interested in 10.1.1.3 p_l = self._create_igmpv3_pck(self.pg2, "Block Old Sources", "239.1.1.1", ["10.1.1.3"]) self.send(self.pg2, p_l) capture = self.pg0.get_capture(1, timeout=2) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.3"]), "Block Old Sources")]) self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32)) # disable igmp on pg1 (also removes interface from proxy device) # proxy leave for 10.1.1.2. pg2 is still interested in 10.1.1.1 self.pg_enable_capture(self.pg_interfaces) self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 0, IGMP_MODE.ROUTER) capture = self.pg0.get_capture(1, timeout=1) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.2"]), "Block Old Sources")]) self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32)) # disable IGMP on pg0 and pg1. # disabling IGMP on pg0 (proxy device upstream interface) # removes this proxy device self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST) self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 0, IGMP_MODE.ROUTER) self.assertFalse(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
def test_igmp_host(self): """ IGMP Host functions """ # # Enable interface for host functions # self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST) # # Add one S,G of state and expect a state-change event report # indicating the addition of the S,G # h1 = self.add_group(self.pg0, IgmpSG("239.1.1.1", ["1.1.1.1"])) # search for the corresponding state created in VPP dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertEqual(len(dump), 1) self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "1.1.1.1")) # # Send a general query (to the all router's address) # expect VPP to respond with a membership report. # Pad the query with 0 - some devices in the big wild # internet are prone to this. # p_g = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='224.0.0.1', tos=0xc0) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="0.0.0.0") / Raw(b'\x00' * 10)) self.send(self.pg0, p_g) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # Group specific query # p_gs = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1")) self.send(self.pg0, p_gs) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # A group and source specific query, with the source matching # the source VPP has # p_gs1 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1"])) self.send(self.pg0, p_gs1) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # A group and source specific query that reports more sources # than the packet actually has. # p_gs2 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1", numsrc=4, srcaddrs=["1.1.1.1"])) self.send_and_assert_no_replies(self.pg0, p_gs2, timeout=10) # # A group and source specific query, with the source NOT matching # the source VPP has. There should be no response. # p_gs2 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.2"])) self.send_and_assert_no_replies(self.pg0, p_gs2, timeout=10) # # A group and source specific query, with the multiple sources # one of which matches the source VPP has. # The report should contain only the source VPP has. # p_gs3 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1", "1.1.1.2", "1.1.1.3"])) self.send(self.pg0, p_gs3) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # Two source and group specific queries in quick succession, the # first does not have VPPs source the second does. then vice-versa # self.send(self.pg0, [p_gs2, p_gs1]) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) self.send(self.pg0, [p_gs1, p_gs2]) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")]) # # remove state, expect the report for the removal # self.remove_group(h1) dump = self.vapi.igmp_dump() self.assertFalse(dump) # # A group with multiple sources # h2 = self.add_group(self.pg0, IgmpSG("239.1.1.1", ["1.1.1.1", "1.1.1.2", "1.1.1.3"])) # search for the corresponding state created in VPP dump = self.vapi.igmp_dump(self.pg0.sw_if_index) self.assertEqual(len(dump), 3) for s in h2.sg.saddrs: self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", s)) # # Send a general query (to the all router's address) # expect VPP to respond with a membership report will all sources # self.send(self.pg0, p_g) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h2.sg, "Mode Is Include")]) # # Group and source specific query; some present some not # p_gs = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst='239.1.1.1', tos=0xc0, options=[IPOption(copy_flag=1, optclass="control", option="router_alert")]) / IGMPv3(type="Membership Query", mrcode=100) / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1", "1.1.1.2", "1.1.1.4"])) self.send(self.pg0, p_gs) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord( IgmpSG('239.1.1.1', ["1.1.1.1", "1.1.1.2"]), "Mode Is Include")]) # # add loads more groups # h3 = self.add_group(self.pg0, IgmpSG("239.1.1.2", ["2.1.1.1", "2.1.1.2", "2.1.1.3"])) h4 = self.add_group(self.pg0, IgmpSG("239.1.1.3", ["3.1.1.1", "3.1.1.2", "3.1.1.3"])) h5 = self.add_group(self.pg0, IgmpSG("239.1.1.4", ["4.1.1.1", "4.1.1.2", "4.1.1.3"])) h6 = self.add_group(self.pg0, IgmpSG("239.1.1.5", ["5.1.1.1", "5.1.1.2", "5.1.1.3"])) h7 = self.add_group(self.pg0, IgmpSG("239.1.1.6", ["6.1.1.1", "6.1.1.2", "6.1.1.3", "6.1.1.4", "6.1.1.5", "6.1.1.6", "6.1.1.7", "6.1.1.8", "6.1.1.9", "6.1.1.10", "6.1.1.11", "6.1.1.12", "6.1.1.13", "6.1.1.14", "6.1.1.15", "6.1.1.16"])) # # general query. # the order the groups come in is not important, so what is # checked for is what VPP is sending today. # self.send(self.pg0, p_g) capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(h3.sg, "Mode Is Include"), IgmpRecord(h2.sg, "Mode Is Include"), IgmpRecord(h6.sg, "Mode Is Include"), IgmpRecord(h4.sg, "Mode Is Include"), IgmpRecord(h5.sg, "Mode Is Include"), IgmpRecord(h7.sg, "Mode Is Include")]) # # modify a group to add and remove some sources # h7.sg = IgmpSG("239.1.1.6", ["6.1.1.1", "6.1.1.2", "6.1.1.5", "6.1.1.6", "6.1.1.7", "6.1.1.8", "6.1.1.9", "6.1.1.10", "6.1.1.11", "6.1.1.12", "6.1.1.13", "6.1.1.14", "6.1.1.15", "6.1.1.16", "6.1.1.17", "6.1.1.18"]) self.pg_enable_capture(self.pg_interfaces) self.pg_start() h7.add_vpp_config() capture = self.pg0.get_capture(1, timeout=10) self.verify_report(capture[0], [IgmpRecord(IgmpSG("239.1.1.6", ["6.1.1.17", "6.1.1.18"]), "Allow New Sources"), IgmpRecord(IgmpSG("239.1.1.6", ["6.1.1.3", "6.1.1.4"]), "Block Old Sources")]) # # add an additional groups with many sources so that each group # consumes the link MTU. We should therefore see multiple state # state reports when queried. # self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [560, 0, 0, 0]) src_list = [] for i in range(128): src_list.append("10.1.1.%d" % i) h8 = self.add_group(self.pg0, IgmpSG("238.1.1.1", src_list)) h9 = self.add_group(self.pg0, IgmpSG("238.1.1.2", src_list)) self.send(self.pg0, p_g) capture = self.pg0.get_capture(4, timeout=10) self.verify_report(capture[0], [IgmpRecord(h3.sg, "Mode Is Include"), IgmpRecord(h2.sg, "Mode Is Include"), IgmpRecord(h6.sg, "Mode Is Include"), IgmpRecord(h4.sg, "Mode Is Include"), IgmpRecord(h5.sg, "Mode Is Include")]) self.verify_report(capture[1], [IgmpRecord(h8.sg, "Mode Is Include")]) self.verify_report(capture[2], [IgmpRecord(h7.sg, "Mode Is Include")]) self.verify_report(capture[3], [IgmpRecord(h9.sg, "Mode Is Include")]) # # drop the MTU further (so a 128 sized group won't fit) # self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [512, 0, 0, 0]) self.pg_enable_capture(self.pg_interfaces) self.pg_start() h10 = VppHostState(self, IGMP_FILTER.INCLUDE, self.pg0.sw_if_index, IgmpSG("238.1.1.3", src_list)) h10.add_vpp_config() capture = self.pg0.get_capture(2, timeout=10) # wait for a little bit self.sleep(1) # # remove state, expect the report for the removal # the dump should be empty # self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [600, 0, 0, 0]) self.remove_group(h8) self.remove_group(h9) self.remove_group(h2) self.remove_group(h3) self.remove_group(h4) self.remove_group(h5) self.remove_group(h6) self.remove_group(h7) self.remove_group(h10) self.logger.info(self.vapi.cli("sh igmp config")) self.assertFalse(self.vapi.igmp_dump()) # # TODO # ADD STATE ON MORE INTERFACES # self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST)