tests: move plugin tests to src/plugins/*/test
[vpp.git] / test / test_igmp.py
diff --git a/test/test_igmp.py b/test/test_igmp.py
deleted file mode 100644 (file)
index f1c49ac..0000000
+++ /dev/null
@@ -1,834 +0,0 @@
-#!/usr/bin/env python
-
-import unittest
-
-from scapy.layers.l2 import Ether, Raw
-from scapy.layers.inet import IP, IPOption
-from scapy.contrib.igmpv3 import IGMPv3, IGMPv3gr, IGMPv3mq, IGMPv3mr
-
-from framework import VppTestCase, VppTestRunner, running_extended_tests
-from vpp_igmp import find_igmp_state, IGMP_FILTER, IgmpRecord, IGMP_MODE, \
-    IgmpSG, VppHostState, wait_for_igmp_event
-from vpp_ip_route import find_mroute, VppIpTable
-
-
-class IgmpMode:
-    HOST = 1
-    ROUTER = 0
-
-
-class TestIgmp(VppTestCase):
-    """ IGMP Test Case """
-
-    @classmethod
-    def setUpClass(cls):
-        super(TestIgmp, cls).setUpClass()
-
-    @classmethod
-    def tearDownClass(cls):
-        super(TestIgmp, cls).tearDownClass()
-
-    def setUp(self):
-        super(TestIgmp, self).setUp()
-
-        self.create_pg_interfaces(range(4))
-        self.sg_list = []
-        self.config_list = []
-
-        self.ip_addr = []
-        self.ip_table = VppIpTable(self, 1)
-        self.ip_table.add_vpp_config()
-
-        for pg in self.pg_interfaces[2:]:
-            pg.set_table_ip4(1)
-        for pg in self.pg_interfaces:
-            pg.admin_up()
-            pg.config_ip4()
-            pg.resolve_arp()
-
-    def tearDown(self):
-        for pg in self.pg_interfaces:
-            self.vapi.igmp_clear_interface(pg.sw_if_index)
-            pg.unconfig_ip4()
-            pg.set_table_ip4(0)
-            pg.admin_down()
-        super(TestIgmp, self).tearDown()
-
-    def send(self, ti, pkts):
-        ti.add_stream(pkts)
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-    def test_igmp_flush(self):
-        """ IGMP Link Up/down and Flush """
-
-        #
-        # FIX THIS. Link down.
-        #
-
-    def test_igmp_enable(self):
-        """ IGMP enable/disable on an interface
-
-        check for the addition/removal of the IGMP mroutes """
-
-        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.HOST)
-
-        self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32))
-        self.assertTrue(find_mroute(self, "224.0.0.22", "0.0.0.0", 32))
-
-        self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 1, IGMP_MODE.HOST)
-        self.vapi.igmp_enable_disable(self.pg3.sw_if_index, 1, IGMP_MODE.HOST)
-
-        self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32,
-                                    table_id=1))
-        self.assertTrue(find_mroute(self, "224.0.0.22", "0.0.0.0", 32,
-                                    table_id=1))
-        self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST)
-        self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 0, IGMP_MODE.HOST)
-        self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 0, IGMP_MODE.HOST)
-        self.vapi.igmp_enable_disable(self.pg3.sw_if_index, 0, IGMP_MODE.HOST)
-
-        self.assertFalse(find_mroute(self, "224.0.0.1", "0.0.0.0", 32))
-        self.assertFalse(find_mroute(self, "224.0.0.22", "0.0.0.0", 32))
-        self.assertFalse(find_mroute(self, "224.0.0.1", "0.0.0.0", 32,
-                                     table_id=1))
-        self.assertFalse(find_mroute(self, "224.0.0.22", "0.0.0.0", 32,
-                                     table_id=1))
-
-    def verify_general_query(self, p):
-        ip = p[IP]
-        self.assertEqual(len(ip.options), 1)
-        self.assertEqual(ip.options[0].option, 20)
-        self.assertEqual(ip.dst, "224.0.0.1")
-        self.assertEqual(ip.proto, 2)
-        igmp = p[IGMPv3]
-        self.assertEqual(igmp.type, 0x11)
-        self.assertEqual(igmp.gaddr, "0.0.0.0")
-
-    def verify_group_query(self, p, grp, srcs):
-        ip = p[IP]
-        self.assertEqual(ip.dst, grp)
-        self.assertEqual(ip.proto, 2)
-        self.assertEqual(len(ip.options), 1)
-        self.assertEqual(ip.options[0].option, 20)
-        self.assertEqual(ip.proto, 2)
-        igmp = p[IGMPv3]
-        self.assertEqual(igmp.type, 0x11)
-        self.assertEqual(igmp.gaddr, grp)
-
-    def verify_report(self, rx, records):
-        ip = rx[IP]
-        self.assertEqual(rx[IP].dst, "224.0.0.22")
-        self.assertEqual(len(ip.options), 1)
-        self.assertEqual(ip.options[0].option, 20)
-        self.assertEqual(ip.proto, 2)
-        self.assertEqual(IGMPv3.igmpv3types[rx[IGMPv3].type],
-                         "Version 3 Membership Report")
-        self.assertEqual(rx[IGMPv3mr].numgrp, len(records))
-
-        received = rx[IGMPv3mr].records
-
-        for ii in range(len(records)):
-            gr = received[ii]
-            r = records[ii]
-            self.assertEqual(IGMPv3gr.igmpv3grtypes[gr.rtype], r.type)
-            self.assertEqual(gr.numsrc, len(r.sg.saddrs))
-            self.assertEqual(gr.maddr, r.sg.gaddr)
-            self.assertEqual(len(gr.srcaddrs), len(r.sg.saddrs))
-
-            self.assertEqual(sorted(gr.srcaddrs),
-                             sorted(r.sg.saddrs))
-
-    def add_group(self, itf, sg, n_pkts=2):
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-
-        hs = VppHostState(self,
-                          IGMP_FILTER.INCLUDE,
-                          itf.sw_if_index,
-                          sg)
-        hs.add_vpp_config()
-
-        capture = itf.get_capture(n_pkts, timeout=10)
-
-        # reports are transmitted twice due to default rebostness value=2
-        self.verify_report(capture[0],
-                           [IgmpRecord(sg, "Allow New Sources")]),
-        self.verify_report(capture[1],
-                           [IgmpRecord(sg, "Allow New Sources")]),
-
-        return hs
-
-    def remove_group(self, hs):
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-        hs.remove_vpp_config()
-
-        capture = self.pg0.get_capture(1, timeout=10)
-
-        self.verify_report(capture[0],
-                           [IgmpRecord(hs.sg, "Block Old Sources")])
-
-    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('\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)
-
-    def test_igmp_router(self):
-        """ IGMP Router Functions """
-
-        #
-        # Drop reports when not enabled
-        #
-        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.1", srcaddrs=["10.1.1.1", "10.1.1.2"]))
-        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,
-                  options=[IPOption(copy_flag=1, optclass="control",
-                                    option="router_alert")]) /
-               IGMPv3(type="Version 3 Membership Report") /
-               IGMPv3mr(numgrp=1) /
-               IGMPv3gr(rtype="Block Old Sources",
-                        maddr="239.1.1.1", srcaddrs=["10.1.1.1", "10.1.1.2"]))
-
-        self.send(self.pg0, p_j)
-        self.assertFalse(self.vapi.igmp_dump())
-
-        #
-        # drop the default timer values so these tests execute in a
-        # reasonable time frame
-        #
-        self.vapi.cli("test igmp timers query 1 src 3 leave 1")
-
-        #
-        # enable router functions on the interface
-        #
-        self.pg_enable_capture(self.pg_interfaces)
-        self.pg_start()
-        self.vapi.igmp_enable_disable(self.pg0.sw_if_index,
-                                      1,
-                                      IGMP_MODE.ROUTER)
-        self.vapi.want_igmp_events(1)
-
-        #
-        # wait for router to send general query
-        #
-        for ii in range(3):
-            capture = self.pg0.get_capture(1, timeout=2)
-            self.verify_general_query(capture[0])
-            self.pg_enable_capture(self.pg_interfaces)
-            self.pg_start()
-
-        #
-        # re-send the report. VPP should now hold state for the new group
-        # VPP sends a notification that a new group has been joined
-        #
-        self.send(self.pg0, p_j)
-
-        self.assertTrue(wait_for_igmp_event(self, 1, self.pg0,
-                                            "239.1.1.1", "10.1.1.1", 1))
-        self.assertTrue(wait_for_igmp_event(self, 1, self.pg0,
-                                            "239.1.1.1", "10.1.1.2", 1))
-        dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
-        self.assertEqual(len(dump), 2)
-        self.assertTrue(find_igmp_state(dump, self.pg0,
-                                        "239.1.1.1", "10.1.1.1"))
-        self.assertTrue(find_igmp_state(dump, self.pg0,
-                                        "239.1.1.1", "10.1.1.2"))
-
-        #
-        # wait for the per-source timer to expire
-        # the state should be reaped
-        # VPP sends a notification that the group has been left
-        #
-        self.assertTrue(wait_for_igmp_event(self, 4, self.pg0,
-                                            "239.1.1.1", "10.1.1.1", 0))
-        self.assertTrue(wait_for_igmp_event(self, 1, self.pg0,
-                                            "239.1.1.1", "10.1.1.2", 0))
-        self.assertFalse(self.vapi.igmp_dump())
-
-        #
-        # resend the join. wait for two queries and then send a current-state
-        # record to include all sources. this should reset the expiry time
-        # on the sources and thus they will still be present in 2 seconds time.
-        # If the source timer was not refreshed, then the state would have
-        # expired in 3 seconds.
-        #
-        self.send(self.pg0, p_j)
-        self.assertTrue(wait_for_igmp_event(self, 1, self.pg0,
-                                            "239.1.1.1", "10.1.1.1", 1))
-        self.assertTrue(wait_for_igmp_event(self, 1, self.pg0,
-                                            "239.1.1.1", "10.1.1.2", 1))
-        dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
-        self.assertEqual(len(dump), 2)
-
-        capture = self.pg0.get_capture(2, timeout=3)
-        self.verify_general_query(capture[0])
-        self.verify_general_query(capture[1])
-
-        p_cs = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
-                IP(src=self.pg0.remote_ip4, dst="224.0.0.22", tos=0xc0,
-                   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.1", srcaddrs=["10.1.1.1", "10.1.1.2"]))
-
-        self.send(self.pg0, p_cs)
-
-        self.sleep(2)
-        dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
-        self.assertEqual(len(dump), 2)
-        self.assertTrue(find_igmp_state(dump, self.pg0,
-                                        "239.1.1.1", "10.1.1.1"))
-        self.assertTrue(find_igmp_state(dump, self.pg0,
-                                        "239.1.1.1", "10.1.1.2"))
-
-        #
-        # wait for the per-source timer to expire
-        # the state should be reaped
-        #
-        self.assertTrue(wait_for_igmp_event(self, 4, self.pg0,
-                                            "239.1.1.1", "10.1.1.1", 0))
-        self.assertTrue(wait_for_igmp_event(self, 1, self.pg0,
-                                            "239.1.1.1", "10.1.1.2", 0))
-        self.assertFalse(self.vapi.igmp_dump())
-
-        #
-        # resend the join, then a leave. Router sends a group+source
-        # specific query containing both sources
-        #
-        self.send(self.pg0, p_j)
-
-        self.assertTrue(wait_for_igmp_event(self, 1, self.pg0,
-                                            "239.1.1.1", "10.1.1.1", 1))
-        self.assertTrue(wait_for_igmp_event(self, 1, self.pg0,
-                                            "239.1.1.1", "10.1.1.2", 1))
-        dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
-        self.assertEqual(len(dump), 2)
-
-        self.send(self.pg0, p_l)
-        capture = self.pg0.get_capture(1, timeout=3)
-        self.verify_group_query(capture[0], "239.1.1.1",
-                                ["10.1.1.1", "10.1.1.2"])
-
-        #
-        # the group specific query drops the timeout to leave (=1) seconds
-        #
-        self.assertTrue(wait_for_igmp_event(self, 2, self.pg0,
-                                            "239.1.1.1", "10.1.1.1", 0))
-        self.assertTrue(wait_for_igmp_event(self, 1, self.pg0,
-                                            "239.1.1.1", "10.1.1.2", 0))
-        self.assertFalse(self.vapi.igmp_dump())
-        self.assertFalse(self.vapi.igmp_dump())
-
-        #
-        # 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,
-                  options=[IPOption(copy_flag=1, optclass="control",
-                                    option="router_alert")]) /
-               IGMPv3(type="Version 3 Membership Report") /
-               IGMPv3mr(numgrp=1) /
-               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 sources' 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
-        #
-        self.vapi.igmp_enable_disable(self.pg0.sw_if_index,
-                                      0,
-                                      IGMP_MODE.ROUTER)
-
-    def _create_igmpv3_pck(self, itf, rtype, maddr, srcaddrs):
-        p = (Ether(dst=itf.local_mac, src=itf.remote_mac) /
-             IP(src=itf.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=rtype,
-                      maddr=maddr, srcaddrs=srcaddrs))
-        return p
-
-    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))
-
-
-if __name__ == '__main__':
-    unittest.main(testRunner=VppTestRunner)