IGMP plugin
[vpp.git] / test / test_igmp.py
1 #!/usr/bin/env python
2
3 import unittest
4 import socket
5
6 from framework import VppTestCase, VppTestRunner, running_extended_tests
7 from vpp_igmp import *
8
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 *
14
15
16 def checkIGMPv3():
17     try:
18         tmp = IGMPv3()
19         tmp = IGMPv3mr()
20         tmp = IGMPv3gr()
21         tmp = IGMPv3mq()
22     except NameError:
23         return False
24     return True
25
26
27 class TestIgmp(VppTestCase):
28     """ IGMP Test Case """
29
30     def setUp(self):
31         super(TestIgmp, self).setUp()
32
33         self.create_pg_interfaces(range(2))
34         self.sg_list = []
35         self.config_list = []
36
37         self.ip_addr = []
38         for pg in self.pg_interfaces:
39             pg.admin_up()
40             pg.config_ip4()
41             pg.resolve_arp()
42
43     def tearDown(self):
44         for pg in self.pg_interfaces:
45             self.vapi.igmp_clear_interface(pg.sw_if_index)
46             pg.unconfig_ip4()
47             pg.admin_down()
48         super(TestIgmp, self).tearDown()
49
50     def send(self, ti, pkts):
51         ti.add_stream(pkts)
52         self.pg_enable_capture(self.pg_interfaces)
53         self.pg_start()
54
55     @unittest.skipUnless(checkIGMPv3(), "missing scapy igmpv3 implementation")
56     def test_igmp_parse_report(self):
57         """ IGMP parse Membership Report """
58
59         #
60         # VPP acts as a router
61         #
62         self.vapi.want_igmp_events(1)
63
64         # hos sends join IGMP 'join'
65         p_join = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
66                   IP(src=self.pg0.remote_ip4, dst='224.0.0.22', tos=0xc0) /
67                   IGMPv3() /
68                   IGMPv3mr(numgrp=1) /
69                   IGMPv3gr(rtype=3, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
70
71         self.send(self.pg0, p_join)
72
73         # search for the corresponding state created in VPP
74         dump = self.vapi.igmp_dump()
75         self.assertEqual(len(dump), 1)
76         self.assertEqual(dump[0].sw_if_index, self.pg0.sw_if_index)
77         self.assertEqual(dump[0].gaddr,
78                          socket.inet_pton(socket.AF_INET,
79                                           "224.1.1.1"))
80         self.assertEqual(dump[0].saddr,
81                          socket.inet_pton(socket.AF_INET,
82                                           "10.1.1.1"))
83
84         # VPP sends a notification that a new group has been joined
85         ev = self.vapi.wait_for_event(2, "igmp_event")
86
87         self.assertEqual(ev.saddr,
88                          socket.inet_pton(socket.AF_INET,
89                                           "10.1.1.1"))
90         self.assertEqual(ev.gaddr,
91                          socket.inet_pton(socket.AF_INET,
92                                           "224.1.1.1"))
93         self.assertEqual(ev.is_join, 1)
94
95         # host sends IGMP leave
96         p_leave = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
97                    IP(src=self.pg0.remote_ip4, dst='224.0.0.22', tos=0xc0) /
98                    IGMPv3() /
99                    IGMPv3mr(numgrp=1) /
100                    IGMPv3gr(rtype=4, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
101
102         self.send(self.pg0, p_leave)
103
104         # VPP sends a notification that a new group has been left
105         ev = self.vapi.wait_for_event(2, "igmp_event")
106
107         self.assertEqual(ev.saddr,
108                          socket.inet_pton(socket.AF_INET,
109                                           "10.1.1.1"))
110         self.assertEqual(ev.gaddr,
111                          socket.inet_pton(socket.AF_INET,
112                                           "224.1.1.1"))
113         self.assertEqual(ev.is_join, 0)
114
115         # state gone
116         dump = self.vapi.igmp_dump()
117         self.assertFalse(dump)
118
119         # resend the join
120         self.send(self.pg0, p_join)
121         dump = self.vapi.igmp_dump()
122         self.assertEqual(len(dump), 1)
123         self.assertEqual(dump[0].sw_if_index, self.pg0.sw_if_index)
124         self.assertEqual(dump[0].gaddr,
125                          socket.inet_pton(socket.AF_INET,
126                                           "224.1.1.1"))
127         self.assertEqual(dump[0].saddr,
128                          socket.inet_pton(socket.AF_INET,
129                                           "10.1.1.1"))
130
131         # IGMP block
132         p_block = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
133                    IP(src=self.pg0.remote_ip4, dst='224.0.0.22', tos=0xc0) /
134                    IGMPv3() /
135                    IGMPv3mr(numgrp=1) /
136                    IGMPv3gr(rtype=6, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
137
138         self.send(self.pg0, p_block)
139
140         dump = self.vapi.igmp_dump()
141         self.assertFalse(dump)
142
143     def verify_general_query(self, p):
144         ip = p[IP]
145         self.assertEqual(ip.dst, "224.0.0.1")
146         self.assertEqual(ip.proto, 2)
147         igmp = p[IGMPv3]
148         self.assertEqual(igmp.type, 0x11)
149         self.assertEqual(igmp.gaddr, "0.0.0.0")
150
151     @unittest.skipUnless(checkIGMPv3(), "missing scapy igmpv3 implementation")
152     def test_igmp_send_query(self):
153         """ IGMP send General Query """
154
155         #
156         # VPP acts as a router.
157         #   Send a membership report so VPP builds state
158         #
159         p_mr = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
160                 IP(src=self.pg0.remote_ip4, dst='224.0.0.22') /
161                 IGMPv3() /
162                 IGMPv3mr(numgrp=1) /
163                 IGMPv3gr(rtype=3, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
164
165         self.send(self.pg0, p_mr)
166         self.logger.info(self.vapi.cli("sh igmp config"))
167
168         #
169         # wait for VPP to send out the General Query
170         #
171         capture = self.pg0.get_capture(1, timeout=61)
172
173         self.verify_general_query(capture[0])
174
175         #
176         # the state will expire in 10 more seconds
177         #
178         self.sleep(10)
179         self.assertFalse(self.vapi.igmp_dump())
180
181     @unittest.skipUnless(checkIGMPv3(), "missing scapy igmpv3 implementation")
182     def test_igmp_src_exp(self):
183         """ IGMP per source timer """
184
185         #
186         # VPP Acts as a router
187         #
188
189         # Host join for (10.1.1.1,224.1.1.1)
190         p_mr1 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
191                  IP(src=self.pg0.remote_ip4, dst='224.0.0.22') /
192                  IGMPv3() /
193                  IGMPv3mr(numgrp=1) /
194                  IGMPv3gr(rtype=3, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
195
196         self.send(self.pg0, p_mr1)
197
198         # VPP (router) sends General Query
199         capture = self.pg0.get_capture(1, timeout=61)
200
201         self.verify_general_query(capture[0])
202
203         # host join for same G and another S: (10.1.1.2,224.1.1.1)
204         # therefore leaving (10.1.1.1,224.1.1.1)
205         p_mr2 = (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_mr2)
212
213         # wait for VPP to send general query
214         capture = self.pg0.get_capture(1, timeout=61)
215         self.verify_general_query(capture[0])
216
217         # host leaves (10.1.1.2,224.1.1.1)
218         p_l = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
219                IP(src=self.pg0.remote_ip4, dst='224.0.0.22') /
220                IGMPv3() /
221                IGMPv3mr(numgrp=1) /
222                IGMPv3gr(rtype=2, maddr="224.1.1.1", srcaddrs=["10.1.1.2"]))
223
224         self.send(self.pg0, p_l)
225
226         # FIXME BUG
227         p_l = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
228                IP(src=self.pg0.remote_ip4, dst='224.0.0.22') /
229                IGMPv3() /
230                IGMPv3mr(numgrp=1) /
231                IGMPv3gr(rtype=2, maddr="224.1.1.1", srcaddrs=["10.1.1.1"]))
232         self.send(self.pg0, p_l)
233
234         #
235         # host has left all groups, no state left.
236         #
237         self.sleep(10)
238         self.logger.error(self.vapi.cli("sh igmp config"))
239         self.assertFalse(self.vapi.igmp_dump())
240
241     @unittest.skipUnless(checkIGMPv3(), "missing scapy igmpv3 implementation")
242     def test_igmp_query_resp(self):
243         """ IGMP General Query response """
244
245         #
246         # VPP acting as a host.
247         #  Add a listener in VPP for (10.1.1.1,244.1.1.1)
248         #
249         self.config_list.append(
250             VppIgmpConfig(
251                 self, self.pg0.sw_if_index, IgmpSG(
252                     socket.inet_pton(
253                         socket.AF_INET, "10.1.1.1"), socket.inet_pton(
254                         socket.AF_INET, "224.1.1.1"))))
255         self.config_list[0].add_vpp_config()
256
257         # verify state exists
258         self.assertTrue(self.vapi.igmp_dump(self.pg0.sw_if_index))
259
260         #
261         # Send a general query (from a router)
262         #
263         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
264              IP(src=self.pg0.remote_ip4, dst='224.0.0.1', tos=0xc0) /
265              IGMPv3(type=0x11, mrcode=100) /
266              IGMPv3mq(gaddr="0.0.0.0"))
267
268         self.send(self.pg0, p)
269
270         #
271         # expect VPP to respond with a membership report for the
272         # (10.1.1.1, 224.1.1.1) state
273         #
274         capture = self.pg0.get_capture(1, timeout=10)
275
276         self.assertEqual(capture[0][IGMPv3].type, 0x22)
277         self.assertEqual(capture[0][IGMPv3mr].numgrp, 1)
278         self.assertEqual(capture[0][IGMPv3gr].rtype, 1)
279         self.assertEqual(capture[0][IGMPv3gr].numsrc, 1)
280         self.assertEqual(capture[0][IGMPv3gr].maddr, "224.1.1.1")
281         self.assertEqual(len(capture[0][IGMPv3gr].srcaddrs), 1)
282         self.assertEqual(capture[0][IGMPv3gr].srcaddrs[0], "10.1.1.1")
283
284     @unittest.skipUnless(checkIGMPv3(), "missing scapy igmpv3 implementation")
285     def test_igmp_listen(self):
286         """ IGMP listen (S,G)s """
287
288         #
289         # VPP acts as a host
290         #  Add IGMP group state to multiple interfaces and validate its
291         #  presence
292         #
293         for pg in self.pg_interfaces:
294             self.sg_list.append(IgmpSG(socket.inet_pton(
295                 socket.AF_INET, "10.1.1.%d" % pg._sw_if_index),
296                 socket.inet_pton(socket.AF_INET, "224.1.1.1")))
297
298         for pg in self.pg_interfaces:
299             self.config_list.append(
300                 VppIgmpConfig(
301                     self,
302                     pg._sw_if_index,
303                     self.sg_list))
304             self.config_list[-1].add_vpp_config()
305
306         for config in self.config_list:
307             dump = self.vapi.igmp_dump(config.sw_if_index)
308             self.assertTrue(dump)
309             self.assertEqual(len(dump), len(config.sg_list))
310             for idx, e in enumerate(dump):
311                 self.assertEqual(e.sw_if_index, config.sw_if_index)
312                 self.assertEqual(e.saddr, config.sg_list[idx].saddr)
313                 self.assertEqual(e.gaddr, config.sg_list[idx].gaddr)
314
315         for config in self.config_list:
316             config.remove_vpp_config()
317
318         dump = self.vapi.igmp_dump()
319         self.assertFalse(dump)
320
321
322 if __name__ == '__main__':
323     unittest.main(testRunner=VppTestRunner)