From 8fe8cc21d1e389d8e971a303e53c9e703aaaa0e0 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Tue, 1 Nov 2016 10:05:08 +0000 Subject: MPLS Exp-null Tests Add some 'make test' unit tests for MPLS explicit NULL label handling. Fix the stacking of the MPLS load-balance result form the lookup onto the IPx lookup object. Change-Id: I890d1221b8e3dea99bcc714ed9d0154a5f602c52 Signed-off-by: Neale Ranns --- test/framework.py | 3 +- test/test_mpls.py | 209 ++++++++++++++++++++++++++++++++++++++++++++++ test/vpp_interface.py | 17 ++++ test/vpp_papi_provider.py | 23 +++++ 4 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 test/test_mpls.py (limited to 'test') diff --git a/test/framework.py b/test/framework.py index 5f75e010..8c39701b 100644 --- a/test/framework.py +++ b/test/framework.py @@ -113,7 +113,8 @@ class VppTestCase(unittest.TestCase): cls.set_debug_flags(d) cls.vpp_bin = os.getenv('VPP_TEST_BIN', "vpp") cls.plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH') - cls.vpp_cmdline = [cls.vpp_bin, "unix", "nodaemon", + cls.vpp_cmdline = [cls.vpp_bin, "unix", "{", "nodaemon", + "cli-listen localhost:5002", "}", "api-segment", "{", "prefix", cls.shm_prefix, "}"] if cls.plugin_path is not None: cls.vpp_cmdline.extend(["plugin_path", cls.plugin_path]) diff --git a/test/test_mpls.py b/test/test_mpls.py new file mode 100644 index 00000000..45af4704 --- /dev/null +++ b/test/test_mpls.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python + +import unittest +import socket +from logging import * + +from framework import VppTestCase, VppTestRunner +from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint + +from scapy.packet import Raw +from scapy.layers.l2 import Ether, Dot1Q, ARP +from scapy.layers.inet import IP, UDP +from scapy.layers.inet6 import ICMPv6ND_NS, IPv6, UDP +from scapy.contrib.mpls import MPLS + +class TestMPLS(VppTestCase): + """ MPLS Test Case """ + + @classmethod + def setUpClass(cls): + super(TestMPLS, cls).setUpClass() + + def setUp(self): + super(TestMPLS, self).setUp() + + # create 2 pg interfaces + self.create_pg_interfaces(range(3)) + + # setup both interfaces + # assign them different tables. + table_id = 0 + + for i in self.pg_interfaces: + i.admin_up() + i.set_table_ip4(table_id) + i.set_table_ip6(table_id) + i.config_ip4() + i.config_ip6() + i.enable_mpls() + i.resolve_arp() + i.resolve_ndp() + table_id += 1 + + def tearDown(self): + super(TestMPLS, self).tearDown() + + def create_stream_ip4(self, src_if, mpls_label, mpls_ttl): + pkts = [] + for i in range(0, 257): + info = self.create_packet_info(src_if.sw_if_index, + src_if.sw_if_index) + payload = self.info_to_payload(info) + p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / + MPLS(label=mpls_label, ttl=mpls_ttl) / + IP(src=src_if.remote_ip4, dst=src_if.remote_ip4) / + UDP(sport=1234, dport=1234) / + Raw(payload)) + info.data = p.copy() + pkts.append(p) + return pkts + + def create_stream_ip6(self, src_if, mpls_label, mpls_ttl): + pkts = [] + for i in range(0, 257): + info = self.create_packet_info(src_if.sw_if_index, + src_if.sw_if_index) + payload = self.info_to_payload(info) + p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / + MPLS(label=mpls_label, ttl=mpls_ttl) / + IPv6(src=src_if.remote_ip6, dst=src_if.remote_ip6) / + UDP(sport=1234, dport=1234) / + Raw(payload)) + info.data = p.copy() + pkts.append(p) + return pkts + + def verify_capture_ip4(self, src_if, capture, sent): + try: + self.assertEqual(len(capture), len(sent)) + + for i in range(len(capture)): + tx = sent[i] + rx = capture[i] + + # the rx'd packet has the MPLS label popped + eth = rx[Ether]; + self.assertEqual(eth.type, 0x800); + + tx_ip = tx[IP] + rx_ip = rx[IP] + + self.assertEqual(rx_ip.src, tx_ip.src) + self.assertEqual(rx_ip.dst, tx_ip.dst) + # IP processing post pop has decremented the TTL + self.assertEqual(rx_ip.ttl+1, tx_ip.ttl) + + except: + raise; + + def verify_capture_ip6(self, src_if, capture, sent): + try: + self.assertEqual(len(capture), len(sent)) + + for i in range(len(capture)): + tx = sent[i] + rx = capture[i] + + # the rx'd packet has the MPLS label popped + eth = rx[Ether]; + self.assertEqual(eth.type, 0x86DD); + + tx_ip = tx[IPv6] + rx_ip = rx[IPv6] + + self.assertEqual(rx_ip.src, tx_ip.src) + self.assertEqual(rx_ip.dst, tx_ip.dst) + # IP processing post pop has decremented the TTL + self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim) + + except: + raise; + + + def test_v4_exp_null(self): + """ MPLS V4 Explicit NULL test """ + + # + # The first test case has an MPLS TTL of 0 + # all packet should be dropped + # + tx = self.create_stream_ip4(self.pg0, 0, 0) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + + try: + self.assertEqual(0, len(rx)); + except: + error("MPLS TTL=0 packets forwarded") + error(packet.show()) + raise + + # + # a stream with a non-zero MPLS TTL + # PG0 is in the default table + # + self.vapi.cli("clear trace") + tx = self.create_stream_ip4(self.pg0, 0, 2) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_capture_ip4(self.pg0, rx, tx) + + # + # a stream with a non-zero MPLS TTL + # PG1 is in table 1 + # we are ensuring the post-pop lookup occurs in the VRF table + # + self.vapi.cli("clear trace") + tx = self.create_stream_ip4(self.pg1, 0, 2) + self.pg1.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture() + self.verify_capture_ip4(self.pg0, rx, tx) + + def test_v6_exp_null(self): + """ MPLS V6 Explicit NULL test """ + + # + # a stream with a non-zero MPLS TTL + # PG0 is in the default table + # + self.vapi.cli("clear trace") + tx = self.create_stream_ip6(self.pg0, 2, 2) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_capture_ip6(self.pg0, rx, tx) + + # + # a stream with a non-zero MPLS TTL + # PG1 is in table 1 + # we are ensuring the post-pop lookup occurs in the VRF table + # + self.vapi.cli("clear trace") + tx = self.create_stream_ip6(self.pg1, 2, 2) + self.pg1.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture() + self.verify_capture_ip6(self.pg0, rx, tx) + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_interface.py b/test/vpp_interface.py index c596ab5a..509ab952 100644 --- a/test/vpp_interface.py +++ b/test/vpp_interface.py @@ -131,6 +131,18 @@ class VppInterface(object): self.test.vapi.sw_interface_add_del_address( self.sw_if_index, addr, addr_len, is_ipv6=1) + def set_table_ip4(self, table_id): + """Set the interface in a IPv4 Table. + Must be called before configuring IP4 addresses""" + self.test.vapi.sw_interface_set_table( + self.sw_if_index, 0, table_id) + + def set_table_ip6(self, table_id): + """Set the interface in a IPv6 Table. + Must be called before configuring IP6 addresses""" + self.test.vapi.sw_interface_set_table( + self.sw_if_index, 1, table_id) + def disable_ipv6_ra(self): """Configure IPv6 RA suppress on the VPP interface""" self.test.vapi.sw_interface_ra_suppress(self.sw_if_index) @@ -238,3 +250,8 @@ class VppInterface(object): self.sub_if.append(sub_if) else: self.sub_if = sub_if + + def enable_mpls(self): + """Enable MPLS on the VPP interface""" + self.test.vapi.sw_interface_enable_disable_mpls( + self.sw_if_index) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 261a0f4a..f0eb410b 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -106,6 +106,18 @@ class VppPapiProvider(object): args = (0, b'') return self.api(vpp_papi.sw_interface_dump, args) + def sw_interface_set_table(self, sw_if_index, is_ipv6, table_id): + """ + Set the IPvX Table-id for the Interface + + :param sw_if_index: + :param is_ipv6: + :param table_id: + + """ + return self.api(vpp_papi.sw_interface_set_table, + (sw_if_index, is_ipv6, table_id)) + def sw_interface_add_del_address(self, sw_if_index, addr, addr_len, is_ipv6=0, is_add=1, del_all=0): """ @@ -121,6 +133,17 @@ class VppPapiProvider(object): return self.api(vpp_papi.sw_interface_add_del_address, (sw_if_index, is_add, is_ipv6, del_all, addr_len, addr)) + def sw_interface_enable_disable_mpls(self, sw_if_index, + is_enable=1): + """ + Enable/Disable MPLS on the interface + :param sw_if_index: + :param is_enable: (Default value = 1) + + """ + return self.api(vpp_papi.sw_interface_set_mpls_enable, + (sw_if_index, is_enable)) + def sw_interface_ra_suppress(self, sw_if_index): suppress = 1 managed = 0 -- cgit 1.2.3-korg