blob: e0da2b82079d8aaaf499fcc79252a76bd8035351 [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Jakub Grajciar7b867a82017-12-08 16:28:42 +01002
3import unittest
Jakub Grajciar7b867a82017-12-08 16:28:42 +01004
snaramre5d4b8912019-12-13 23:39:35 +00005from scapy.layers.l2 import Ether
6from scapy.packet import Raw
Paul Vinciguerraa279d9c2019-02-28 09:00:09 -08007from scapy.layers.inet import IP, IPOption
8from scapy.contrib.igmpv3 import IGMPv3, IGMPv3gr, IGMPv3mq, IGMPv3mr
9
Dave Wallace8800f732023-08-31 00:47:44 -040010from framework import VppTestCase
11from asfframework import VppTestRunner, tag_fixme_vpp_workers
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020012from vpp_igmp import (
13 find_igmp_state,
14 IGMP_FILTER,
15 IgmpRecord,
16 IGMP_MODE,
17 IgmpSG,
18 VppHostState,
19 wait_for_igmp_event,
20)
Neale Ranns947ea622018-06-07 23:48:20 -070021from vpp_ip_route import find_mroute, VppIpTable
Dmitry Valter34fa0ce2024-03-11 10:38:46 +000022from config import config
Neale Ranns947ea622018-06-07 23:48:20 -070023
24
25class IgmpMode:
26 HOST = 1
27 ROUTER = 0
Jakub Grajciar7b867a82017-12-08 16:28:42 +010028
29
Andrew Yourtchenko8dc0d482021-01-29 13:17:19 +000030@tag_fixme_vpp_workers
Dmitry Valter34fa0ce2024-03-11 10:38:46 +000031@unittest.skipIf("igmp" in config.excluded_plugins, "Exclude IGMP plugin tests")
Jakub Grajciar7b867a82017-12-08 16:28:42 +010032class TestIgmp(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020033 """IGMP Test Case"""
Jakub Grajciar7b867a82017-12-08 16:28:42 +010034
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070035 @classmethod
36 def setUpClass(cls):
37 super(TestIgmp, cls).setUpClass()
38
39 @classmethod
40 def tearDownClass(cls):
41 super(TestIgmp, cls).tearDownClass()
42
Jakub Grajciar7b867a82017-12-08 16:28:42 +010043 def setUp(self):
44 super(TestIgmp, self).setUp()
45
Neale Ranns947ea622018-06-07 23:48:20 -070046 self.create_pg_interfaces(range(4))
Jakub Grajciar7b867a82017-12-08 16:28:42 +010047 self.sg_list = []
48 self.config_list = []
49
50 self.ip_addr = []
Neale Ranns947ea622018-06-07 23:48:20 -070051 self.ip_table = VppIpTable(self, 1)
52 self.ip_table.add_vpp_config()
53
54 for pg in self.pg_interfaces[2:]:
55 pg.set_table_ip4(1)
Jakub Grajciar7b867a82017-12-08 16:28:42 +010056 for pg in self.pg_interfaces:
57 pg.admin_up()
58 pg.config_ip4()
59 pg.resolve_arp()
60
61 def tearDown(self):
62 for pg in self.pg_interfaces:
63 self.vapi.igmp_clear_interface(pg.sw_if_index)
64 pg.unconfig_ip4()
Neale Ranns947ea622018-06-07 23:48:20 -070065 pg.set_table_ip4(0)
Jakub Grajciar7b867a82017-12-08 16:28:42 +010066 pg.admin_down()
67 super(TestIgmp, self).tearDown()
68
69 def send(self, ti, pkts):
70 ti.add_stream(pkts)
71 self.pg_enable_capture(self.pg_interfaces)
72 self.pg_start()
73
Neale Ranns947ea622018-06-07 23:48:20 -070074 def test_igmp_flush(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020075 """IGMP Link Up/down and Flush"""
Jakub Grajciar7b867a82017-12-08 16:28:42 +010076
77 #
Neale Ranns947ea622018-06-07 23:48:20 -070078 # FIX THIS. Link down.
Jakub Grajciar7b867a82017-12-08 16:28:42 +010079 #
Jakub Grajciar7b867a82017-12-08 16:28:42 +010080
Neale Ranns947ea622018-06-07 23:48:20 -070081 def test_igmp_enable(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020082 """IGMP enable/disable on an interface
Jakub Grajciar7b867a82017-12-08 16:28:42 +010083
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020084 check for the addition/removal of the IGMP mroutes"""
Jakub Grajciar7b867a82017-12-08 16:28:42 +010085
Neale Ranns947ea622018-06-07 23:48:20 -070086 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST)
87 self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 1, IGMP_MODE.HOST)
Jakub Grajciar7b867a82017-12-08 16:28:42 +010088
Neale Ranns947ea622018-06-07 23:48:20 -070089 self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32))
90 self.assertTrue(find_mroute(self, "224.0.0.22", "0.0.0.0", 32))
Jakub Grajciar7b867a82017-12-08 16:28:42 +010091
Neale Ranns947ea622018-06-07 23:48:20 -070092 self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 1, IGMP_MODE.HOST)
93 self.vapi.igmp_enable_disable(self.pg3.sw_if_index, 1, IGMP_MODE.HOST)
Jakub Grajciar7b867a82017-12-08 16:28:42 +010094
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020095 self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32, table_id=1))
96 self.assertTrue(find_mroute(self, "224.0.0.22", "0.0.0.0", 32, table_id=1))
Neale Ranns947ea622018-06-07 23:48:20 -070097 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST)
98 self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 0, IGMP_MODE.HOST)
99 self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 0, IGMP_MODE.HOST)
100 self.vapi.igmp_enable_disable(self.pg3.sw_if_index, 0, IGMP_MODE.HOST)
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100101
Neale Ranns03c254e2020-03-17 14:25:10 +0000102 self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32))
Neale Ranns947ea622018-06-07 23:48:20 -0700103 self.assertFalse(find_mroute(self, "224.0.0.22", "0.0.0.0", 32))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200104 self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32, table_id=1))
105 self.assertFalse(find_mroute(self, "224.0.0.22", "0.0.0.0", 32, table_id=1))
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100106
107 def verify_general_query(self, p):
108 ip = p[IP]
Neale Ranns947ea622018-06-07 23:48:20 -0700109 self.assertEqual(len(ip.options), 1)
110 self.assertEqual(ip.options[0].option, 20)
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100111 self.assertEqual(ip.dst, "224.0.0.1")
112 self.assertEqual(ip.proto, 2)
113 igmp = p[IGMPv3]
114 self.assertEqual(igmp.type, 0x11)
115 self.assertEqual(igmp.gaddr, "0.0.0.0")
116
Neale Ranns947ea622018-06-07 23:48:20 -0700117 def verify_group_query(self, p, grp, srcs):
118 ip = p[IP]
119 self.assertEqual(ip.dst, grp)
120 self.assertEqual(ip.proto, 2)
121 self.assertEqual(len(ip.options), 1)
122 self.assertEqual(ip.options[0].option, 20)
123 self.assertEqual(ip.proto, 2)
124 igmp = p[IGMPv3]
125 self.assertEqual(igmp.type, 0x11)
126 self.assertEqual(igmp.gaddr, grp)
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100127
Neale Ranns947ea622018-06-07 23:48:20 -0700128 def verify_report(self, rx, records):
129 ip = rx[IP]
130 self.assertEqual(rx[IP].dst, "224.0.0.22")
131 self.assertEqual(len(ip.options), 1)
132 self.assertEqual(ip.options[0].option, 20)
133 self.assertEqual(ip.proto, 2)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200134 self.assertEqual(
135 IGMPv3.igmpv3types[rx[IGMPv3].type], "Version 3 Membership Report"
136 )
Neale Ranns947ea622018-06-07 23:48:20 -0700137 self.assertEqual(rx[IGMPv3mr].numgrp, len(records))
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100138
Neale Ranns947ea622018-06-07 23:48:20 -0700139 received = rx[IGMPv3mr].records
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100140
Neale Ranns947ea622018-06-07 23:48:20 -0700141 for ii in range(len(records)):
142 gr = received[ii]
143 r = records[ii]
144 self.assertEqual(IGMPv3gr.igmpv3grtypes[gr.rtype], r.type)
145 self.assertEqual(gr.numsrc, len(r.sg.saddrs))
146 self.assertEqual(gr.maddr, r.sg.gaddr)
147 self.assertEqual(len(gr.srcaddrs), len(r.sg.saddrs))
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100148
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200149 self.assertEqual(sorted(gr.srcaddrs), sorted(r.sg.saddrs))
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100150
Neale Ranns947ea622018-06-07 23:48:20 -0700151 def add_group(self, itf, sg, n_pkts=2):
152 self.pg_enable_capture(self.pg_interfaces)
153 self.pg_start()
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100154
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200155 hs = VppHostState(self, IGMP_FILTER.INCLUDE, itf.sw_if_index, sg)
Neale Ranns947ea622018-06-07 23:48:20 -0700156 hs.add_vpp_config()
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100157
Neale Ranns947ea622018-06-07 23:48:20 -0700158 capture = itf.get_capture(n_pkts, timeout=10)
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100159
Neale Ranns947ea622018-06-07 23:48:20 -0700160 # reports are transmitted twice due to default rebostness value=2
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200161 self.verify_report(capture[0], [IgmpRecord(sg, "Allow New Sources")]),
162 self.verify_report(capture[1], [IgmpRecord(sg, "Allow New Sources")]),
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100163
Neale Ranns947ea622018-06-07 23:48:20 -0700164 return hs
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100165
Neale Ranns947ea622018-06-07 23:48:20 -0700166 def remove_group(self, hs):
167 self.pg_enable_capture(self.pg_interfaces)
168 self.pg_start()
169 hs.remove_vpp_config()
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100170
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100171 capture = self.pg0.get_capture(1, timeout=10)
172
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200173 self.verify_report(capture[0], [IgmpRecord(hs.sg, "Block Old Sources")])
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100174
Neale Ranns947ea622018-06-07 23:48:20 -0700175 def test_igmp_host(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200176 """IGMP Host functions"""
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100177
178 #
Neale Ranns947ea622018-06-07 23:48:20 -0700179 # Enable interface for host functions
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100180 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200181 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST)
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100182
Neale Ranns947ea622018-06-07 23:48:20 -0700183 #
184 # Add one S,G of state and expect a state-change event report
185 # indicating the addition of the S,G
186 #
187 h1 = self.add_group(self.pg0, IgmpSG("239.1.1.1", ["1.1.1.1"]))
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100188
Neale Ranns947ea622018-06-07 23:48:20 -0700189 # search for the corresponding state created in VPP
190 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
191 self.assertEqual(len(dump), 1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200192 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "1.1.1.1"))
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100193
Neale Ranns947ea622018-06-07 23:48:20 -0700194 #
195 # Send a general query (to the all router's address)
Neale Ranns01b0a052019-06-30 09:05:05 +0000196 # expect VPP to respond with a membership report.
197 # Pad the query with 0 - some devices in the big wild
198 # internet are prone to this.
Neale Ranns947ea622018-06-07 23:48:20 -0700199 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200200 p_g = (
201 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
202 / IP(src=self.pg0.remote_ip4, dst="224.0.0.1", tos=0xC0)
203 / IGMPv3(type="Membership Query", mrcode=100)
204 / IGMPv3mq(gaddr="0.0.0.0")
205 / Raw(b"\x00" * 10)
206 )
Neale Ranns947ea622018-06-07 23:48:20 -0700207
208 self.send(self.pg0, p_g)
209
210 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200211 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700212
213 #
214 # Group specific query
215 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200216 p_gs = (
217 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
218 / IP(
219 src=self.pg0.remote_ip4,
220 dst="239.1.1.1",
221 tos=0xC0,
222 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000223 IPOption(
224 copy_flag=1, optclass="control", option="router_alert", length=4
225 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200226 ],
227 )
228 / IGMPv3(type="Membership Query", mrcode=100)
229 / IGMPv3mq(gaddr="239.1.1.1")
230 )
Neale Ranns947ea622018-06-07 23:48:20 -0700231
232 self.send(self.pg0, p_gs)
233
234 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200235 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700236
237 #
238 # A group and source specific query, with the source matching
239 # the source VPP has
240 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200241 p_gs1 = (
242 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
243 / IP(
244 src=self.pg0.remote_ip4,
245 dst="239.1.1.1",
246 tos=0xC0,
247 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000248 IPOption(
249 copy_flag=1, optclass="control", option="router_alert", length=4
250 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200251 ],
252 )
253 / IGMPv3(type="Membership Query", mrcode=100)
254 / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1"])
255 )
Neale Ranns947ea622018-06-07 23:48:20 -0700256
257 self.send(self.pg0, p_gs1)
258
259 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200260 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700261
262 #
Neale Ranns01b0a052019-06-30 09:05:05 +0000263 # A group and source specific query that reports more sources
264 # than the packet actually has.
265 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200266 p_gs2 = (
267 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
268 / IP(
269 src=self.pg0.remote_ip4,
270 dst="239.1.1.1",
271 tos=0xC0,
272 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000273 IPOption(
274 copy_flag=1, optclass="control", option="router_alert", length=4
275 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200276 ],
277 )
278 / IGMPv3(type="Membership Query", mrcode=100)
279 / IGMPv3mq(gaddr="239.1.1.1", numsrc=4, srcaddrs=["1.1.1.1"])
280 )
Neale Ranns01b0a052019-06-30 09:05:05 +0000281
282 self.send_and_assert_no_replies(self.pg0, p_gs2, timeout=10)
283
284 #
Neale Ranns947ea622018-06-07 23:48:20 -0700285 # A group and source specific query, with the source NOT matching
286 # the source VPP has. There should be no response.
287 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200288 p_gs2 = (
289 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
290 / IP(
291 src=self.pg0.remote_ip4,
292 dst="239.1.1.1",
293 tos=0xC0,
294 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000295 IPOption(
296 copy_flag=1, optclass="control", option="router_alert", length=4
297 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200298 ],
299 )
300 / IGMPv3(type="Membership Query", mrcode=100)
301 / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.2"])
302 )
Neale Ranns947ea622018-06-07 23:48:20 -0700303
304 self.send_and_assert_no_replies(self.pg0, p_gs2, timeout=10)
305
306 #
307 # A group and source specific query, with the multiple sources
308 # one of which matches the source VPP has.
309 # The report should contain only the source VPP has.
310 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200311 p_gs3 = (
312 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
313 / IP(
314 src=self.pg0.remote_ip4,
315 dst="239.1.1.1",
316 tos=0xC0,
317 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000318 IPOption(
319 copy_flag=1, optclass="control", option="router_alert", length=4
320 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200321 ],
322 )
323 / IGMPv3(type="Membership Query", mrcode=100)
324 / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1", "1.1.1.2", "1.1.1.3"])
325 )
Neale Ranns947ea622018-06-07 23:48:20 -0700326
327 self.send(self.pg0, p_gs3)
328
329 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200330 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700331
332 #
Paul Vinciguerra8feeaff2019-03-27 11:25:48 -0700333 # Two source and group specific queries in quick succession, the
Neale Ranns947ea622018-06-07 23:48:20 -0700334 # first does not have VPPs source the second does. then vice-versa
335 #
336 self.send(self.pg0, [p_gs2, p_gs1])
337 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200338 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700339
340 self.send(self.pg0, [p_gs1, p_gs2])
341 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200342 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700343
344 #
345 # remove state, expect the report for the removal
346 #
347 self.remove_group(h1)
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100348
349 dump = self.vapi.igmp_dump()
350 self.assertFalse(dump)
351
Neale Ranns947ea622018-06-07 23:48:20 -0700352 #
353 # A group with multiple sources
354 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200355 h2 = self.add_group(
356 self.pg0, IgmpSG("239.1.1.1", ["1.1.1.1", "1.1.1.2", "1.1.1.3"])
357 )
Neale Ranns947ea622018-06-07 23:48:20 -0700358
359 # search for the corresponding state created in VPP
360 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
361 self.assertEqual(len(dump), 3)
362 for s in h2.sg.saddrs:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200363 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", s))
Neale Ranns947ea622018-06-07 23:48:20 -0700364 #
365 # Send a general query (to the all router's address)
366 # expect VPP to respond with a membership report will all sources
367 #
368 self.send(self.pg0, p_g)
369
370 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200371 self.verify_report(capture[0], [IgmpRecord(h2.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700372
373 #
374 # Group and source specific query; some present some not
375 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200376 p_gs = (
377 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
378 / IP(
379 src=self.pg0.remote_ip4,
380 dst="239.1.1.1",
381 tos=0xC0,
382 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000383 IPOption(
384 copy_flag=1, optclass="control", option="router_alert", length=4
385 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200386 ],
387 )
388 / IGMPv3(type="Membership Query", mrcode=100)
389 / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1", "1.1.1.2", "1.1.1.4"])
390 )
Neale Ranns947ea622018-06-07 23:48:20 -0700391
392 self.send(self.pg0, p_gs)
393
394 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200395 self.verify_report(
396 capture[0],
397 [
398 IgmpRecord(
399 IgmpSG("239.1.1.1", ["1.1.1.1", "1.1.1.2"]), "Mode Is Include"
400 )
401 ],
402 )
Neale Ranns947ea622018-06-07 23:48:20 -0700403
404 #
405 # add loads more groups
406 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200407 h3 = self.add_group(
408 self.pg0, IgmpSG("239.1.1.2", ["2.1.1.1", "2.1.1.2", "2.1.1.3"])
409 )
410 h4 = self.add_group(
411 self.pg0, IgmpSG("239.1.1.3", ["3.1.1.1", "3.1.1.2", "3.1.1.3"])
412 )
413 h5 = self.add_group(
414 self.pg0, IgmpSG("239.1.1.4", ["4.1.1.1", "4.1.1.2", "4.1.1.3"])
415 )
416 h6 = self.add_group(
417 self.pg0, IgmpSG("239.1.1.5", ["5.1.1.1", "5.1.1.2", "5.1.1.3"])
418 )
419 h7 = self.add_group(
420 self.pg0,
421 IgmpSG(
422 "239.1.1.6",
423 [
424 "6.1.1.1",
425 "6.1.1.2",
426 "6.1.1.3",
427 "6.1.1.4",
428 "6.1.1.5",
429 "6.1.1.6",
430 "6.1.1.7",
431 "6.1.1.8",
432 "6.1.1.9",
433 "6.1.1.10",
434 "6.1.1.11",
435 "6.1.1.12",
436 "6.1.1.13",
437 "6.1.1.14",
438 "6.1.1.15",
439 "6.1.1.16",
440 ],
441 ),
442 )
Neale Ranns947ea622018-06-07 23:48:20 -0700443
444 #
445 # general query.
446 # the order the groups come in is not important, so what is
447 # checked for is what VPP is sending today.
448 #
449 self.send(self.pg0, p_g)
450
451 capture = self.pg0.get_capture(1, timeout=10)
452
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200453 self.verify_report(
454 capture[0],
455 [
456 IgmpRecord(h3.sg, "Mode Is Include"),
457 IgmpRecord(h2.sg, "Mode Is Include"),
458 IgmpRecord(h6.sg, "Mode Is Include"),
459 IgmpRecord(h4.sg, "Mode Is Include"),
460 IgmpRecord(h5.sg, "Mode Is Include"),
461 IgmpRecord(h7.sg, "Mode Is Include"),
462 ],
463 )
Neale Ranns947ea622018-06-07 23:48:20 -0700464
465 #
466 # modify a group to add and remove some sources
467 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200468 h7.sg = IgmpSG(
469 "239.1.1.6",
470 [
471 "6.1.1.1",
472 "6.1.1.2",
473 "6.1.1.5",
474 "6.1.1.6",
475 "6.1.1.7",
476 "6.1.1.8",
477 "6.1.1.9",
478 "6.1.1.10",
479 "6.1.1.11",
480 "6.1.1.12",
481 "6.1.1.13",
482 "6.1.1.14",
483 "6.1.1.15",
484 "6.1.1.16",
485 "6.1.1.17",
486 "6.1.1.18",
487 ],
488 )
Neale Ranns947ea622018-06-07 23:48:20 -0700489
490 self.pg_enable_capture(self.pg_interfaces)
491 self.pg_start()
492 h7.add_vpp_config()
493
494 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200495 self.verify_report(
496 capture[0],
497 [
498 IgmpRecord(
499 IgmpSG("239.1.1.6", ["6.1.1.17", "6.1.1.18"]), "Allow New Sources"
500 ),
501 IgmpRecord(
502 IgmpSG("239.1.1.6", ["6.1.1.3", "6.1.1.4"]), "Block Old Sources"
503 ),
504 ],
505 )
Neale Ranns947ea622018-06-07 23:48:20 -0700506
507 #
508 # add an additional groups with many sources so that each group
509 # consumes the link MTU. We should therefore see multiple state
510 # state reports when queried.
511 #
512 self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [560, 0, 0, 0])
513
514 src_list = []
515 for i in range(128):
516 src_list.append("10.1.1.%d" % i)
517
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200518 h8 = self.add_group(self.pg0, IgmpSG("238.1.1.1", src_list))
519 h9 = self.add_group(self.pg0, IgmpSG("238.1.1.2", src_list))
Neale Ranns947ea622018-06-07 23:48:20 -0700520
521 self.send(self.pg0, p_g)
522
523 capture = self.pg0.get_capture(4, timeout=10)
524
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200525 self.verify_report(
526 capture[0],
527 [
528 IgmpRecord(h3.sg, "Mode Is Include"),
529 IgmpRecord(h2.sg, "Mode Is Include"),
530 IgmpRecord(h6.sg, "Mode Is Include"),
531 IgmpRecord(h4.sg, "Mode Is Include"),
532 IgmpRecord(h5.sg, "Mode Is Include"),
533 ],
534 )
535 self.verify_report(capture[1], [IgmpRecord(h8.sg, "Mode Is Include")])
536 self.verify_report(capture[2], [IgmpRecord(h7.sg, "Mode Is Include")])
537 self.verify_report(capture[3], [IgmpRecord(h9.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700538
539 #
540 # drop the MTU further (so a 128 sized group won't fit)
541 #
542 self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [512, 0, 0, 0])
543
544 self.pg_enable_capture(self.pg_interfaces)
545 self.pg_start()
546
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200547 h10 = VppHostState(
548 self,
549 IGMP_FILTER.INCLUDE,
550 self.pg0.sw_if_index,
551 IgmpSG("238.1.1.3", src_list),
552 )
Neale Ranns947ea622018-06-07 23:48:20 -0700553 h10.add_vpp_config()
554
555 capture = self.pg0.get_capture(2, timeout=10)
Andrew Yourtchenkocb265c62019-07-25 10:03:51 +0000556 # wait for a little bit
557 self.sleep(1)
Neale Ranns947ea622018-06-07 23:48:20 -0700558
559 #
560 # remove state, expect the report for the removal
561 # the dump should be empty
562 #
563 self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [600, 0, 0, 0])
564 self.remove_group(h8)
565 self.remove_group(h9)
566 self.remove_group(h2)
567 self.remove_group(h3)
568 self.remove_group(h4)
569 self.remove_group(h5)
570 self.remove_group(h6)
571 self.remove_group(h7)
572 self.remove_group(h10)
573
574 self.logger.info(self.vapi.cli("sh igmp config"))
575 self.assertFalse(self.vapi.igmp_dump())
576
577 #
578 # TODO
579 # ADD STATE ON MORE INTERFACES
580 #
581
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200582 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST)
Neale Ranns947ea622018-06-07 23:48:20 -0700583
584 def test_igmp_router(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200585 """IGMP Router Functions"""
Neale Ranns947ea622018-06-07 23:48:20 -0700586
587 #
588 # Drop reports when not enabled
589 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200590 p_j = (
591 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
592 / IP(
593 src=self.pg0.remote_ip4,
594 dst="224.0.0.22",
595 tos=0xC0,
596 ttl=1,
597 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000598 IPOption(
599 copy_flag=1, optclass="control", option="router_alert", length=4
600 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200601 ],
602 )
603 / IGMPv3(type="Version 3 Membership Report")
604 / IGMPv3mr(numgrp=1)
605 / IGMPv3gr(
606 rtype="Allow New Sources",
607 maddr="239.1.1.1",
608 srcaddrs=["10.1.1.1", "10.1.1.2"],
609 )
610 )
611 p_l = (
612 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
613 / IP(
614 src=self.pg0.remote_ip4,
615 dst="224.0.0.22",
616 tos=0xC0,
617 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000618 IPOption(
619 copy_flag=1, optclass="control", option="router_alert", length=4
620 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200621 ],
622 )
623 / IGMPv3(type="Version 3 Membership Report")
624 / IGMPv3mr(numgrp=1)
625 / IGMPv3gr(
626 rtype="Block Old Sources",
627 maddr="239.1.1.1",
628 srcaddrs=["10.1.1.1", "10.1.1.2"],
629 )
630 )
Neale Ranns947ea622018-06-07 23:48:20 -0700631
632 self.send(self.pg0, p_j)
633 self.assertFalse(self.vapi.igmp_dump())
634
635 #
636 # drop the default timer values so these tests execute in a
637 # reasonable time frame
638 #
639 self.vapi.cli("test igmp timers query 1 src 3 leave 1")
640
641 #
642 # enable router functions on the interface
643 #
644 self.pg_enable_capture(self.pg_interfaces)
645 self.pg_start()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200646 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.ROUTER)
Neale Ranns947ea622018-06-07 23:48:20 -0700647 self.vapi.want_igmp_events(1)
648
649 #
650 # wait for router to send general query
651 #
652 for ii in range(3):
653 capture = self.pg0.get_capture(1, timeout=2)
654 self.verify_general_query(capture[0])
655 self.pg_enable_capture(self.pg_interfaces)
656 self.pg_start()
657
658 #
659 # re-send the report. VPP should now hold state for the new group
660 # VPP sends a notification that a new group has been joined
661 #
662 self.send(self.pg0, p_j)
663
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200664 self.assertTrue(
665 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)
666 )
667 self.assertTrue(
668 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)
669 )
Neale Ranns947ea622018-06-07 23:48:20 -0700670 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
671 self.assertEqual(len(dump), 2)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200672 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.1"))
673 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.2"))
Neale Ranns947ea622018-06-07 23:48:20 -0700674
675 #
676 # wait for the per-source timer to expire
677 # the state should be reaped
678 # VPP sends a notification that the group has been left
679 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200680 self.assertTrue(
681 wait_for_igmp_event(self, 4, self.pg0, "239.1.1.1", "10.1.1.1", 0)
682 )
683 self.assertTrue(
684 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)
685 )
Neale Ranns947ea622018-06-07 23:48:20 -0700686 self.assertFalse(self.vapi.igmp_dump())
687
688 #
689 # resend the join. wait for two queries and then send a current-state
Paul Vinciguerra8feeaff2019-03-27 11:25:48 -0700690 # record to include all sources. this should reset the expiry time
Neale Ranns947ea622018-06-07 23:48:20 -0700691 # on the sources and thus they will still be present in 2 seconds time.
692 # If the source timer was not refreshed, then the state would have
693 # expired in 3 seconds.
694 #
695 self.send(self.pg0, p_j)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200696 self.assertTrue(
697 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)
698 )
699 self.assertTrue(
700 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)
701 )
Neale Ranns947ea622018-06-07 23:48:20 -0700702 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
703 self.assertEqual(len(dump), 2)
704
705 capture = self.pg0.get_capture(2, timeout=3)
706 self.verify_general_query(capture[0])
707 self.verify_general_query(capture[1])
708
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200709 p_cs = (
710 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
711 / IP(
712 src=self.pg0.remote_ip4,
713 dst="224.0.0.22",
714 tos=0xC0,
715 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000716 IPOption(
717 copy_flag=1, optclass="control", option="router_alert", length=4
718 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200719 ],
720 )
721 / IGMPv3(type="Version 3 Membership Report")
722 / IGMPv3mr(numgrp=1)
723 / IGMPv3gr(
724 rtype="Mode Is Include",
725 maddr="239.1.1.1",
726 srcaddrs=["10.1.1.1", "10.1.1.2"],
727 )
728 )
Neale Ranns947ea622018-06-07 23:48:20 -0700729
730 self.send(self.pg0, p_cs)
731
732 self.sleep(2)
733 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
734 self.assertEqual(len(dump), 2)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200735 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.1"))
736 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.2"))
Neale Ranns947ea622018-06-07 23:48:20 -0700737
738 #
739 # wait for the per-source timer to expire
740 # the state should be reaped
741 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200742 self.assertTrue(
743 wait_for_igmp_event(self, 4, self.pg0, "239.1.1.1", "10.1.1.1", 0)
744 )
745 self.assertTrue(
746 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)
747 )
Neale Ranns947ea622018-06-07 23:48:20 -0700748 self.assertFalse(self.vapi.igmp_dump())
749
750 #
Paul Vinciguerra8feeaff2019-03-27 11:25:48 -0700751 # resend the join, then a leave. Router sends a group+source
Neale Ranns947ea622018-06-07 23:48:20 -0700752 # specific query containing both sources
753 #
754 self.send(self.pg0, p_j)
755
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200756 self.assertTrue(
757 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)
758 )
759 self.assertTrue(
760 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)
761 )
Neale Ranns947ea622018-06-07 23:48:20 -0700762 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
763 self.assertEqual(len(dump), 2)
764
765 self.send(self.pg0, p_l)
766 capture = self.pg0.get_capture(1, timeout=3)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200767 self.verify_group_query(capture[0], "239.1.1.1", ["10.1.1.1", "10.1.1.2"])
Neale Ranns947ea622018-06-07 23:48:20 -0700768
769 #
770 # the group specific query drops the timeout to leave (=1) seconds
771 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200772 self.assertTrue(
773 wait_for_igmp_event(self, 2, self.pg0, "239.1.1.1", "10.1.1.1", 0)
774 )
775 self.assertTrue(
776 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)
777 )
Neale Ranns947ea622018-06-07 23:48:20 -0700778 self.assertFalse(self.vapi.igmp_dump())
779 self.assertFalse(self.vapi.igmp_dump())
780
781 #
Neale Ranns0f7af532018-11-06 05:51:58 -0800782 # a TO_EX({}) / IN_EX({}) is treated like a (*,G) join
Neale Rannsc17776e2018-09-26 06:51:39 -0700783 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200784 p_j = (
785 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
786 / IP(
787 src=self.pg0.remote_ip4,
788 dst="224.0.0.22",
789 tos=0xC0,
790 ttl=1,
791 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000792 IPOption(
793 copy_flag=1, optclass="control", option="router_alert", length=4
794 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200795 ],
796 )
797 / IGMPv3(type="Version 3 Membership Report")
798 / IGMPv3mr(numgrp=1)
799 / IGMPv3gr(rtype="Change To Exclude Mode", maddr="239.1.1.2")
800 )
Neale Rannsc17776e2018-09-26 06:51:39 -0700801
802 self.send(self.pg0, p_j)
803
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200804 self.assertTrue(
805 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.2", "0.0.0.0", 1)
806 )
Neale Rannsc17776e2018-09-26 06:51:39 -0700807
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200808 p_j = (
809 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
810 / IP(
811 src=self.pg0.remote_ip4,
812 dst="224.0.0.22",
813 tos=0xC0,
814 ttl=1,
815 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000816 IPOption(
817 copy_flag=1, optclass="control", option="router_alert", length=4
818 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200819 ],
820 )
821 / IGMPv3(type="Version 3 Membership Report")
822 / IGMPv3mr(numgrp=1)
823 / IGMPv3gr(rtype="Mode Is Exclude", maddr="239.1.1.3")
824 )
Neale Ranns0f7af532018-11-06 05:51:58 -0800825
826 self.send(self.pg0, p_j)
827
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200828 self.assertTrue(
829 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.3", "0.0.0.0", 1)
830 )
Neale Ranns0f7af532018-11-06 05:51:58 -0800831
832 #
Paul Vinciguerra8feeaff2019-03-27 11:25:48 -0700833 # A 'allow sources' for {} should be ignored as it should
Neale Ranns0f7af532018-11-06 05:51:58 -0800834 # never be sent.
835 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200836 p_j = (
837 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
838 / IP(
839 src=self.pg0.remote_ip4,
840 dst="224.0.0.22",
841 tos=0xC0,
842 ttl=1,
843 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000844 IPOption(
845 copy_flag=1, optclass="control", option="router_alert", length=4
846 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200847 ],
848 )
849 / IGMPv3(type="Version 3 Membership Report")
850 / IGMPv3mr(numgrp=1)
851 / IGMPv3gr(rtype="Allow New Sources", maddr="239.1.1.4")
852 )
Neale Ranns0f7af532018-11-06 05:51:58 -0800853
854 self.send(self.pg0, p_j)
855
856 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200857 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.2", "0.0.0.0"))
858 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.3", "0.0.0.0"))
859 self.assertFalse(find_igmp_state(dump, self.pg0, "239.1.1.4", "0.0.0.0"))
Neale Ranns0f7af532018-11-06 05:51:58 -0800860
861 #
862 # a TO_IN({}) and IS_IN({}) are treated like a (*,G) leave
863 #
864 self.vapi.cli("set logging class igmp level debug")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200865 p_l = (
866 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
867 / IP(
868 src=self.pg0.remote_ip4,
869 dst="224.0.0.22",
870 tos=0xC0,
871 ttl=1,
872 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000873 IPOption(
874 copy_flag=1, optclass="control", option="router_alert", length=4
875 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200876 ],
877 )
878 / IGMPv3(type="Version 3 Membership Report")
879 / IGMPv3mr(numgrp=1)
880 / IGMPv3gr(rtype="Change To Include Mode", maddr="239.1.1.2")
881 )
Neale Ranns0f7af532018-11-06 05:51:58 -0800882
883 self.send(self.pg0, p_l)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200884 self.assertTrue(
885 wait_for_igmp_event(self, 2, self.pg0, "239.1.1.2", "0.0.0.0", 0)
886 )
Neale Ranns0f7af532018-11-06 05:51:58 -0800887
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200888 p_l = (
889 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
890 / IP(
891 src=self.pg0.remote_ip4,
892 dst="224.0.0.22",
893 tos=0xC0,
894 ttl=1,
895 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000896 IPOption(
897 copy_flag=1, optclass="control", option="router_alert", length=4
898 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200899 ],
900 )
901 / IGMPv3(type="Version 3 Membership Report")
902 / IGMPv3mr(numgrp=1)
903 / IGMPv3gr(rtype="Mode Is Include", maddr="239.1.1.3")
904 )
Neale Ranns0f7af532018-11-06 05:51:58 -0800905
906 self.send(self.pg0, p_l)
907
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200908 self.assertTrue(
909 wait_for_igmp_event(self, 2, self.pg0, "239.1.1.3", "0.0.0.0", 0)
910 )
Neale Ranns0f7af532018-11-06 05:51:58 -0800911 self.assertFalse(self.vapi.igmp_dump(self.pg0.sw_if_index))
912
Neale Rannsc17776e2018-09-26 06:51:39 -0700913 #
Neale Ranns947ea622018-06-07 23:48:20 -0700914 # disable router config
915 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200916 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.ROUTER)
Neale Ranns947ea622018-06-07 23:48:20 -0700917
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200918 def _create_igmpv3_pck(self, itf, rtype, maddr, srcaddrs):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200919 p = (
920 Ether(dst=itf.local_mac, src=itf.remote_mac)
921 / IP(
922 src=itf.remote_ip4,
923 dst="224.0.0.22",
924 tos=0xC0,
925 ttl=1,
926 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000927 IPOption(
928 copy_flag=1, optclass="control", option="router_alert", length=4
929 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200930 ],
931 )
932 / IGMPv3(type="Version 3 Membership Report")
933 / IGMPv3mr(numgrp=1)
934 / IGMPv3gr(rtype=rtype, maddr=maddr, srcaddrs=srcaddrs)
935 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200936 return p
937
938 def test_igmp_proxy_device(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200939 """IGMP proxy device"""
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200940 self.pg2.admin_down()
941 self.pg2.unconfig_ip4()
942 self.pg2.set_table_ip4(0)
943 self.pg2.config_ip4()
944 self.pg2.admin_up()
945
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200946 self.vapi.cli("test igmp timers query 10 src 3 leave 1")
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200947
948 # enable IGMP
949 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200950 self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 1, IGMP_MODE.ROUTER)
951 self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 1, IGMP_MODE.ROUTER)
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200952
953 # create IGMP proxy device
954 self.vapi.igmp_proxy_device_add_del(0, self.pg0.sw_if_index, 1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200955 self.vapi.igmp_proxy_device_add_del_interface(0, self.pg1.sw_if_index, 1)
956 self.vapi.igmp_proxy_device_add_del_interface(0, self.pg2.sw_if_index, 1)
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200957
958 # send join on pg1. join should be proxied by pg0
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200959 p_j = self._create_igmpv3_pck(
960 self.pg1, "Allow New Sources", "239.1.1.1", ["10.1.1.1", "10.1.1.2"]
961 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200962 self.send(self.pg1, p_j)
963
964 capture = self.pg0.get_capture(1, timeout=1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200965 self.verify_report(
966 capture[0],
967 [
968 IgmpRecord(
969 IgmpSG("239.1.1.1", ["10.1.1.1", "10.1.1.2"]), "Allow New Sources"
970 )
971 ],
972 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200973 self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
974
975 # send join on pg2. join should be proxied by pg0.
976 # the group should contain only 10.1.1.3 as
977 # 10.1.1.1 was already reported
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200978 p_j = self._create_igmpv3_pck(
979 self.pg2, "Allow New Sources", "239.1.1.1", ["10.1.1.1", "10.1.1.3"]
980 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200981 self.send(self.pg2, p_j)
982
983 capture = self.pg0.get_capture(1, timeout=1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200984 self.verify_report(
985 capture[0],
986 [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.3"]), "Allow New Sources")],
987 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200988 self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
989
990 # send leave on pg2. leave for 10.1.1.3 should be proxyed
991 # as pg2 was the only interface interested in 10.1.1.3
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200992 p_l = self._create_igmpv3_pck(
993 self.pg2, "Block Old Sources", "239.1.1.1", ["10.1.1.3"]
994 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200995 self.send(self.pg2, p_l)
996
997 capture = self.pg0.get_capture(1, timeout=2)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200998 self.verify_report(
999 capture[0],
1000 [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.3"]), "Block Old Sources")],
1001 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +02001002 self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
1003
1004 # disable igmp on pg1 (also removes interface from proxy device)
1005 # proxy leave for 10.1.1.2. pg2 is still interested in 10.1.1.1
1006 self.pg_enable_capture(self.pg_interfaces)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001007 self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 0, IGMP_MODE.ROUTER)
Jakub Grajciar97748ca2018-10-04 11:05:35 +02001008
1009 capture = self.pg0.get_capture(1, timeout=1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001010 self.verify_report(
1011 capture[0],
1012 [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.2"]), "Block Old Sources")],
1013 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +02001014 self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
1015
1016 # disable IGMP on pg0 and pg1.
1017 # disabling IGMP on pg0 (proxy device upstream interface)
1018 # removes this proxy device
1019 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001020 self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 0, IGMP_MODE.ROUTER)
Jakub Grajciar97748ca2018-10-04 11:05:35 +02001021 self.assertFalse(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
1022
Jakub Grajciar7b867a82017-12-08 16:28:42 +01001023
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001024if __name__ == "__main__":
Jakub Grajciar7b867a82017-12-08 16:28:42 +01001025 unittest.main(testRunner=VppTestRunner)