diff options
author | Jakub Grajciar <jgrajcia@cisco.com> | 2017-12-08 16:28:42 +0100 |
---|---|---|
committer | Chris Luke <chris_luke@comcast.com> | 2018-03-19 15:53:19 +0000 |
commit | 7b867a8e491357058d37838091ed67a2e77bce2c (patch) | |
tree | 8f9373e4e41e930d0f20e90305168a1ab80dd714 /test/test_igmp.py | |
parent | 489cc829224407a51ca8d612c92bd78c30eaf660 (diff) |
IGMP plugin
- host mode:
igmp_listen - API to signal that the host has joined an (S,G)
- route mode:
igmp_enable - API to enable the reception of host IGMP messages
igmp_event - API to report the host join/leave from an (S,G)
Change-Id: Id180ec27dee617d33ab3088f5dcf6125d3aa9c8f
Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com>
Diffstat (limited to 'test/test_igmp.py')
-rw-r--r-- | test/test_igmp.py | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/test/test_igmp.py b/test/test_igmp.py new file mode 100644 index 00000000000..62b1c9637d6 --- /dev/null +++ b/test/test_igmp.py @@ -0,0 +1,323 @@ +#!/usr/bin/env python + +import unittest +import socket + +from framework import VppTestCase, VppTestRunner, running_extended_tests +from vpp_igmp import * + +from scapy.packet import Raw +from scapy.layers.l2 import Ether +from scapy.layers.inet import IP +from scapy.contrib.igmpv3 import * +from scapy.contrib.igmp import * + + +def checkIGMPv3(): + try: + tmp = IGMPv3() + tmp = IGMPv3mr() + tmp = IGMPv3gr() + tmp = IGMPv3mq() + except NameError: + return False + return True + + +class TestIgmp(VppTestCase): + """ IGMP Test Case """ + + def setUp(self): + super(TestIgmp, self).setUp() + + self.create_pg_interfaces(range(2)) + self.sg_list = [] + self.config_list = [] + + self.ip_addr = [] + for pg in self.pg_interfaces: + pg.admin_up() + pg.config_ip4() + pg.resolve_arp() + + def tearDown(self): + for pg in self.pg_interfaces: + self.vapi.igmp_clear_interface(pg.sw_if_index) + pg.unconfig_ip4() + pg.admin_down() + super(TestIgmp, self).tearDown() + + def send(self, ti, pkts): + ti.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + @unittest.skipUnless(checkIGMPv3(), "missing scapy igmpv3 implementation") + def test_igmp_parse_report(self): + """ IGMP parse Membership Report """ + + # + # VPP acts as a router + # + self.vapi.want_igmp_events(1) + + # hos sends join IGMP 'join' + p_join = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst='224.0.0.22', tos=0xc0) / + IGMPv3() / + IGMPv3mr(numgrp=1) / + IGMPv3gr(rtype=3, maddr="224.1.1.1", srcaddrs=["10.1.1.1"])) + + self.send(self.pg0, p_join) + + # search for the corresponding state created in VPP + dump = self.vapi.igmp_dump() + self.assertEqual(len(dump), 1) + self.assertEqual(dump[0].sw_if_index, self.pg0.sw_if_index) + self.assertEqual(dump[0].gaddr, + socket.inet_pton(socket.AF_INET, + "224.1.1.1")) + self.assertEqual(dump[0].saddr, + socket.inet_pton(socket.AF_INET, + "10.1.1.1")) + + # VPP sends a notification that a new group has been joined + ev = self.vapi.wait_for_event(2, "igmp_event") + + self.assertEqual(ev.saddr, + socket.inet_pton(socket.AF_INET, + "10.1.1.1")) + self.assertEqual(ev.gaddr, + socket.inet_pton(socket.AF_INET, + "224.1.1.1")) + self.assertEqual(ev.is_join, 1) + + # host sends IGMP leave + p_leave = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst='224.0.0.22', tos=0xc0) / + IGMPv3() / + IGMPv3mr(numgrp=1) / + IGMPv3gr(rtype=4, maddr="224.1.1.1", srcaddrs=["10.1.1.1"])) + + self.send(self.pg0, p_leave) + + # VPP sends a notification that a new group has been left + ev = self.vapi.wait_for_event(2, "igmp_event") + + self.assertEqual(ev.saddr, + socket.inet_pton(socket.AF_INET, + "10.1.1.1")) + self.assertEqual(ev.gaddr, + socket.inet_pton(socket.AF_INET, + "224.1.1.1")) + self.assertEqual(ev.is_join, 0) + + # state gone + dump = self.vapi.igmp_dump() + self.assertFalse(dump) + + # resend the join + self.send(self.pg0, p_join) + dump = self.vapi.igmp_dump() + self.assertEqual(len(dump), 1) + self.assertEqual(dump[0].sw_if_index, self.pg0.sw_if_index) + self.assertEqual(dump[0].gaddr, + socket.inet_pton(socket.AF_INET, + "224.1.1.1")) + self.assertEqual(dump[0].saddr, + socket.inet_pton(socket.AF_INET, + "10.1.1.1")) + + # IGMP block + p_block = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst='224.0.0.22', tos=0xc0) / + IGMPv3() / + IGMPv3mr(numgrp=1) / + IGMPv3gr(rtype=6, maddr="224.1.1.1", srcaddrs=["10.1.1.1"])) + + self.send(self.pg0, p_block) + + dump = self.vapi.igmp_dump() + self.assertFalse(dump) + + def verify_general_query(self, p): + ip = p[IP] + self.assertEqual(ip.dst, "224.0.0.1") + self.assertEqual(ip.proto, 2) + igmp = p[IGMPv3] + self.assertEqual(igmp.type, 0x11) + self.assertEqual(igmp.gaddr, "0.0.0.0") + + @unittest.skipUnless(checkIGMPv3(), "missing scapy igmpv3 implementation") + def test_igmp_send_query(self): + """ IGMP send General Query """ + + # + # VPP acts as a router. + # Send a membership report so VPP builds state + # + p_mr = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst='224.0.0.22') / + IGMPv3() / + IGMPv3mr(numgrp=1) / + IGMPv3gr(rtype=3, maddr="224.1.1.1", srcaddrs=["10.1.1.1"])) + + self.send(self.pg0, p_mr) + self.logger.info(self.vapi.cli("sh igmp config")) + + # + # wait for VPP to send out the General Query + # + capture = self.pg0.get_capture(1, timeout=61) + + self.verify_general_query(capture[0]) + + # + # the state will expire in 10 more seconds + # + self.sleep(10) + self.assertFalse(self.vapi.igmp_dump()) + + @unittest.skipUnless(checkIGMPv3(), "missing scapy igmpv3 implementation") + def test_igmp_src_exp(self): + """ IGMP per source timer """ + + # + # VPP Acts as a router + # + + # Host join for (10.1.1.1,224.1.1.1) + p_mr1 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst='224.0.0.22') / + IGMPv3() / + IGMPv3mr(numgrp=1) / + IGMPv3gr(rtype=3, maddr="224.1.1.1", srcaddrs=["10.1.1.1"])) + + self.send(self.pg0, p_mr1) + + # VPP (router) sends General Query + capture = self.pg0.get_capture(1, timeout=61) + + self.verify_general_query(capture[0]) + + # host join for same G and another S: (10.1.1.2,224.1.1.1) + # therefore leaving (10.1.1.1,224.1.1.1) + p_mr2 = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst='224.0.0.22') / + IGMPv3() / + IGMPv3mr(numgrp=1) / + IGMPv3gr(rtype=2, maddr="224.1.1.1", srcaddrs=["10.1.1.2"])) + + self.send(self.pg0, p_mr2) + + # wait for VPP to send general query + capture = self.pg0.get_capture(1, timeout=61) + self.verify_general_query(capture[0]) + + # host leaves (10.1.1.2,224.1.1.1) + p_l = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst='224.0.0.22') / + IGMPv3() / + IGMPv3mr(numgrp=1) / + IGMPv3gr(rtype=2, maddr="224.1.1.1", srcaddrs=["10.1.1.2"])) + + self.send(self.pg0, p_l) + + # FIXME BUG + p_l = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst='224.0.0.22') / + IGMPv3() / + IGMPv3mr(numgrp=1) / + IGMPv3gr(rtype=2, maddr="224.1.1.1", srcaddrs=["10.1.1.1"])) + self.send(self.pg0, p_l) + + # + # host has left all groups, no state left. + # + self.sleep(10) + self.logger.error(self.vapi.cli("sh igmp config")) + self.assertFalse(self.vapi.igmp_dump()) + + @unittest.skipUnless(checkIGMPv3(), "missing scapy igmpv3 implementation") + def test_igmp_query_resp(self): + """ IGMP General Query response """ + + # + # VPP acting as a host. + # Add a listener in VPP for (10.1.1.1,244.1.1.1) + # + self.config_list.append( + VppIgmpConfig( + self, self.pg0.sw_if_index, IgmpSG( + socket.inet_pton( + socket.AF_INET, "10.1.1.1"), socket.inet_pton( + socket.AF_INET, "224.1.1.1")))) + self.config_list[0].add_vpp_config() + + # verify state exists + self.assertTrue(self.vapi.igmp_dump(self.pg0.sw_if_index)) + + # + # Send a general query (from a router) + # + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst='224.0.0.1', tos=0xc0) / + IGMPv3(type=0x11, mrcode=100) / + IGMPv3mq(gaddr="0.0.0.0")) + + self.send(self.pg0, p) + + # + # expect VPP to respond with a membership report for the + # (10.1.1.1, 224.1.1.1) state + # + capture = self.pg0.get_capture(1, timeout=10) + + self.assertEqual(capture[0][IGMPv3].type, 0x22) + self.assertEqual(capture[0][IGMPv3mr].numgrp, 1) + self.assertEqual(capture[0][IGMPv3gr].rtype, 1) + self.assertEqual(capture[0][IGMPv3gr].numsrc, 1) + self.assertEqual(capture[0][IGMPv3gr].maddr, "224.1.1.1") + self.assertEqual(len(capture[0][IGMPv3gr].srcaddrs), 1) + self.assertEqual(capture[0][IGMPv3gr].srcaddrs[0], "10.1.1.1") + + @unittest.skipUnless(checkIGMPv3(), "missing scapy igmpv3 implementation") + def test_igmp_listen(self): + """ IGMP listen (S,G)s """ + + # + # VPP acts as a host + # Add IGMP group state to multiple interfaces and validate its + # presence + # + for pg in self.pg_interfaces: + self.sg_list.append(IgmpSG(socket.inet_pton( + socket.AF_INET, "10.1.1.%d" % pg._sw_if_index), + socket.inet_pton(socket.AF_INET, "224.1.1.1"))) + + for pg in self.pg_interfaces: + self.config_list.append( + VppIgmpConfig( + self, + pg._sw_if_index, + self.sg_list)) + self.config_list[-1].add_vpp_config() + + for config in self.config_list: + dump = self.vapi.igmp_dump(config.sw_if_index) + self.assertTrue(dump) + self.assertEqual(len(dump), len(config.sg_list)) + for idx, e in enumerate(dump): + self.assertEqual(e.sw_if_index, config.sw_if_index) + self.assertEqual(e.saddr, config.sg_list[idx].saddr) + self.assertEqual(e.gaddr, config.sg_list[idx].gaddr) + + for config in self.config_list: + config.remove_vpp_config() + + dump = self.vapi.igmp_dump() + self.assertFalse(dump) + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) |