blob: e741e6b146fb334ef18b3d1eb40a42fc911ba41e [file] [log] [blame]
Jakub Grajciar7b867a82017-12-08 16:28:42 +01001#!/usr/bin/env python
2
3import unittest
4import socket
5
6from framework import VppTestCase, VppTestRunner, running_extended_tests
7from vpp_igmp import *
8
9from scapy.packet import Raw
10from scapy.layers.l2 import Ether
11from scapy.layers.inet import IP
12from scapy.contrib.igmpv3 import *
13from scapy.contrib.igmp import *
14
15
Jakub Grajciar7b867a82017-12-08 16:28:42 +010016class TestIgmp(VppTestCase):
17 """ IGMP Test Case """
18
19 def setUp(self):
20 super(TestIgmp, self).setUp()
21
22 self.create_pg_interfaces(range(2))
23 self.sg_list = []
24 self.config_list = []
25
26 self.ip_addr = []
27 for pg in self.pg_interfaces:
28 pg.admin_up()
29 pg.config_ip4()
30 pg.resolve_arp()
31
32 def tearDown(self):
33 for pg in self.pg_interfaces:
34 self.vapi.igmp_clear_interface(pg.sw_if_index)
35 pg.unconfig_ip4()
36 pg.admin_down()
37 super(TestIgmp, self).tearDown()
38
39 def send(self, ti, pkts):
40 ti.add_stream(pkts)
41 self.pg_enable_capture(self.pg_interfaces)
42 self.pg_start()
43
Jakub Grajciar7b867a82017-12-08 16:28:42 +010044 def test_igmp_parse_report(self):
45 """ IGMP parse Membership Report """
46
47 #
48 # VPP acts as a router
49 #
50 self.vapi.want_igmp_events(1)
51
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) /
55 IGMPv3() /
56 IGMPv3mr(numgrp=1) /
57 IGMPv3gr(rtype=3, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
58
59 self.send(self.pg0, p_join)
60
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,
67 "224.1.1.1"))
68 self.assertEqual(dump[0].saddr,
69 socket.inet_pton(socket.AF_INET,
70 "10.1.1.1"))
71
72 # VPP sends a notification that a new group has been joined
73 ev = self.vapi.wait_for_event(2, "igmp_event")
74
75 self.assertEqual(ev.saddr,
76 socket.inet_pton(socket.AF_INET,
77 "10.1.1.1"))
78 self.assertEqual(ev.gaddr,
79 socket.inet_pton(socket.AF_INET,
80 "224.1.1.1"))
81 self.assertEqual(ev.is_join, 1)
82
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) /
86 IGMPv3() /
87 IGMPv3mr(numgrp=1) /
88 IGMPv3gr(rtype=4, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
89
90 self.send(self.pg0, p_leave)
91
92 # VPP sends a notification that a new group has been left
93 ev = self.vapi.wait_for_event(2, "igmp_event")
94
95 self.assertEqual(ev.saddr,
96 socket.inet_pton(socket.AF_INET,
97 "10.1.1.1"))
98 self.assertEqual(ev.gaddr,
99 socket.inet_pton(socket.AF_INET,
100 "224.1.1.1"))
101 self.assertEqual(ev.is_join, 0)
102
103 # state gone
104 dump = self.vapi.igmp_dump()
105 self.assertFalse(dump)
106
107 # resend the join
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,
114 "224.1.1.1"))
115 self.assertEqual(dump[0].saddr,
116 socket.inet_pton(socket.AF_INET,
117 "10.1.1.1"))
118
119 # IGMP block
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) /
122 IGMPv3() /
123 IGMPv3mr(numgrp=1) /
124 IGMPv3gr(rtype=6, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
125
126 self.send(self.pg0, p_block)
127
128 dump = self.vapi.igmp_dump()
129 self.assertFalse(dump)
130
131 def verify_general_query(self, p):
132 ip = p[IP]
133 self.assertEqual(ip.dst, "224.0.0.1")
134 self.assertEqual(ip.proto, 2)
135 igmp = p[IGMPv3]
136 self.assertEqual(igmp.type, 0x11)
137 self.assertEqual(igmp.gaddr, "0.0.0.0")
138
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100139 def test_igmp_send_query(self):
140 """ IGMP send General Query """
141
142 #
143 # VPP acts as a router.
144 # Send a membership report so VPP builds state
145 #
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') /
148 IGMPv3() /
149 IGMPv3mr(numgrp=1) /
150 IGMPv3gr(rtype=3, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
151
152 self.send(self.pg0, p_mr)
153 self.logger.info(self.vapi.cli("sh igmp config"))
154
155 #
156 # wait for VPP to send out the General Query
157 #
158 capture = self.pg0.get_capture(1, timeout=61)
159
160 self.verify_general_query(capture[0])
161
162 #
163 # the state will expire in 10 more seconds
164 #
165 self.sleep(10)
166 self.assertFalse(self.vapi.igmp_dump())
167
Neale Ranns5237c772018-03-21 16:34:28 -0700168 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100169 def test_igmp_src_exp(self):
170 """ IGMP per source timer """
171
172 #
173 # VPP Acts as a router
174 #
175
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') /
179 IGMPv3() /
180 IGMPv3mr(numgrp=1) /
181 IGMPv3gr(rtype=3, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
182
183 self.send(self.pg0, p_mr1)
184
185 # VPP (router) sends General Query
186 capture = self.pg0.get_capture(1, timeout=61)
187
188 self.verify_general_query(capture[0])
189
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') /
194 IGMPv3() /
195 IGMPv3mr(numgrp=1) /
196 IGMPv3gr(rtype=2, maddr="224.1.1.1", srcaddrs=["10.1.1.2"]))
197
198 self.send(self.pg0, p_mr2)
199
200 # wait for VPP to send general query
201 capture = self.pg0.get_capture(1, timeout=61)
202 self.verify_general_query(capture[0])
203
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') /
207 IGMPv3() /
208 IGMPv3mr(numgrp=1) /
209 IGMPv3gr(rtype=2, maddr="224.1.1.1", srcaddrs=["10.1.1.2"]))
210
211 self.send(self.pg0, p_l)
212
213 # FIXME BUG
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') /
216 IGMPv3() /
217 IGMPv3mr(numgrp=1) /
218 IGMPv3gr(rtype=2, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
219 self.send(self.pg0, p_l)
220
221 #
222 # host has left all groups, no state left.
223 #
224 self.sleep(10)
225 self.logger.error(self.vapi.cli("sh igmp config"))
226 self.assertFalse(self.vapi.igmp_dump())
227
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100228 def test_igmp_query_resp(self):
229 """ IGMP General Query response """
230
231 #
232 # VPP acting as a host.
233 # Add a listener in VPP for (10.1.1.1,244.1.1.1)
234 #
235 self.config_list.append(
236 VppIgmpConfig(
237 self, self.pg0.sw_if_index, IgmpSG(
238 socket.inet_pton(
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()
242
243 # verify state exists
244 self.assertTrue(self.vapi.igmp_dump(self.pg0.sw_if_index))
245
246 #
247 # Send a general query (from a router)
248 #
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"))
253
254 self.send(self.pg0, p)
255
256 #
257 # expect VPP to respond with a membership report for the
258 # (10.1.1.1, 224.1.1.1) state
259 #
260 capture = self.pg0.get_capture(1, timeout=10)
261
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")
269
Jakub Grajciar7b867a82017-12-08 16:28:42 +0100270 def test_igmp_listen(self):
271 """ IGMP listen (S,G)s """
272
273 #
274 # VPP acts as a host
275 # Add IGMP group state to multiple interfaces and validate its
276 # presence
277 #
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")))
282
283 for pg in self.pg_interfaces:
284 self.config_list.append(
285 VppIgmpConfig(
286 self,
287 pg._sw_if_index,
288 self.sg_list))
289 self.config_list[-1].add_vpp_config()
290
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)
299
300 for config in self.config_list:
301 config.remove_vpp_config()
302
303 dump = self.vapi.igmp_dump()
304 self.assertFalse(dump)
305
306
307if __name__ == '__main__':
308 unittest.main(testRunner=VppTestRunner)