5 from scapy.layers.l2 import Ether
6 from scapy.packet import Raw
7 from scapy.layers.inet import IP, IPOption
8 from scapy.contrib.igmpv3 import IGMPv3, IGMPv3gr, IGMPv3mq, IGMPv3mr
10 from framework import tag_fixme_vpp_workers
11 from framework import VppTestCase, VppTestRunner
12 from vpp_igmp import (
21 from vpp_ip_route import find_mroute, VppIpTable
29 @tag_fixme_vpp_workers
30 class TestIgmp(VppTestCase):
35 super(TestIgmp, cls).setUpClass()
38 def tearDownClass(cls):
39 super(TestIgmp, cls).tearDownClass()
42 super(TestIgmp, self).setUp()
44 self.create_pg_interfaces(range(4))
49 self.ip_table = VppIpTable(self, 1)
50 self.ip_table.add_vpp_config()
52 for pg in self.pg_interfaces[2:]:
54 for pg in self.pg_interfaces:
60 for pg in self.pg_interfaces:
61 self.vapi.igmp_clear_interface(pg.sw_if_index)
65 super(TestIgmp, self).tearDown()
67 def send(self, ti, pkts):
69 self.pg_enable_capture(self.pg_interfaces)
72 def test_igmp_flush(self):
73 """IGMP Link Up/down and Flush"""
76 # FIX THIS. Link down.
79 def test_igmp_enable(self):
80 """IGMP enable/disable on an interface
82 check for the addition/removal of the IGMP mroutes"""
84 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST)
85 self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 1, IGMP_MODE.HOST)
87 self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32))
88 self.assertTrue(find_mroute(self, "224.0.0.22", "0.0.0.0", 32))
90 self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 1, IGMP_MODE.HOST)
91 self.vapi.igmp_enable_disable(self.pg3.sw_if_index, 1, IGMP_MODE.HOST)
93 self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32, table_id=1))
94 self.assertTrue(find_mroute(self, "224.0.0.22", "0.0.0.0", 32, table_id=1))
95 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST)
96 self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 0, IGMP_MODE.HOST)
97 self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 0, IGMP_MODE.HOST)
98 self.vapi.igmp_enable_disable(self.pg3.sw_if_index, 0, IGMP_MODE.HOST)
100 self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32))
101 self.assertFalse(find_mroute(self, "224.0.0.22", "0.0.0.0", 32))
102 self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32, table_id=1))
103 self.assertFalse(find_mroute(self, "224.0.0.22", "0.0.0.0", 32, table_id=1))
105 def verify_general_query(self, p):
107 self.assertEqual(len(ip.options), 1)
108 self.assertEqual(ip.options[0].option, 20)
109 self.assertEqual(ip.dst, "224.0.0.1")
110 self.assertEqual(ip.proto, 2)
112 self.assertEqual(igmp.type, 0x11)
113 self.assertEqual(igmp.gaddr, "0.0.0.0")
115 def verify_group_query(self, p, grp, srcs):
117 self.assertEqual(ip.dst, grp)
118 self.assertEqual(ip.proto, 2)
119 self.assertEqual(len(ip.options), 1)
120 self.assertEqual(ip.options[0].option, 20)
121 self.assertEqual(ip.proto, 2)
123 self.assertEqual(igmp.type, 0x11)
124 self.assertEqual(igmp.gaddr, grp)
126 def verify_report(self, rx, records):
128 self.assertEqual(rx[IP].dst, "224.0.0.22")
129 self.assertEqual(len(ip.options), 1)
130 self.assertEqual(ip.options[0].option, 20)
131 self.assertEqual(ip.proto, 2)
133 IGMPv3.igmpv3types[rx[IGMPv3].type], "Version 3 Membership Report"
135 self.assertEqual(rx[IGMPv3mr].numgrp, len(records))
137 received = rx[IGMPv3mr].records
139 for ii in range(len(records)):
142 self.assertEqual(IGMPv3gr.igmpv3grtypes[gr.rtype], r.type)
143 self.assertEqual(gr.numsrc, len(r.sg.saddrs))
144 self.assertEqual(gr.maddr, r.sg.gaddr)
145 self.assertEqual(len(gr.srcaddrs), len(r.sg.saddrs))
147 self.assertEqual(sorted(gr.srcaddrs), sorted(r.sg.saddrs))
149 def add_group(self, itf, sg, n_pkts=2):
150 self.pg_enable_capture(self.pg_interfaces)
153 hs = VppHostState(self, IGMP_FILTER.INCLUDE, itf.sw_if_index, sg)
156 capture = itf.get_capture(n_pkts, timeout=10)
158 # reports are transmitted twice due to default rebostness value=2
159 self.verify_report(capture[0], [IgmpRecord(sg, "Allow New Sources")]),
160 self.verify_report(capture[1], [IgmpRecord(sg, "Allow New Sources")]),
164 def remove_group(self, hs):
165 self.pg_enable_capture(self.pg_interfaces)
167 hs.remove_vpp_config()
169 capture = self.pg0.get_capture(1, timeout=10)
171 self.verify_report(capture[0], [IgmpRecord(hs.sg, "Block Old Sources")])
173 def test_igmp_host(self):
174 """IGMP Host functions"""
177 # Enable interface for host functions
179 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST)
182 # Add one S,G of state and expect a state-change event report
183 # indicating the addition of the S,G
185 h1 = self.add_group(self.pg0, IgmpSG("239.1.1.1", ["1.1.1.1"]))
187 # search for the corresponding state created in VPP
188 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
189 self.assertEqual(len(dump), 1)
190 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "1.1.1.1"))
193 # Send a general query (to the all router's address)
194 # expect VPP to respond with a membership report.
195 # Pad the query with 0 - some devices in the big wild
196 # internet are prone to this.
199 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
200 / IP(src=self.pg0.remote_ip4, dst="224.0.0.1", tos=0xC0)
201 / IGMPv3(type="Membership Query", mrcode=100)
202 / IGMPv3mq(gaddr="0.0.0.0")
206 self.send(self.pg0, p_g)
208 capture = self.pg0.get_capture(1, timeout=10)
209 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
212 # Group specific query
215 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
217 src=self.pg0.remote_ip4,
221 IPOption(copy_flag=1, optclass="control", option="router_alert")
224 / IGMPv3(type="Membership Query", mrcode=100)
225 / IGMPv3mq(gaddr="239.1.1.1")
228 self.send(self.pg0, p_gs)
230 capture = self.pg0.get_capture(1, timeout=10)
231 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
234 # A group and source specific query, with the source matching
238 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
240 src=self.pg0.remote_ip4,
244 IPOption(copy_flag=1, optclass="control", option="router_alert")
247 / IGMPv3(type="Membership Query", mrcode=100)
248 / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1"])
251 self.send(self.pg0, p_gs1)
253 capture = self.pg0.get_capture(1, timeout=10)
254 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
257 # A group and source specific query that reports more sources
258 # than the packet actually has.
261 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
263 src=self.pg0.remote_ip4,
267 IPOption(copy_flag=1, optclass="control", option="router_alert")
270 / IGMPv3(type="Membership Query", mrcode=100)
271 / IGMPv3mq(gaddr="239.1.1.1", numsrc=4, srcaddrs=["1.1.1.1"])
274 self.send_and_assert_no_replies(self.pg0, p_gs2, timeout=10)
277 # A group and source specific query, with the source NOT matching
278 # the source VPP has. There should be no response.
281 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
283 src=self.pg0.remote_ip4,
287 IPOption(copy_flag=1, optclass="control", option="router_alert")
290 / IGMPv3(type="Membership Query", mrcode=100)
291 / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.2"])
294 self.send_and_assert_no_replies(self.pg0, p_gs2, timeout=10)
297 # A group and source specific query, with the multiple sources
298 # one of which matches the source VPP has.
299 # The report should contain only the source VPP has.
302 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
304 src=self.pg0.remote_ip4,
308 IPOption(copy_flag=1, optclass="control", option="router_alert")
311 / IGMPv3(type="Membership Query", mrcode=100)
312 / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1", "1.1.1.2", "1.1.1.3"])
315 self.send(self.pg0, p_gs3)
317 capture = self.pg0.get_capture(1, timeout=10)
318 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
321 # Two source and group specific queries in quick succession, the
322 # first does not have VPPs source the second does. then vice-versa
324 self.send(self.pg0, [p_gs2, p_gs1])
325 capture = self.pg0.get_capture(1, timeout=10)
326 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
328 self.send(self.pg0, [p_gs1, p_gs2])
329 capture = self.pg0.get_capture(1, timeout=10)
330 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
333 # remove state, expect the report for the removal
335 self.remove_group(h1)
337 dump = self.vapi.igmp_dump()
338 self.assertFalse(dump)
341 # A group with multiple sources
344 self.pg0, IgmpSG("239.1.1.1", ["1.1.1.1", "1.1.1.2", "1.1.1.3"])
347 # search for the corresponding state created in VPP
348 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
349 self.assertEqual(len(dump), 3)
350 for s in h2.sg.saddrs:
351 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", s))
353 # Send a general query (to the all router's address)
354 # expect VPP to respond with a membership report will all sources
356 self.send(self.pg0, p_g)
358 capture = self.pg0.get_capture(1, timeout=10)
359 self.verify_report(capture[0], [IgmpRecord(h2.sg, "Mode Is Include")])
362 # Group and source specific query; some present some not
365 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
367 src=self.pg0.remote_ip4,
371 IPOption(copy_flag=1, optclass="control", option="router_alert")
374 / IGMPv3(type="Membership Query", mrcode=100)
375 / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1", "1.1.1.2", "1.1.1.4"])
378 self.send(self.pg0, p_gs)
380 capture = self.pg0.get_capture(1, timeout=10)
385 IgmpSG("239.1.1.1", ["1.1.1.1", "1.1.1.2"]), "Mode Is Include"
391 # add loads more groups
394 self.pg0, IgmpSG("239.1.1.2", ["2.1.1.1", "2.1.1.2", "2.1.1.3"])
397 self.pg0, IgmpSG("239.1.1.3", ["3.1.1.1", "3.1.1.2", "3.1.1.3"])
400 self.pg0, IgmpSG("239.1.1.4", ["4.1.1.1", "4.1.1.2", "4.1.1.3"])
403 self.pg0, IgmpSG("239.1.1.5", ["5.1.1.1", "5.1.1.2", "5.1.1.3"])
432 # the order the groups come in is not important, so what is
433 # checked for is what VPP is sending today.
435 self.send(self.pg0, p_g)
437 capture = self.pg0.get_capture(1, timeout=10)
442 IgmpRecord(h3.sg, "Mode Is Include"),
443 IgmpRecord(h2.sg, "Mode Is Include"),
444 IgmpRecord(h6.sg, "Mode Is Include"),
445 IgmpRecord(h4.sg, "Mode Is Include"),
446 IgmpRecord(h5.sg, "Mode Is Include"),
447 IgmpRecord(h7.sg, "Mode Is Include"),
452 # modify a group to add and remove some sources
476 self.pg_enable_capture(self.pg_interfaces)
480 capture = self.pg0.get_capture(1, timeout=10)
485 IgmpSG("239.1.1.6", ["6.1.1.17", "6.1.1.18"]), "Allow New Sources"
488 IgmpSG("239.1.1.6", ["6.1.1.3", "6.1.1.4"]), "Block Old Sources"
494 # add an additional groups with many sources so that each group
495 # consumes the link MTU. We should therefore see multiple state
496 # state reports when queried.
498 self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [560, 0, 0, 0])
502 src_list.append("10.1.1.%d" % i)
504 h8 = self.add_group(self.pg0, IgmpSG("238.1.1.1", src_list))
505 h9 = self.add_group(self.pg0, IgmpSG("238.1.1.2", src_list))
507 self.send(self.pg0, p_g)
509 capture = self.pg0.get_capture(4, timeout=10)
514 IgmpRecord(h3.sg, "Mode Is Include"),
515 IgmpRecord(h2.sg, "Mode Is Include"),
516 IgmpRecord(h6.sg, "Mode Is Include"),
517 IgmpRecord(h4.sg, "Mode Is Include"),
518 IgmpRecord(h5.sg, "Mode Is Include"),
521 self.verify_report(capture[1], [IgmpRecord(h8.sg, "Mode Is Include")])
522 self.verify_report(capture[2], [IgmpRecord(h7.sg, "Mode Is Include")])
523 self.verify_report(capture[3], [IgmpRecord(h9.sg, "Mode Is Include")])
526 # drop the MTU further (so a 128 sized group won't fit)
528 self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [512, 0, 0, 0])
530 self.pg_enable_capture(self.pg_interfaces)
536 self.pg0.sw_if_index,
537 IgmpSG("238.1.1.3", src_list),
541 capture = self.pg0.get_capture(2, timeout=10)
542 # wait for a little bit
546 # remove state, expect the report for the removal
547 # the dump should be empty
549 self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [600, 0, 0, 0])
550 self.remove_group(h8)
551 self.remove_group(h9)
552 self.remove_group(h2)
553 self.remove_group(h3)
554 self.remove_group(h4)
555 self.remove_group(h5)
556 self.remove_group(h6)
557 self.remove_group(h7)
558 self.remove_group(h10)
560 self.logger.info(self.vapi.cli("sh igmp config"))
561 self.assertFalse(self.vapi.igmp_dump())
565 # ADD STATE ON MORE INTERFACES
568 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST)
570 def test_igmp_router(self):
571 """IGMP Router Functions"""
574 # Drop reports when not enabled
577 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
579 src=self.pg0.remote_ip4,
584 IPOption(copy_flag=1, optclass="control", option="router_alert")
587 / IGMPv3(type="Version 3 Membership Report")
590 rtype="Allow New Sources",
592 srcaddrs=["10.1.1.1", "10.1.1.2"],
596 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
598 src=self.pg0.remote_ip4,
602 IPOption(copy_flag=1, optclass="control", option="router_alert")
605 / IGMPv3(type="Version 3 Membership Report")
608 rtype="Block Old Sources",
610 srcaddrs=["10.1.1.1", "10.1.1.2"],
614 self.send(self.pg0, p_j)
615 self.assertFalse(self.vapi.igmp_dump())
618 # drop the default timer values so these tests execute in a
619 # reasonable time frame
621 self.vapi.cli("test igmp timers query 1 src 3 leave 1")
624 # enable router functions on the interface
626 self.pg_enable_capture(self.pg_interfaces)
628 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.ROUTER)
629 self.vapi.want_igmp_events(1)
632 # wait for router to send general query
635 capture = self.pg0.get_capture(1, timeout=2)
636 self.verify_general_query(capture[0])
637 self.pg_enable_capture(self.pg_interfaces)
641 # re-send the report. VPP should now hold state for the new group
642 # VPP sends a notification that a new group has been joined
644 self.send(self.pg0, p_j)
647 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)
650 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)
652 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
653 self.assertEqual(len(dump), 2)
654 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.1"))
655 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.2"))
658 # wait for the per-source timer to expire
659 # the state should be reaped
660 # VPP sends a notification that the group has been left
663 wait_for_igmp_event(self, 4, self.pg0, "239.1.1.1", "10.1.1.1", 0)
666 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)
668 self.assertFalse(self.vapi.igmp_dump())
671 # resend the join. wait for two queries and then send a current-state
672 # record to include all sources. this should reset the expiry time
673 # on the sources and thus they will still be present in 2 seconds time.
674 # If the source timer was not refreshed, then the state would have
675 # expired in 3 seconds.
677 self.send(self.pg0, p_j)
679 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)
682 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)
684 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
685 self.assertEqual(len(dump), 2)
687 capture = self.pg0.get_capture(2, timeout=3)
688 self.verify_general_query(capture[0])
689 self.verify_general_query(capture[1])
692 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
694 src=self.pg0.remote_ip4,
698 IPOption(copy_flag=1, optclass="control", option="router_alert")
701 / IGMPv3(type="Version 3 Membership Report")
704 rtype="Mode Is Include",
706 srcaddrs=["10.1.1.1", "10.1.1.2"],
710 self.send(self.pg0, p_cs)
713 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
714 self.assertEqual(len(dump), 2)
715 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.1"))
716 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.2"))
719 # wait for the per-source timer to expire
720 # the state should be reaped
723 wait_for_igmp_event(self, 4, self.pg0, "239.1.1.1", "10.1.1.1", 0)
726 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)
728 self.assertFalse(self.vapi.igmp_dump())
731 # resend the join, then a leave. Router sends a group+source
732 # specific query containing both sources
734 self.send(self.pg0, p_j)
737 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)
740 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)
742 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
743 self.assertEqual(len(dump), 2)
745 self.send(self.pg0, p_l)
746 capture = self.pg0.get_capture(1, timeout=3)
747 self.verify_group_query(capture[0], "239.1.1.1", ["10.1.1.1", "10.1.1.2"])
750 # the group specific query drops the timeout to leave (=1) seconds
753 wait_for_igmp_event(self, 2, self.pg0, "239.1.1.1", "10.1.1.1", 0)
756 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)
758 self.assertFalse(self.vapi.igmp_dump())
759 self.assertFalse(self.vapi.igmp_dump())
762 # a TO_EX({}) / IN_EX({}) is treated like a (*,G) join
765 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
767 src=self.pg0.remote_ip4,
772 IPOption(copy_flag=1, optclass="control", option="router_alert")
775 / IGMPv3(type="Version 3 Membership Report")
777 / IGMPv3gr(rtype="Change To Exclude Mode", maddr="239.1.1.2")
780 self.send(self.pg0, p_j)
783 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.2", "0.0.0.0", 1)
787 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
789 src=self.pg0.remote_ip4,
794 IPOption(copy_flag=1, optclass="control", option="router_alert")
797 / IGMPv3(type="Version 3 Membership Report")
799 / IGMPv3gr(rtype="Mode Is Exclude", maddr="239.1.1.3")
802 self.send(self.pg0, p_j)
805 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.3", "0.0.0.0", 1)
809 # A 'allow sources' for {} should be ignored as it should
813 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
815 src=self.pg0.remote_ip4,
820 IPOption(copy_flag=1, optclass="control", option="router_alert")
823 / IGMPv3(type="Version 3 Membership Report")
825 / IGMPv3gr(rtype="Allow New Sources", maddr="239.1.1.4")
828 self.send(self.pg0, p_j)
830 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
831 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.2", "0.0.0.0"))
832 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.3", "0.0.0.0"))
833 self.assertFalse(find_igmp_state(dump, self.pg0, "239.1.1.4", "0.0.0.0"))
836 # a TO_IN({}) and IS_IN({}) are treated like a (*,G) leave
838 self.vapi.cli("set logging class igmp level debug")
840 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
842 src=self.pg0.remote_ip4,
847 IPOption(copy_flag=1, optclass="control", option="router_alert")
850 / IGMPv3(type="Version 3 Membership Report")
852 / IGMPv3gr(rtype="Change To Include Mode", maddr="239.1.1.2")
855 self.send(self.pg0, p_l)
857 wait_for_igmp_event(self, 2, self.pg0, "239.1.1.2", "0.0.0.0", 0)
861 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
863 src=self.pg0.remote_ip4,
868 IPOption(copy_flag=1, optclass="control", option="router_alert")
871 / IGMPv3(type="Version 3 Membership Report")
873 / IGMPv3gr(rtype="Mode Is Include", maddr="239.1.1.3")
876 self.send(self.pg0, p_l)
879 wait_for_igmp_event(self, 2, self.pg0, "239.1.1.3", "0.0.0.0", 0)
881 self.assertFalse(self.vapi.igmp_dump(self.pg0.sw_if_index))
884 # disable router config
886 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.ROUTER)
888 def _create_igmpv3_pck(self, itf, rtype, maddr, srcaddrs):
890 Ether(dst=itf.local_mac, src=itf.remote_mac)
897 IPOption(copy_flag=1, optclass="control", option="router_alert")
900 / IGMPv3(type="Version 3 Membership Report")
902 / IGMPv3gr(rtype=rtype, maddr=maddr, srcaddrs=srcaddrs)
906 def test_igmp_proxy_device(self):
907 """IGMP proxy device"""
908 self.pg2.admin_down()
909 self.pg2.unconfig_ip4()
910 self.pg2.set_table_ip4(0)
911 self.pg2.config_ip4()
914 self.vapi.cli("test igmp timers query 10 src 3 leave 1")
917 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST)
918 self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 1, IGMP_MODE.ROUTER)
919 self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 1, IGMP_MODE.ROUTER)
921 # create IGMP proxy device
922 self.vapi.igmp_proxy_device_add_del(0, self.pg0.sw_if_index, 1)
923 self.vapi.igmp_proxy_device_add_del_interface(0, self.pg1.sw_if_index, 1)
924 self.vapi.igmp_proxy_device_add_del_interface(0, self.pg2.sw_if_index, 1)
926 # send join on pg1. join should be proxied by pg0
927 p_j = self._create_igmpv3_pck(
928 self.pg1, "Allow New Sources", "239.1.1.1", ["10.1.1.1", "10.1.1.2"]
930 self.send(self.pg1, p_j)
932 capture = self.pg0.get_capture(1, timeout=1)
937 IgmpSG("239.1.1.1", ["10.1.1.1", "10.1.1.2"]), "Allow New Sources"
941 self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
943 # send join on pg2. join should be proxied by pg0.
944 # the group should contain only 10.1.1.3 as
945 # 10.1.1.1 was already reported
946 p_j = self._create_igmpv3_pck(
947 self.pg2, "Allow New Sources", "239.1.1.1", ["10.1.1.1", "10.1.1.3"]
949 self.send(self.pg2, p_j)
951 capture = self.pg0.get_capture(1, timeout=1)
954 [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.3"]), "Allow New Sources")],
956 self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
958 # send leave on pg2. leave for 10.1.1.3 should be proxyed
959 # as pg2 was the only interface interested in 10.1.1.3
960 p_l = self._create_igmpv3_pck(
961 self.pg2, "Block Old Sources", "239.1.1.1", ["10.1.1.3"]
963 self.send(self.pg2, p_l)
965 capture = self.pg0.get_capture(1, timeout=2)
968 [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.3"]), "Block Old Sources")],
970 self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
972 # disable igmp on pg1 (also removes interface from proxy device)
973 # proxy leave for 10.1.1.2. pg2 is still interested in 10.1.1.1
974 self.pg_enable_capture(self.pg_interfaces)
975 self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 0, IGMP_MODE.ROUTER)
977 capture = self.pg0.get_capture(1, timeout=1)
980 [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.2"]), "Block Old Sources")],
982 self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
984 # disable IGMP on pg0 and pg1.
985 # disabling IGMP on pg0 (proxy device upstream interface)
986 # removes this proxy device
987 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST)
988 self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 0, IGMP_MODE.ROUTER)
989 self.assertFalse(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
992 if __name__ == "__main__":
993 unittest.main(testRunner=VppTestRunner)