6 from framework import VppTestCase, VppTestRunner, running_extended_tests
9 from scapy.packet import Raw
10 from scapy.layers.l2 import Ether
11 from scapy.layers.inet import IP
12 from scapy.contrib.igmpv3 import *
13 from scapy.contrib.igmp import *
16 class TestIgmp(VppTestCase):
17 """ IGMP Test Case """
20 super(TestIgmp, self).setUp()
22 self.create_pg_interfaces(range(2))
27 for pg in self.pg_interfaces:
33 for pg in self.pg_interfaces:
34 self.vapi.igmp_clear_interface(pg.sw_if_index)
37 super(TestIgmp, self).tearDown()
39 def send(self, ti, pkts):
41 self.pg_enable_capture(self.pg_interfaces)
44 def test_igmp_parse_report(self):
45 """ IGMP parse Membership Report """
48 # VPP acts as a router
50 self.vapi.want_igmp_events(1)
52 # hos sends join IGMP 'join'
53 p_join = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
54 IP(src=self.pg0.remote_ip4, dst='224.0.0.22', tos=0xc0) /
57 IGMPv3gr(rtype=3, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
59 self.send(self.pg0, p_join)
61 # search for the corresponding state created in VPP
62 dump = self.vapi.igmp_dump()
63 self.assertEqual(len(dump), 1)
64 self.assertEqual(dump[0].sw_if_index, self.pg0.sw_if_index)
65 self.assertEqual(dump[0].gaddr,
66 socket.inet_pton(socket.AF_INET,
68 self.assertEqual(dump[0].saddr,
69 socket.inet_pton(socket.AF_INET,
72 # VPP sends a notification that a new group has been joined
73 ev = self.vapi.wait_for_event(2, "igmp_event")
75 self.assertEqual(ev.saddr,
76 socket.inet_pton(socket.AF_INET,
78 self.assertEqual(ev.gaddr,
79 socket.inet_pton(socket.AF_INET,
81 self.assertEqual(ev.is_join, 1)
83 # host sends IGMP leave
84 p_leave = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
85 IP(src=self.pg0.remote_ip4, dst='224.0.0.22', tos=0xc0) /
88 IGMPv3gr(rtype=4, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
90 self.send(self.pg0, p_leave)
92 # VPP sends a notification that a new group has been left
93 ev = self.vapi.wait_for_event(2, "igmp_event")
95 self.assertEqual(ev.saddr,
96 socket.inet_pton(socket.AF_INET,
98 self.assertEqual(ev.gaddr,
99 socket.inet_pton(socket.AF_INET,
101 self.assertEqual(ev.is_join, 0)
104 dump = self.vapi.igmp_dump()
105 self.assertFalse(dump)
108 self.send(self.pg0, p_join)
109 dump = self.vapi.igmp_dump()
110 self.assertEqual(len(dump), 1)
111 self.assertEqual(dump[0].sw_if_index, self.pg0.sw_if_index)
112 self.assertEqual(dump[0].gaddr,
113 socket.inet_pton(socket.AF_INET,
115 self.assertEqual(dump[0].saddr,
116 socket.inet_pton(socket.AF_INET,
120 p_block = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
121 IP(src=self.pg0.remote_ip4, dst='224.0.0.22', tos=0xc0) /
124 IGMPv3gr(rtype=6, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
126 self.send(self.pg0, p_block)
128 dump = self.vapi.igmp_dump()
129 self.assertFalse(dump)
131 def verify_general_query(self, p):
133 self.assertEqual(ip.dst, "224.0.0.1")
134 self.assertEqual(ip.proto, 2)
136 self.assertEqual(igmp.type, 0x11)
137 self.assertEqual(igmp.gaddr, "0.0.0.0")
139 def test_igmp_send_query(self):
140 """ IGMP send General Query """
143 # VPP acts as a router.
144 # Send a membership report so VPP builds state
146 p_mr = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
147 IP(src=self.pg0.remote_ip4, dst='224.0.0.22') /
150 IGMPv3gr(rtype=3, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
152 self.send(self.pg0, p_mr)
153 self.logger.info(self.vapi.cli("sh igmp config"))
156 # wait for VPP to send out the General Query
158 capture = self.pg0.get_capture(1, timeout=61)
160 self.verify_general_query(capture[0])
163 # the state will expire in 10 more seconds
166 self.assertFalse(self.vapi.igmp_dump())
168 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
169 def test_igmp_src_exp(self):
170 """ IGMP per source timer """
173 # VPP Acts as a router
176 # Host join for (10.1.1.1,224.1.1.1)
177 p_mr1 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
178 IP(src=self.pg0.remote_ip4, dst='224.0.0.22') /
181 IGMPv3gr(rtype=3, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
183 self.send(self.pg0, p_mr1)
185 # VPP (router) sends General Query
186 capture = self.pg0.get_capture(1, timeout=61)
188 self.verify_general_query(capture[0])
190 # host join for same G and another S: (10.1.1.2,224.1.1.1)
191 # therefore leaving (10.1.1.1,224.1.1.1)
192 p_mr2 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
193 IP(src=self.pg0.remote_ip4, dst='224.0.0.22') /
196 IGMPv3gr(rtype=2, maddr="224.1.1.1", srcaddrs=["10.1.1.2"]))
198 self.send(self.pg0, p_mr2)
200 # wait for VPP to send general query
201 capture = self.pg0.get_capture(1, timeout=61)
202 self.verify_general_query(capture[0])
204 # host leaves (10.1.1.2,224.1.1.1)
205 p_l = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
206 IP(src=self.pg0.remote_ip4, dst='224.0.0.22') /
209 IGMPv3gr(rtype=2, maddr="224.1.1.1", srcaddrs=["10.1.1.2"]))
211 self.send(self.pg0, p_l)
214 p_l = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
215 IP(src=self.pg0.remote_ip4, dst='224.0.0.22') /
218 IGMPv3gr(rtype=2, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
219 self.send(self.pg0, p_l)
222 # host has left all groups, no state left.
225 self.logger.error(self.vapi.cli("sh igmp config"))
226 self.assertFalse(self.vapi.igmp_dump())
228 def test_igmp_query_resp(self):
229 """ IGMP General Query response """
232 # VPP acting as a host.
233 # Add a listener in VPP for (10.1.1.1,244.1.1.1)
235 self.config_list.append(
237 self, self.pg0.sw_if_index, IgmpSG(
239 socket.AF_INET, "10.1.1.1"), socket.inet_pton(
240 socket.AF_INET, "224.1.1.1"))))
241 self.config_list[0].add_vpp_config()
243 # verify state exists
244 self.assertTrue(self.vapi.igmp_dump(self.pg0.sw_if_index))
247 # Send a general query (from a router)
249 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
250 IP(src=self.pg0.remote_ip4, dst='224.0.0.1', tos=0xc0) /
251 IGMPv3(type=0x11, mrcode=100) /
252 IGMPv3mq(gaddr="0.0.0.0"))
254 self.send(self.pg0, p)
257 # expect VPP to respond with a membership report for the
258 # (10.1.1.1, 224.1.1.1) state
260 capture = self.pg0.get_capture(1, timeout=10)
262 self.assertEqual(capture[0][IGMPv3].type, 0x22)
263 self.assertEqual(capture[0][IGMPv3mr].numgrp, 1)
264 self.assertEqual(capture[0][IGMPv3gr].rtype, 1)
265 self.assertEqual(capture[0][IGMPv3gr].numsrc, 1)
266 self.assertEqual(capture[0][IGMPv3gr].maddr, "224.1.1.1")
267 self.assertEqual(len(capture[0][IGMPv3gr].srcaddrs), 1)
268 self.assertEqual(capture[0][IGMPv3gr].srcaddrs[0], "10.1.1.1")
270 def test_igmp_listen(self):
271 """ IGMP listen (S,G)s """
275 # Add IGMP group state to multiple interfaces and validate its
278 for pg in self.pg_interfaces:
279 self.sg_list.append(IgmpSG(socket.inet_pton(
280 socket.AF_INET, "10.1.1.%d" % pg._sw_if_index),
281 socket.inet_pton(socket.AF_INET, "224.1.1.1")))
283 for pg in self.pg_interfaces:
284 self.config_list.append(
289 self.config_list[-1].add_vpp_config()
291 for config in self.config_list:
292 dump = self.vapi.igmp_dump(config.sw_if_index)
293 self.assertTrue(dump)
294 self.assertEqual(len(dump), len(config.sg_list))
295 for idx, e in enumerate(dump):
296 self.assertEqual(e.sw_if_index, config.sw_if_index)
297 self.assertEqual(e.saddr, config.sg_list[idx].saddr)
298 self.assertEqual(e.gaddr, config.sg_list[idx].gaddr)
300 for config in self.config_list:
301 config.remove_vpp_config()
303 dump = self.vapi.igmp_dump()
304 self.assertFalse(dump)
307 if __name__ == '__main__':
308 unittest.main(testRunner=VppTestRunner)