| #!/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 * |
| |
| |
| 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() |
| |
| 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") |
| |
| 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(running_extended_tests(), "part of extended tests") |
| 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()) |
| |
| 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") |
| |
| 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) |