blob: 037f108b897529b46cf16743b88ef250dd8aef6a [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
22
23
24class IgmpMode:
25 HOST = 1
26 ROUTER = 0
Jakub Grajciar7b867a82017-12-08 16:28:42 +010027
28
Andrew Yourtchenko8dc0d482021-01-29 13:17:19 +000029@tag_fixme_vpp_workers
Jakub Grajciar7b867a82017-12-08 16:28:42 +010030class TestIgmp(VppTestCase):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020031 """IGMP Test Case"""
Jakub Grajciar7b867a82017-12-08 16:28:42 +010032
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -070033 @classmethod
34 def setUpClass(cls):
35 super(TestIgmp, cls).setUpClass()
36
37 @classmethod
38 def tearDownClass(cls):
39 super(TestIgmp, cls).tearDownClass()
40
Jakub Grajciar7b867a82017-12-08 16:28:42 +010041 def setUp(self):
42 super(TestIgmp, self).setUp()
43
Neale Ranns947ea622018-06-07 23:48:20 -070044 self.create_pg_interfaces(range(4))
Jakub Grajciar7b867a82017-12-08 16:28:42 +010045 self.sg_list = []
46 self.config_list = []
47
48 self.ip_addr = []
Neale Ranns947ea622018-06-07 23:48:20 -070049 self.ip_table = VppIpTable(self, 1)
50 self.ip_table.add_vpp_config()
51
52 for pg in self.pg_interfaces[2:]:
53 pg.set_table_ip4(1)
Jakub Grajciar7b867a82017-12-08 16:28:42 +010054 for pg in self.pg_interfaces:
55 pg.admin_up()
56 pg.config_ip4()
57 pg.resolve_arp()
58
59 def tearDown(self):
60 for pg in self.pg_interfaces:
61 self.vapi.igmp_clear_interface(pg.sw_if_index)
62 pg.unconfig_ip4()
Neale Ranns947ea622018-06-07 23:48:20 -070063 pg.set_table_ip4(0)
Jakub Grajciar7b867a82017-12-08 16:28:42 +010064 pg.admin_down()
65 super(TestIgmp, self).tearDown()
66
67 def send(self, ti, pkts):
68 ti.add_stream(pkts)
69 self.pg_enable_capture(self.pg_interfaces)
70 self.pg_start()
71
Neale Ranns947ea622018-06-07 23:48:20 -070072 def test_igmp_flush(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020073 """IGMP Link Up/down and Flush"""
Jakub Grajciar7b867a82017-12-08 16:28:42 +010074
75 #
Neale Ranns947ea622018-06-07 23:48:20 -070076 # FIX THIS. Link down.
Jakub Grajciar7b867a82017-12-08 16:28:42 +010077 #
Jakub Grajciar7b867a82017-12-08 16:28:42 +010078
Neale Ranns947ea622018-06-07 23:48:20 -070079 def test_igmp_enable(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020080 """IGMP enable/disable on an interface
Jakub Grajciar7b867a82017-12-08 16:28:42 +010081
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020082 check for the addition/removal of the IGMP mroutes"""
Jakub Grajciar7b867a82017-12-08 16:28:42 +010083
Neale Ranns947ea622018-06-07 23:48:20 -070084 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)
Jakub Grajciar7b867a82017-12-08 16:28:42 +010086
Neale Ranns947ea622018-06-07 23:48:20 -070087 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))
Jakub Grajciar7b867a82017-12-08 16:28:42 +010089
Neale Ranns947ea622018-06-07 23:48:20 -070090 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)
Jakub Grajciar7b867a82017-12-08 16:28:42 +010092
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020093 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))
Neale Ranns947ea622018-06-07 23:48:20 -070095 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)
Jakub Grajciar7b867a82017-12-08 16:28:42 +010099
Neale Ranns03c254e2020-03-17 14:25:10 +0000100 self.assertTrue(find_mroute(self, "224.0.0.1", "0.0.0.0", 32))
Neale Ranns947ea622018-06-07 23:48:20 -0700101 self.assertFalse(find_mroute(self, "224.0.0.22", "0.0.0.0", 32))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200102 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))
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100104
105 def verify_general_query(self, p):
106 ip = p[IP]
Neale Ranns947ea622018-06-07 23:48:20 -0700107 self.assertEqual(len(ip.options), 1)
108 self.assertEqual(ip.options[0].option, 20)
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100109 self.assertEqual(ip.dst, "224.0.0.1")
110 self.assertEqual(ip.proto, 2)
111 igmp = p[IGMPv3]
112 self.assertEqual(igmp.type, 0x11)
113 self.assertEqual(igmp.gaddr, "0.0.0.0")
114
Neale Ranns947ea622018-06-07 23:48:20 -0700115 def verify_group_query(self, p, grp, srcs):
116 ip = p[IP]
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)
122 igmp = p[IGMPv3]
123 self.assertEqual(igmp.type, 0x11)
124 self.assertEqual(igmp.gaddr, grp)
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100125
Neale Ranns947ea622018-06-07 23:48:20 -0700126 def verify_report(self, rx, records):
127 ip = rx[IP]
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)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200132 self.assertEqual(
133 IGMPv3.igmpv3types[rx[IGMPv3].type], "Version 3 Membership Report"
134 )
Neale Ranns947ea622018-06-07 23:48:20 -0700135 self.assertEqual(rx[IGMPv3mr].numgrp, len(records))
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100136
Neale Ranns947ea622018-06-07 23:48:20 -0700137 received = rx[IGMPv3mr].records
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100138
Neale Ranns947ea622018-06-07 23:48:20 -0700139 for ii in range(len(records)):
140 gr = received[ii]
141 r = records[ii]
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))
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100146
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200147 self.assertEqual(sorted(gr.srcaddrs), sorted(r.sg.saddrs))
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100148
Neale Ranns947ea622018-06-07 23:48:20 -0700149 def add_group(self, itf, sg, n_pkts=2):
150 self.pg_enable_capture(self.pg_interfaces)
151 self.pg_start()
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100152
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200153 hs = VppHostState(self, IGMP_FILTER.INCLUDE, itf.sw_if_index, sg)
Neale Ranns947ea622018-06-07 23:48:20 -0700154 hs.add_vpp_config()
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100155
Neale Ranns947ea622018-06-07 23:48:20 -0700156 capture = itf.get_capture(n_pkts, timeout=10)
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100157
Neale Ranns947ea622018-06-07 23:48:20 -0700158 # reports are transmitted twice due to default rebostness value=2
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200159 self.verify_report(capture[0], [IgmpRecord(sg, "Allow New Sources")]),
160 self.verify_report(capture[1], [IgmpRecord(sg, "Allow New Sources")]),
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100161
Neale Ranns947ea622018-06-07 23:48:20 -0700162 return hs
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100163
Neale Ranns947ea622018-06-07 23:48:20 -0700164 def remove_group(self, hs):
165 self.pg_enable_capture(self.pg_interfaces)
166 self.pg_start()
167 hs.remove_vpp_config()
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100168
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100169 capture = self.pg0.get_capture(1, timeout=10)
170
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200171 self.verify_report(capture[0], [IgmpRecord(hs.sg, "Block Old Sources")])
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100172
Neale Ranns947ea622018-06-07 23:48:20 -0700173 def test_igmp_host(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200174 """IGMP Host functions"""
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100175
176 #
Neale Ranns947ea622018-06-07 23:48:20 -0700177 # Enable interface for host functions
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100178 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200179 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST)
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100180
Neale Ranns947ea622018-06-07 23:48:20 -0700181 #
182 # Add one S,G of state and expect a state-change event report
183 # indicating the addition of the S,G
184 #
185 h1 = self.add_group(self.pg0, IgmpSG("239.1.1.1", ["1.1.1.1"]))
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100186
Neale Ranns947ea622018-06-07 23:48:20 -0700187 # 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)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200190 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "1.1.1.1"))
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100191
Neale Ranns947ea622018-06-07 23:48:20 -0700192 #
193 # Send a general query (to the all router's address)
Neale Ranns01b0a052019-06-30 09:05:05 +0000194 # 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.
Neale Ranns947ea622018-06-07 23:48:20 -0700197 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200198 p_g = (
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")
203 / Raw(b"\x00" * 10)
204 )
Neale Ranns947ea622018-06-07 23:48:20 -0700205
206 self.send(self.pg0, p_g)
207
208 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200209 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700210
211 #
212 # Group specific query
213 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200214 p_gs = (
215 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
216 / IP(
217 src=self.pg0.remote_ip4,
218 dst="239.1.1.1",
219 tos=0xC0,
220 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000221 IPOption(
222 copy_flag=1, optclass="control", option="router_alert", length=4
223 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200224 ],
225 )
226 / IGMPv3(type="Membership Query", mrcode=100)
227 / IGMPv3mq(gaddr="239.1.1.1")
228 )
Neale Ranns947ea622018-06-07 23:48:20 -0700229
230 self.send(self.pg0, p_gs)
231
232 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200233 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700234
235 #
236 # A group and source specific query, with the source matching
237 # the source VPP has
238 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200239 p_gs1 = (
240 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
241 / IP(
242 src=self.pg0.remote_ip4,
243 dst="239.1.1.1",
244 tos=0xC0,
245 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000246 IPOption(
247 copy_flag=1, optclass="control", option="router_alert", length=4
248 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200249 ],
250 )
251 / IGMPv3(type="Membership Query", mrcode=100)
252 / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1"])
253 )
Neale Ranns947ea622018-06-07 23:48:20 -0700254
255 self.send(self.pg0, p_gs1)
256
257 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200258 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700259
260 #
Neale Ranns01b0a052019-06-30 09:05:05 +0000261 # A group and source specific query that reports more sources
262 # than the packet actually has.
263 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200264 p_gs2 = (
265 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
266 / IP(
267 src=self.pg0.remote_ip4,
268 dst="239.1.1.1",
269 tos=0xC0,
270 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000271 IPOption(
272 copy_flag=1, optclass="control", option="router_alert", length=4
273 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200274 ],
275 )
276 / IGMPv3(type="Membership Query", mrcode=100)
277 / IGMPv3mq(gaddr="239.1.1.1", numsrc=4, srcaddrs=["1.1.1.1"])
278 )
Neale Ranns01b0a052019-06-30 09:05:05 +0000279
280 self.send_and_assert_no_replies(self.pg0, p_gs2, timeout=10)
281
282 #
Neale Ranns947ea622018-06-07 23:48:20 -0700283 # A group and source specific query, with the source NOT matching
284 # the source VPP has. There should be no response.
285 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200286 p_gs2 = (
287 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
288 / IP(
289 src=self.pg0.remote_ip4,
290 dst="239.1.1.1",
291 tos=0xC0,
292 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000293 IPOption(
294 copy_flag=1, optclass="control", option="router_alert", length=4
295 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200296 ],
297 )
298 / IGMPv3(type="Membership Query", mrcode=100)
299 / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.2"])
300 )
Neale Ranns947ea622018-06-07 23:48:20 -0700301
302 self.send_and_assert_no_replies(self.pg0, p_gs2, timeout=10)
303
304 #
305 # A group and source specific query, with the multiple sources
306 # one of which matches the source VPP has.
307 # The report should contain only the source VPP has.
308 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200309 p_gs3 = (
310 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
311 / IP(
312 src=self.pg0.remote_ip4,
313 dst="239.1.1.1",
314 tos=0xC0,
315 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000316 IPOption(
317 copy_flag=1, optclass="control", option="router_alert", length=4
318 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200319 ],
320 )
321 / IGMPv3(type="Membership Query", mrcode=100)
322 / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1", "1.1.1.2", "1.1.1.3"])
323 )
Neale Ranns947ea622018-06-07 23:48:20 -0700324
325 self.send(self.pg0, p_gs3)
326
327 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200328 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700329
330 #
Paul Vinciguerra8feeaff2019-03-27 11:25:48 -0700331 # Two source and group specific queries in quick succession, the
Neale Ranns947ea622018-06-07 23:48:20 -0700332 # first does not have VPPs source the second does. then vice-versa
333 #
334 self.send(self.pg0, [p_gs2, p_gs1])
335 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200336 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700337
338 self.send(self.pg0, [p_gs1, p_gs2])
339 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200340 self.verify_report(capture[0], [IgmpRecord(h1.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700341
342 #
343 # remove state, expect the report for the removal
344 #
345 self.remove_group(h1)
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100346
347 dump = self.vapi.igmp_dump()
348 self.assertFalse(dump)
349
Neale Ranns947ea622018-06-07 23:48:20 -0700350 #
351 # A group with multiple sources
352 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200353 h2 = self.add_group(
354 self.pg0, IgmpSG("239.1.1.1", ["1.1.1.1", "1.1.1.2", "1.1.1.3"])
355 )
Neale Ranns947ea622018-06-07 23:48:20 -0700356
357 # search for the corresponding state created in VPP
358 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
359 self.assertEqual(len(dump), 3)
360 for s in h2.sg.saddrs:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200361 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", s))
Neale Ranns947ea622018-06-07 23:48:20 -0700362 #
363 # Send a general query (to the all router's address)
364 # expect VPP to respond with a membership report will all sources
365 #
366 self.send(self.pg0, p_g)
367
368 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200369 self.verify_report(capture[0], [IgmpRecord(h2.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700370
371 #
372 # Group and source specific query; some present some not
373 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200374 p_gs = (
375 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
376 / IP(
377 src=self.pg0.remote_ip4,
378 dst="239.1.1.1",
379 tos=0xC0,
380 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000381 IPOption(
382 copy_flag=1, optclass="control", option="router_alert", length=4
383 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200384 ],
385 )
386 / IGMPv3(type="Membership Query", mrcode=100)
387 / IGMPv3mq(gaddr="239.1.1.1", srcaddrs=["1.1.1.1", "1.1.1.2", "1.1.1.4"])
388 )
Neale Ranns947ea622018-06-07 23:48:20 -0700389
390 self.send(self.pg0, p_gs)
391
392 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200393 self.verify_report(
394 capture[0],
395 [
396 IgmpRecord(
397 IgmpSG("239.1.1.1", ["1.1.1.1", "1.1.1.2"]), "Mode Is Include"
398 )
399 ],
400 )
Neale Ranns947ea622018-06-07 23:48:20 -0700401
402 #
403 # add loads more groups
404 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200405 h3 = self.add_group(
406 self.pg0, IgmpSG("239.1.1.2", ["2.1.1.1", "2.1.1.2", "2.1.1.3"])
407 )
408 h4 = self.add_group(
409 self.pg0, IgmpSG("239.1.1.3", ["3.1.1.1", "3.1.1.2", "3.1.1.3"])
410 )
411 h5 = self.add_group(
412 self.pg0, IgmpSG("239.1.1.4", ["4.1.1.1", "4.1.1.2", "4.1.1.3"])
413 )
414 h6 = self.add_group(
415 self.pg0, IgmpSG("239.1.1.5", ["5.1.1.1", "5.1.1.2", "5.1.1.3"])
416 )
417 h7 = self.add_group(
418 self.pg0,
419 IgmpSG(
420 "239.1.1.6",
421 [
422 "6.1.1.1",
423 "6.1.1.2",
424 "6.1.1.3",
425 "6.1.1.4",
426 "6.1.1.5",
427 "6.1.1.6",
428 "6.1.1.7",
429 "6.1.1.8",
430 "6.1.1.9",
431 "6.1.1.10",
432 "6.1.1.11",
433 "6.1.1.12",
434 "6.1.1.13",
435 "6.1.1.14",
436 "6.1.1.15",
437 "6.1.1.16",
438 ],
439 ),
440 )
Neale Ranns947ea622018-06-07 23:48:20 -0700441
442 #
443 # general query.
444 # the order the groups come in is not important, so what is
445 # checked for is what VPP is sending today.
446 #
447 self.send(self.pg0, p_g)
448
449 capture = self.pg0.get_capture(1, timeout=10)
450
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200451 self.verify_report(
452 capture[0],
453 [
454 IgmpRecord(h3.sg, "Mode Is Include"),
455 IgmpRecord(h2.sg, "Mode Is Include"),
456 IgmpRecord(h6.sg, "Mode Is Include"),
457 IgmpRecord(h4.sg, "Mode Is Include"),
458 IgmpRecord(h5.sg, "Mode Is Include"),
459 IgmpRecord(h7.sg, "Mode Is Include"),
460 ],
461 )
Neale Ranns947ea622018-06-07 23:48:20 -0700462
463 #
464 # modify a group to add and remove some sources
465 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200466 h7.sg = IgmpSG(
467 "239.1.1.6",
468 [
469 "6.1.1.1",
470 "6.1.1.2",
471 "6.1.1.5",
472 "6.1.1.6",
473 "6.1.1.7",
474 "6.1.1.8",
475 "6.1.1.9",
476 "6.1.1.10",
477 "6.1.1.11",
478 "6.1.1.12",
479 "6.1.1.13",
480 "6.1.1.14",
481 "6.1.1.15",
482 "6.1.1.16",
483 "6.1.1.17",
484 "6.1.1.18",
485 ],
486 )
Neale Ranns947ea622018-06-07 23:48:20 -0700487
488 self.pg_enable_capture(self.pg_interfaces)
489 self.pg_start()
490 h7.add_vpp_config()
491
492 capture = self.pg0.get_capture(1, timeout=10)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200493 self.verify_report(
494 capture[0],
495 [
496 IgmpRecord(
497 IgmpSG("239.1.1.6", ["6.1.1.17", "6.1.1.18"]), "Allow New Sources"
498 ),
499 IgmpRecord(
500 IgmpSG("239.1.1.6", ["6.1.1.3", "6.1.1.4"]), "Block Old Sources"
501 ),
502 ],
503 )
Neale Ranns947ea622018-06-07 23:48:20 -0700504
505 #
506 # add an additional groups with many sources so that each group
507 # consumes the link MTU. We should therefore see multiple state
508 # state reports when queried.
509 #
510 self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [560, 0, 0, 0])
511
512 src_list = []
513 for i in range(128):
514 src_list.append("10.1.1.%d" % i)
515
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200516 h8 = self.add_group(self.pg0, IgmpSG("238.1.1.1", src_list))
517 h9 = self.add_group(self.pg0, IgmpSG("238.1.1.2", src_list))
Neale Ranns947ea622018-06-07 23:48:20 -0700518
519 self.send(self.pg0, p_g)
520
521 capture = self.pg0.get_capture(4, timeout=10)
522
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200523 self.verify_report(
524 capture[0],
525 [
526 IgmpRecord(h3.sg, "Mode Is Include"),
527 IgmpRecord(h2.sg, "Mode Is Include"),
528 IgmpRecord(h6.sg, "Mode Is Include"),
529 IgmpRecord(h4.sg, "Mode Is Include"),
530 IgmpRecord(h5.sg, "Mode Is Include"),
531 ],
532 )
533 self.verify_report(capture[1], [IgmpRecord(h8.sg, "Mode Is Include")])
534 self.verify_report(capture[2], [IgmpRecord(h7.sg, "Mode Is Include")])
535 self.verify_report(capture[3], [IgmpRecord(h9.sg, "Mode Is Include")])
Neale Ranns947ea622018-06-07 23:48:20 -0700536
537 #
538 # drop the MTU further (so a 128 sized group won't fit)
539 #
540 self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [512, 0, 0, 0])
541
542 self.pg_enable_capture(self.pg_interfaces)
543 self.pg_start()
544
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200545 h10 = VppHostState(
546 self,
547 IGMP_FILTER.INCLUDE,
548 self.pg0.sw_if_index,
549 IgmpSG("238.1.1.3", src_list),
550 )
Neale Ranns947ea622018-06-07 23:48:20 -0700551 h10.add_vpp_config()
552
553 capture = self.pg0.get_capture(2, timeout=10)
Andrew Yourtchenkocb265c62019-07-25 10:03:51 +0000554 # wait for a little bit
555 self.sleep(1)
Neale Ranns947ea622018-06-07 23:48:20 -0700556
557 #
558 # remove state, expect the report for the removal
559 # the dump should be empty
560 #
561 self.vapi.sw_interface_set_mtu(self.pg0.sw_if_index, [600, 0, 0, 0])
562 self.remove_group(h8)
563 self.remove_group(h9)
564 self.remove_group(h2)
565 self.remove_group(h3)
566 self.remove_group(h4)
567 self.remove_group(h5)
568 self.remove_group(h6)
569 self.remove_group(h7)
570 self.remove_group(h10)
571
572 self.logger.info(self.vapi.cli("sh igmp config"))
573 self.assertFalse(self.vapi.igmp_dump())
574
575 #
576 # TODO
577 # ADD STATE ON MORE INTERFACES
578 #
579
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200580 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST)
Neale Ranns947ea622018-06-07 23:48:20 -0700581
582 def test_igmp_router(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200583 """IGMP Router Functions"""
Neale Ranns947ea622018-06-07 23:48:20 -0700584
585 #
586 # Drop reports when not enabled
587 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200588 p_j = (
589 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
590 / IP(
591 src=self.pg0.remote_ip4,
592 dst="224.0.0.22",
593 tos=0xC0,
594 ttl=1,
595 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000596 IPOption(
597 copy_flag=1, optclass="control", option="router_alert", length=4
598 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200599 ],
600 )
601 / IGMPv3(type="Version 3 Membership Report")
602 / IGMPv3mr(numgrp=1)
603 / IGMPv3gr(
604 rtype="Allow New Sources",
605 maddr="239.1.1.1",
606 srcaddrs=["10.1.1.1", "10.1.1.2"],
607 )
608 )
609 p_l = (
610 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
611 / IP(
612 src=self.pg0.remote_ip4,
613 dst="224.0.0.22",
614 tos=0xC0,
615 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000616 IPOption(
617 copy_flag=1, optclass="control", option="router_alert", length=4
618 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200619 ],
620 )
621 / IGMPv3(type="Version 3 Membership Report")
622 / IGMPv3mr(numgrp=1)
623 / IGMPv3gr(
624 rtype="Block Old Sources",
625 maddr="239.1.1.1",
626 srcaddrs=["10.1.1.1", "10.1.1.2"],
627 )
628 )
Neale Ranns947ea622018-06-07 23:48:20 -0700629
630 self.send(self.pg0, p_j)
631 self.assertFalse(self.vapi.igmp_dump())
632
633 #
634 # drop the default timer values so these tests execute in a
635 # reasonable time frame
636 #
637 self.vapi.cli("test igmp timers query 1 src 3 leave 1")
638
639 #
640 # enable router functions on the interface
641 #
642 self.pg_enable_capture(self.pg_interfaces)
643 self.pg_start()
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200644 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.ROUTER)
Neale Ranns947ea622018-06-07 23:48:20 -0700645 self.vapi.want_igmp_events(1)
646
647 #
648 # wait for router to send general query
649 #
650 for ii in range(3):
651 capture = self.pg0.get_capture(1, timeout=2)
652 self.verify_general_query(capture[0])
653 self.pg_enable_capture(self.pg_interfaces)
654 self.pg_start()
655
656 #
657 # re-send the report. VPP should now hold state for the new group
658 # VPP sends a notification that a new group has been joined
659 #
660 self.send(self.pg0, p_j)
661
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200662 self.assertTrue(
663 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)
664 )
665 self.assertTrue(
666 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)
667 )
Neale Ranns947ea622018-06-07 23:48:20 -0700668 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
669 self.assertEqual(len(dump), 2)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200670 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.1"))
671 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.2"))
Neale Ranns947ea622018-06-07 23:48:20 -0700672
673 #
674 # wait for the per-source timer to expire
675 # the state should be reaped
676 # VPP sends a notification that the group has been left
677 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200678 self.assertTrue(
679 wait_for_igmp_event(self, 4, self.pg0, "239.1.1.1", "10.1.1.1", 0)
680 )
681 self.assertTrue(
682 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)
683 )
Neale Ranns947ea622018-06-07 23:48:20 -0700684 self.assertFalse(self.vapi.igmp_dump())
685
686 #
687 # resend the join. wait for two queries and then send a current-state
Paul Vinciguerra8feeaff2019-03-27 11:25:48 -0700688 # record to include all sources. this should reset the expiry time
Neale Ranns947ea622018-06-07 23:48:20 -0700689 # on the sources and thus they will still be present in 2 seconds time.
690 # If the source timer was not refreshed, then the state would have
691 # expired in 3 seconds.
692 #
693 self.send(self.pg0, p_j)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200694 self.assertTrue(
695 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)
696 )
697 self.assertTrue(
698 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)
699 )
Neale Ranns947ea622018-06-07 23:48:20 -0700700 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
701 self.assertEqual(len(dump), 2)
702
703 capture = self.pg0.get_capture(2, timeout=3)
704 self.verify_general_query(capture[0])
705 self.verify_general_query(capture[1])
706
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200707 p_cs = (
708 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
709 / IP(
710 src=self.pg0.remote_ip4,
711 dst="224.0.0.22",
712 tos=0xC0,
713 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000714 IPOption(
715 copy_flag=1, optclass="control", option="router_alert", length=4
716 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200717 ],
718 )
719 / IGMPv3(type="Version 3 Membership Report")
720 / IGMPv3mr(numgrp=1)
721 / IGMPv3gr(
722 rtype="Mode Is Include",
723 maddr="239.1.1.1",
724 srcaddrs=["10.1.1.1", "10.1.1.2"],
725 )
726 )
Neale Ranns947ea622018-06-07 23:48:20 -0700727
728 self.send(self.pg0, p_cs)
729
730 self.sleep(2)
731 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
732 self.assertEqual(len(dump), 2)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200733 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.1"))
734 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.1", "10.1.1.2"))
Neale Ranns947ea622018-06-07 23:48:20 -0700735
736 #
737 # wait for the per-source timer to expire
738 # the state should be reaped
739 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200740 self.assertTrue(
741 wait_for_igmp_event(self, 4, self.pg0, "239.1.1.1", "10.1.1.1", 0)
742 )
743 self.assertTrue(
744 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)
745 )
Neale Ranns947ea622018-06-07 23:48:20 -0700746 self.assertFalse(self.vapi.igmp_dump())
747
748 #
Paul Vinciguerra8feeaff2019-03-27 11:25:48 -0700749 # resend the join, then a leave. Router sends a group+source
Neale Ranns947ea622018-06-07 23:48:20 -0700750 # specific query containing both sources
751 #
752 self.send(self.pg0, p_j)
753
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200754 self.assertTrue(
755 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.1", 1)
756 )
757 self.assertTrue(
758 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 1)
759 )
Neale Ranns947ea622018-06-07 23:48:20 -0700760 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
761 self.assertEqual(len(dump), 2)
762
763 self.send(self.pg0, p_l)
764 capture = self.pg0.get_capture(1, timeout=3)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200765 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 -0700766
767 #
768 # the group specific query drops the timeout to leave (=1) seconds
769 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200770 self.assertTrue(
771 wait_for_igmp_event(self, 2, self.pg0, "239.1.1.1", "10.1.1.1", 0)
772 )
773 self.assertTrue(
774 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.1", "10.1.1.2", 0)
775 )
Neale Ranns947ea622018-06-07 23:48:20 -0700776 self.assertFalse(self.vapi.igmp_dump())
777 self.assertFalse(self.vapi.igmp_dump())
778
779 #
Neale Ranns0f7af532018-11-06 05:51:58 -0800780 # a TO_EX({}) / IN_EX({}) is treated like a (*,G) join
Neale Rannsc17776e2018-09-26 06:51:39 -0700781 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200782 p_j = (
783 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
784 / IP(
785 src=self.pg0.remote_ip4,
786 dst="224.0.0.22",
787 tos=0xC0,
788 ttl=1,
789 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000790 IPOption(
791 copy_flag=1, optclass="control", option="router_alert", length=4
792 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200793 ],
794 )
795 / IGMPv3(type="Version 3 Membership Report")
796 / IGMPv3mr(numgrp=1)
797 / IGMPv3gr(rtype="Change To Exclude Mode", maddr="239.1.1.2")
798 )
Neale Rannsc17776e2018-09-26 06:51:39 -0700799
800 self.send(self.pg0, p_j)
801
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200802 self.assertTrue(
803 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.2", "0.0.0.0", 1)
804 )
Neale Rannsc17776e2018-09-26 06:51:39 -0700805
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200806 p_j = (
807 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
808 / IP(
809 src=self.pg0.remote_ip4,
810 dst="224.0.0.22",
811 tos=0xC0,
812 ttl=1,
813 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000814 IPOption(
815 copy_flag=1, optclass="control", option="router_alert", length=4
816 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200817 ],
818 )
819 / IGMPv3(type="Version 3 Membership Report")
820 / IGMPv3mr(numgrp=1)
821 / IGMPv3gr(rtype="Mode Is Exclude", maddr="239.1.1.3")
822 )
Neale Ranns0f7af532018-11-06 05:51:58 -0800823
824 self.send(self.pg0, p_j)
825
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200826 self.assertTrue(
827 wait_for_igmp_event(self, 1, self.pg0, "239.1.1.3", "0.0.0.0", 1)
828 )
Neale Ranns0f7af532018-11-06 05:51:58 -0800829
830 #
Paul Vinciguerra8feeaff2019-03-27 11:25:48 -0700831 # A 'allow sources' for {} should be ignored as it should
Neale Ranns0f7af532018-11-06 05:51:58 -0800832 # never be sent.
833 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200834 p_j = (
835 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
836 / IP(
837 src=self.pg0.remote_ip4,
838 dst="224.0.0.22",
839 tos=0xC0,
840 ttl=1,
841 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000842 IPOption(
843 copy_flag=1, optclass="control", option="router_alert", length=4
844 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200845 ],
846 )
847 / IGMPv3(type="Version 3 Membership Report")
848 / IGMPv3mr(numgrp=1)
849 / IGMPv3gr(rtype="Allow New Sources", maddr="239.1.1.4")
850 )
Neale Ranns0f7af532018-11-06 05:51:58 -0800851
852 self.send(self.pg0, p_j)
853
854 dump = self.vapi.igmp_dump(self.pg0.sw_if_index)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200855 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.2", "0.0.0.0"))
856 self.assertTrue(find_igmp_state(dump, self.pg0, "239.1.1.3", "0.0.0.0"))
857 self.assertFalse(find_igmp_state(dump, self.pg0, "239.1.1.4", "0.0.0.0"))
Neale Ranns0f7af532018-11-06 05:51:58 -0800858
859 #
860 # a TO_IN({}) and IS_IN({}) are treated like a (*,G) leave
861 #
862 self.vapi.cli("set logging class igmp level debug")
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200863 p_l = (
864 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
865 / IP(
866 src=self.pg0.remote_ip4,
867 dst="224.0.0.22",
868 tos=0xC0,
869 ttl=1,
870 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000871 IPOption(
872 copy_flag=1, optclass="control", option="router_alert", length=4
873 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200874 ],
875 )
876 / IGMPv3(type="Version 3 Membership Report")
877 / IGMPv3mr(numgrp=1)
878 / IGMPv3gr(rtype="Change To Include Mode", maddr="239.1.1.2")
879 )
Neale Ranns0f7af532018-11-06 05:51:58 -0800880
881 self.send(self.pg0, p_l)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200882 self.assertTrue(
883 wait_for_igmp_event(self, 2, self.pg0, "239.1.1.2", "0.0.0.0", 0)
884 )
Neale Ranns0f7af532018-11-06 05:51:58 -0800885
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200886 p_l = (
887 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
888 / IP(
889 src=self.pg0.remote_ip4,
890 dst="224.0.0.22",
891 tos=0xC0,
892 ttl=1,
893 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000894 IPOption(
895 copy_flag=1, optclass="control", option="router_alert", length=4
896 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200897 ],
898 )
899 / IGMPv3(type="Version 3 Membership Report")
900 / IGMPv3mr(numgrp=1)
901 / IGMPv3gr(rtype="Mode Is Include", maddr="239.1.1.3")
902 )
Neale Ranns0f7af532018-11-06 05:51:58 -0800903
904 self.send(self.pg0, p_l)
905
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200906 self.assertTrue(
907 wait_for_igmp_event(self, 2, self.pg0, "239.1.1.3", "0.0.0.0", 0)
908 )
Neale Ranns0f7af532018-11-06 05:51:58 -0800909 self.assertFalse(self.vapi.igmp_dump(self.pg0.sw_if_index))
910
Neale Rannsc17776e2018-09-26 06:51:39 -0700911 #
Neale Ranns947ea622018-06-07 23:48:20 -0700912 # disable router config
913 #
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200914 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.ROUTER)
Neale Ranns947ea622018-06-07 23:48:20 -0700915
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200916 def _create_igmpv3_pck(self, itf, rtype, maddr, srcaddrs):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200917 p = (
918 Ether(dst=itf.local_mac, src=itf.remote_mac)
919 / IP(
920 src=itf.remote_ip4,
921 dst="224.0.0.22",
922 tos=0xC0,
923 ttl=1,
924 options=[
Vladislav Grishenkoa58dae62022-09-16 17:01:00 +0000925 IPOption(
926 copy_flag=1, optclass="control", option="router_alert", length=4
927 )
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200928 ],
929 )
930 / IGMPv3(type="Version 3 Membership Report")
931 / IGMPv3mr(numgrp=1)
932 / IGMPv3gr(rtype=rtype, maddr=maddr, srcaddrs=srcaddrs)
933 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200934 return p
935
936 def test_igmp_proxy_device(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200937 """IGMP proxy device"""
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200938 self.pg2.admin_down()
939 self.pg2.unconfig_ip4()
940 self.pg2.set_table_ip4(0)
941 self.pg2.config_ip4()
942 self.pg2.admin_up()
943
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200944 self.vapi.cli("test igmp timers query 10 src 3 leave 1")
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200945
946 # enable IGMP
947 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 1, IGMP_MODE.HOST)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200948 self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 1, IGMP_MODE.ROUTER)
949 self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 1, IGMP_MODE.ROUTER)
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200950
951 # create IGMP proxy device
952 self.vapi.igmp_proxy_device_add_del(0, self.pg0.sw_if_index, 1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200953 self.vapi.igmp_proxy_device_add_del_interface(0, self.pg1.sw_if_index, 1)
954 self.vapi.igmp_proxy_device_add_del_interface(0, self.pg2.sw_if_index, 1)
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200955
956 # send join on pg1. join should be proxied by pg0
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200957 p_j = self._create_igmpv3_pck(
958 self.pg1, "Allow New Sources", "239.1.1.1", ["10.1.1.1", "10.1.1.2"]
959 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200960 self.send(self.pg1, p_j)
961
962 capture = self.pg0.get_capture(1, timeout=1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200963 self.verify_report(
964 capture[0],
965 [
966 IgmpRecord(
967 IgmpSG("239.1.1.1", ["10.1.1.1", "10.1.1.2"]), "Allow New Sources"
968 )
969 ],
970 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200971 self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
972
973 # send join on pg2. join should be proxied by pg0.
974 # the group should contain only 10.1.1.3 as
975 # 10.1.1.1 was already reported
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200976 p_j = self._create_igmpv3_pck(
977 self.pg2, "Allow New Sources", "239.1.1.1", ["10.1.1.1", "10.1.1.3"]
978 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200979 self.send(self.pg2, p_j)
980
981 capture = self.pg0.get_capture(1, timeout=1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200982 self.verify_report(
983 capture[0],
984 [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.3"]), "Allow New Sources")],
985 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200986 self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
987
988 # send leave on pg2. leave for 10.1.1.3 should be proxyed
989 # as pg2 was the only interface interested in 10.1.1.3
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200990 p_l = self._create_igmpv3_pck(
991 self.pg2, "Block Old Sources", "239.1.1.1", ["10.1.1.3"]
992 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +0200993 self.send(self.pg2, p_l)
994
995 capture = self.pg0.get_capture(1, timeout=2)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200996 self.verify_report(
997 capture[0],
998 [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.3"]), "Block Old Sources")],
999 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +02001000 self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
1001
1002 # disable igmp on pg1 (also removes interface from proxy device)
1003 # proxy leave for 10.1.1.2. pg2 is still interested in 10.1.1.1
1004 self.pg_enable_capture(self.pg_interfaces)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001005 self.vapi.igmp_enable_disable(self.pg1.sw_if_index, 0, IGMP_MODE.ROUTER)
Jakub Grajciar97748ca2018-10-04 11:05:35 +02001006
1007 capture = self.pg0.get_capture(1, timeout=1)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001008 self.verify_report(
1009 capture[0],
1010 [IgmpRecord(IgmpSG("239.1.1.1", ["10.1.1.2"]), "Block Old Sources")],
1011 )
Jakub Grajciar97748ca2018-10-04 11:05:35 +02001012 self.assertTrue(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
1013
1014 # disable IGMP on pg0 and pg1.
1015 # disabling IGMP on pg0 (proxy device upstream interface)
1016 # removes this proxy device
1017 self.vapi.igmp_enable_disable(self.pg0.sw_if_index, 0, IGMP_MODE.HOST)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001018 self.vapi.igmp_enable_disable(self.pg2.sw_if_index, 0, IGMP_MODE.ROUTER)
Jakub Grajciar97748ca2018-10-04 11:05:35 +02001019 self.assertFalse(find_mroute(self, "239.1.1.1", "0.0.0.0", 32))
1020
Jakub Grajciar7b867a82017-12-08 16:28:42 +01001021
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001022if __name__ == "__main__":
Jakub Grajciar7b867a82017-12-08 16:28:42 +01001023 unittest.main(testRunner=VppTestRunner)