From 177bbdcd8fa4e7621c5bdd3afd8c6e74b603e096 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Tue, 15 Nov 2016 09:46:51 +0000 Subject: GRE tests and fixes Change-Id: I234240e9bdd4b69ad64a17b1449ae1e81c0edaca Signed-off-by: Neale Ranns --- test/Makefile | 30 +- test/framework.py | 2 +- test/patches/scapy-2.3.3/gre-layers.patch | 25 ++ test/test_gre.py | 700 ++++++++++++++++++++++++++++++ test/vpp_gre_interface.py | 34 ++ test/vpp_interface.py | 31 ++ test/vpp_ip_route.py | 46 ++ test/vpp_papi_provider.py | 51 +++ test/vpp_sub_interface.py | 3 + 9 files changed, 917 insertions(+), 5 deletions(-) create mode 100644 test/patches/scapy-2.3.3/gre-layers.patch create mode 100644 test/test_gre.py create mode 100644 test/vpp_gre_interface.py create mode 100644 test/vpp_ip_route.py (limited to 'test') diff --git a/test/Makefile b/test/Makefile index aac637d3e5f..de6aaa7a793 100644 --- a/test/Makefile +++ b/test/Makefile @@ -6,23 +6,45 @@ ifndef VPP_PYTHON_PREFIX endif PYTHON_VENV_PATH=$(VPP_PYTHON_PREFIX)/virtualenv -PYTHON_DEPENDS=scapy pexpect +PYTHON_DEPENDS=scapy==2.3.3 pexpect +SCAPY_SOURCE=$(WS_ROOT)/build-root/python/virtualenv/lib/python2.7/site-packages/ -test: wipe verify-python-path + +.pip-install.ok: @virtualenv $(PYTHON_VENV_PATH) @bash -c "source $(PYTHON_VENV_PATH)/bin/activate && pip install $(PYTHON_DEPENDS)" + @touch $@ + +.pip-patch.ok: .pip-install.ok + @echo --- patching --- + for f in $(CURDIR)/patches/scapy-2.3.3/*.patch ; do \ + echo Applying patch: $$(basename $$f) ; \ + patch -p1 -d $(SCAPY_SOURCE) < $$f ; \ + done + @touch $@ + +.install.ok: .pip-patch.ok @bash -c "source $(PYTHON_VENV_PATH)/bin/activate && cd $(WS_ROOT)/vpp-api/python && python setup.py install" + @touch $@ + +PHONIES=.install.ok .pip-patch.ok .pip-install.ok +.PHONY: $(PHONIES) + +test: reset verify-python-path .install.ok @bash -c "source $(PYTHON_VENV_PATH)/bin/activate && python run_tests.py discover -p test_$(TEST)\"*.py\"" -retest: wipe verify-python-path +retest: reset verify-python-path @bash -c "source $(PYTHON_VENV_PATH)/bin/activate && python run_tests.py discover -p test_$(TEST)\"*.py\"" .PHONY: wipe doc -wipe: +reset: @rm -f /dev/shm/vpp-unittest-* @rm -rf /tmp/vpp-unittest-* +wipe: reset + @rm -f $(PHONIES) + doc: verify-python-path @virtualenv $(PYTHON_VENV_PATH) @bash -c "source $(PYTHON_VENV_PATH)/bin/activate && pip install $(PYTHON_DEPENDS) sphinx" diff --git a/test/framework.py b/test/framework.py index 227428ef9c2..1375f076dd6 100644 --- a/test/framework.py +++ b/test/framework.py @@ -265,8 +265,8 @@ class VppTestCase(unittest.TestCase): def tearDown(self): """ Show various debug prints after each test """ if not self.vpp_dead: - self.logger.info(self.vapi.ppcli("show int")) self.logger.debug(self.vapi.cli("show trace")) + self.logger.info(self.vapi.ppcli("show int")) self.logger.info(self.vapi.ppcli("show hardware")) self.logger.info(self.vapi.ppcli("show error")) self.logger.info(self.vapi.ppcli("show run")) diff --git a/test/patches/scapy-2.3.3/gre-layers.patch b/test/patches/scapy-2.3.3/gre-layers.patch new file mode 100644 index 00000000000..605a705b0f7 --- /dev/null +++ b/test/patches/scapy-2.3.3/gre-layers.patch @@ -0,0 +1,25 @@ +diff --git a/scapy/layers/inet6.py b/scapy/layers/inet6.py +index 03b80ec..a7e1e0f 100644 +--- a/scapy/layers/inet6.py ++++ b/scapy/layers/inet6.py +@@ -3722,6 +3722,7 @@ conf.l2types.register(31, IPv6) + + bind_layers(Ether, IPv6, type = 0x86dd ) + bind_layers(CookedLinux, IPv6, proto = 0x86dd ) ++bind_layers(GRE, IPv6, proto = 0x86dd ) + bind_layers(IPerror6, TCPerror, nh = socket.IPPROTO_TCP ) + bind_layers(IPerror6, UDPerror, nh = socket.IPPROTO_UDP ) + bind_layers(IPv6, TCP, nh = socket.IPPROTO_TCP ) +diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py +index 4f491d2..661a5da 100644 +--- a/scapy/layers/l2.py ++++ b/scapy/layers/l2.py +@@ -628,7 +628,7 @@ bind_layers( CookedLinux, EAPOL, proto=34958) + bind_layers( GRE, LLC, proto=122) + bind_layers( GRE, Dot1Q, proto=33024) + bind_layers( GRE, Dot1AD, type=0x88a8) +-bind_layers( GRE, Ether, proto=1) ++bind_layers( GRE, Ether, proto=0x6558) + bind_layers( GRE, ARP, proto=2054) + bind_layers( GRE, EAPOL, proto=34958) + bind_layers( GRE, GRErouting, { "routing_present" : 1 } ) diff --git a/test/test_gre.py b/test/test_gre.py new file mode 100644 index 00000000000..b5a1e346eca --- /dev/null +++ b/test/test_gre.py @@ -0,0 +1,700 @@ +#!/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 vpp_gre_interface import VppGreInterface +from vpp_ip_route import IpRoute, IpPath +from vpp_papi_provider import L2_VTR_OP + +from scapy.packet import Raw +from scapy.layers.l2 import Ether, Dot1Q, ARP, GRE +from scapy.layers.inet import IP, UDP +from scapy.layers.inet6 import ICMPv6ND_NS, IPv6, UDP +from scapy.contrib.mpls import MPLS +from scapy.volatile import RandMAC, RandIP + + +class TestGRE(VppTestCase): + """ GRE Test Case """ + + @classmethod + def setUpClass(cls): + super(TestGRE, cls).setUpClass() + + def setUp(self): + super(TestGRE, self).setUp() + + # create 2 pg interfaces - set one in a non-default table. + self.create_pg_interfaces(range(2)) + + self.pg1.set_table_ip4(1) + for i in self.pg_interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + def tearDown(self): + super(TestGRE, self).tearDown() + + def create_stream_ip4(self, src_if, src_ip, dst_ip): + 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) / + IP(src=src_ip, dst=dst_ip) / + UDP(sport=1234, dport=1234) / + Raw(payload)) + info.data = p.copy() + pkts.append(p) + return pkts + + def create_tunnel_stream_4o4(self, src_if, + tunnel_src, tunnel_dst, + src_ip, dst_ip): + 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) / + IP(src=tunnel_src, dst=tunnel_dst) / + GRE() / + IP(src=src_ip, dst=dst_ip) / + UDP(sport=1234, dport=1234) / + Raw(payload)) + info.data = p.copy() + pkts.append(p) + return pkts + + def create_tunnel_stream_6o4(self, src_if, + tunnel_src, tunnel_dst, + src_ip, dst_ip): + 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) / + IP(src=tunnel_src, dst=tunnel_dst) / + GRE() / + IPv6(src=src_ip, dst=dst_ip) / + UDP(sport=1234, dport=1234) / + Raw(payload)) + info.data = p.copy() + pkts.append(p) + return pkts + + def create_tunnel_stream_l2o4(self, src_if, + tunnel_src, tunnel_dst): + 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) / + IP(src=tunnel_src, dst=tunnel_dst) / + GRE() / + Ether(dst=RandMAC('*:*:*:*:*:*'), + src=RandMAC('*:*:*:*:*:*')) / + IP(src=str(RandIP()), dst=str(RandIP())) / + UDP(sport=1234, dport=1234) / + Raw(payload)) + info.data = p.copy() + pkts.append(p) + return pkts + + def create_tunnel_stream_vlano4(self, src_if, + tunnel_src, tunnel_dst, vlan): + 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) / + IP(src=tunnel_src, dst=tunnel_dst) / + GRE() / + Ether(dst=RandMAC('*:*:*:*:*:*'), + src=RandMAC('*:*:*:*:*:*')) / + Dot1Q(vlan=vlan) / + IP(src=str(RandIP()), dst=str(RandIP())) / + UDP(sport=1234, dport=1234) / + Raw(payload)) + info.data = p.copy() + pkts.append(p) + return pkts + + def verify_filter(self, capture, sent): + if not len(capture) == len(sent): + # filter out any IPv6 RAs from the captur + for p in capture: + if (p.haslayer(ICMPv6ND_RA)): + capture.remove(p) + return capture + + def verify_tunneled_4o4(self, src_if, capture, sent, + tunnel_src, tunnel_dst): + + capture = self.verify_filter(capture, sent) + self.assertEqual(len(capture), len(sent)) + + for i in range(len(capture)): + try: + tx = sent[i] + rx = capture[i] + + tx_ip = tx[IP] + rx_ip = rx[IP] + + self.assertEqual(rx_ip.src, tunnel_src) + self.assertEqual(rx_ip.dst, tunnel_dst) + + rx_gre = rx[GRE] + rx_ip = rx_gre[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: + rx.show() + tx.show() + raise + + def verify_tunneled_l2o4(self, src_if, capture, sent, + tunnel_src, tunnel_dst): + capture = self.verify_filter(capture, sent) + self.assertEqual(len(capture), len(sent)) + + for i in range(len(capture)): + try: + tx = sent[i] + rx = capture[i] + + tx_ip = tx[IP] + rx_ip = rx[IP] + + self.assertEqual(rx_ip.src, tunnel_src) + self.assertEqual(rx_ip.dst, tunnel_dst) + + rx_gre = rx[GRE] + rx_l2 = rx_gre[Ether] + rx_ip = rx_l2[IP] + tx_gre = tx[GRE] + tx_l2 = tx_gre[Ether] + tx_ip = tx_l2[IP] + + self.assertEqual(rx_ip.src, tx_ip.src) + self.assertEqual(rx_ip.dst, tx_ip.dst) + # bridged, not L3 forwarded, so no TTL decrement + self.assertEqual(rx_ip.ttl, tx_ip.ttl) + + except: + rx.show() + tx.show() + raise + + def verify_tunneled_vlano4(self, src_if, capture, sent, + tunnel_src, tunnel_dst, vlan): + try: + capture = self.verify_filter(capture, sent) + self.assertEqual(len(capture), len(sent)) + except: + capture.show() + raise + + for i in range(len(capture)): + try: + tx = sent[i] + rx = capture[i] + + tx_ip = tx[IP] + rx_ip = rx[IP] + + self.assertEqual(rx_ip.src, tunnel_src) + self.assertEqual(rx_ip.dst, tunnel_dst) + + rx_gre = rx[GRE] + rx_l2 = rx_gre[Ether] + rx_vlan = rx_l2[Dot1Q] + rx_ip = rx_l2[IP] + + self.assertEqual(rx_vlan.vlan, vlan) + + tx_gre = tx[GRE] + tx_l2 = tx_gre[Ether] + tx_ip = tx_l2[IP] + + self.assertEqual(rx_ip.src, tx_ip.src) + self.assertEqual(rx_ip.dst, tx_ip.dst) + # bridged, not L3 forwarded, so no TTL decrement + self.assertEqual(rx_ip.ttl, tx_ip.ttl) + + except: + rx.show() + tx.show() + raise + + def verify_decapped_4o4(self, src_if, capture, sent): + capture = self.verify_filter(capture, sent) + self.assertEqual(len(capture), len(sent)) + + for i in range(len(capture)): + try: + tx = sent[i] + rx = capture[i] + + tx_ip = tx[IP] + rx_ip = rx[IP] + tx_gre = tx[GRE] + tx_ip = tx_gre[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: + rx.show() + tx.show() + raise + + def verify_decapped_6o4(self, src_if, capture, sent): + capture = self.verify_filter(capture, sent) + self.assertEqual(len(capture), len(sent)) + + for i in range(len(capture)): + try: + tx = sent[i] + rx = capture[i] + + tx_ip = tx[IP] + rx_ip = rx[IPv6] + tx_gre = tx[GRE] + tx_ip = tx_gre[IPv6] + + self.assertEqual(rx_ip.src, tx_ip.src) + self.assertEqual(rx_ip.dst, tx_ip.dst) + self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim) + + except: + rx.show() + tx.show() + raise + + def test_gre(self): + """ GRE tunnel Tests """ + + # + # Create an L3 GRE tunnel. + # - set it admin up + # - assign an IP Addres + # - Add a route via the tunnel + # + gre_if = VppGreInterface(self, + self.pg0.local_ip4, + "1.1.1.2") + gre_if.add_vpp_config() + + # + # The double create (create the same tunnel twice) should fail, + # and we should still be able to use the original + # + try: + gre_if.add_vpp_config() + except Exception: + pass + else: + self.fail("Double GRE tunnel add does not fail") + + gre_if.admin_up() + gre_if.config_ip4() + + route_via_tun = IpRoute(self, "4.4.4.4", 32, + [IpPath("0.0.0.0", gre_if.sw_if_index)]) + + route_via_tun.add_vpp_config() + + # + # Send a packet stream that is routed into the tunnel + # - they are all dropped since the tunnel's desintation IP + # is unresolved - or resolves via the default route - which + # which is a drop. + # + tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "4.4.4.4") + 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("GRE packets forwarded without DIP resolved") + error(rx.show()) + raise + + # + # Add a route that resolves the tunnel's destination + # + route_tun_dst = IpRoute(self, "1.1.1.2", 32, + [IpPath(self.pg0.remote_ip4, self.pg0.sw_if_index)]) + route_tun_dst.add_vpp_config() + + # + # Send a packet stream that is routed into the tunnel + # - packets are GRE encapped + # + self.vapi.cli("clear trace") + tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "4.4.4.4") + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_tunneled_4o4(self.pg0, rx, tx, + self.pg0.local_ip4, "1.1.1.2") + + # + # Send tunneled packets that match the created tunnel and + # are decapped and forwarded + # + self.vapi.cli("clear trace") + tx = self.create_tunnel_stream_4o4(self.pg0, + "1.1.1.2", + self.pg0.local_ip4, + self.pg0.local_ip4, + self.pg0.remote_ip4) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_decapped_4o4(self.pg0, rx, tx) + + # + # Send tunneled packets that do not match the tunnel's src + # + self.vapi.cli("clear trace") + tx = self.create_tunnel_stream_4o4(self.pg0, + "1.1.1.3", + self.pg0.local_ip4, + self.pg0.local_ip4, + self.pg0.remote_ip4) + 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("GRE packets forwarded despite no SRC address match") + error(rx.show()) + raise + + # + # Configure IPv6 on the PG interface so we can route IPv6 + # packets + # + self.pg0.config_ip6() + self.pg0.resolve_ndp() + + # + # Send IPv6 tunnel encapslated packets + # - dropped since IPv6 is not enabled on the tunnel + # + self.vapi.cli("clear trace") + tx = self.create_tunnel_stream_6o4(self.pg0, + "1.1.1.2", + self.pg0.local_ip4, + self.pg0.local_ip6, + self.pg0.remote_ip6) + 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("IPv6 GRE packets forwarded despite IPv6 not enabled on tunnel") + error(rx.show()) + raise + + # + # Enable IPv6 on the tunnel + # + gre_if.config_ip6() + + # + # Send IPv6 tunnel encapslated packets + # - forwarded since IPv6 is enabled on the tunnel + # + self.vapi.cli("clear trace") + tx = self.create_tunnel_stream_6o4(self.pg0, + "1.1.1.2", + self.pg0.local_ip4, + self.pg0.local_ip6, + self.pg0.remote_ip6) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_decapped_6o4(self.pg0, rx, tx) + + # + # test case cleanup + # + route_tun_dst.remove_vpp_config() + route_via_tun.remove_vpp_config() + gre_if.remove_vpp_config() + + self.pg0.unconfig_ip6() + + def test_gre_vrf(self): + """ GRE tunnel VRF Tests """ + + # + # Create an L3 GRE tunnel whose destination is in the non-default + # table. The underlay is thus non-default - the overlay is still + # the default. + # - set it admin up + # - assign an IP Addres + # + gre_if = VppGreInterface(self, self.pg1.local_ip4, + "2.2.2.2", + outer_fib_id=1) + gre_if.add_vpp_config() + gre_if.admin_up() + gre_if.config_ip4() + + # + # Add a route via the tunnel - in the overlay + # + route_via_tun = IpRoute(self, "9.9.9.9", 32, + [IpPath("0.0.0.0", gre_if.sw_if_index)]) + route_via_tun.add_vpp_config() + + # + # Add a route that resolves the tunnel's destination - in the + # underlay table + # + route_tun_dst = IpRoute(self, "2.2.2.2", 32, table_id=1, + paths=[IpPath(self.pg1.remote_ip4, + self.pg1.sw_if_index)]) + route_tun_dst.add_vpp_config() + + # + # Send a packet stream that is routed into the tunnel + # packets are sent in on pg0 which is in the default table + # - packets are GRE encapped + # + self.vapi.cli("clear trace") + tx = self.create_stream_ip4(self.pg0, "5.5.5.5", "9.9.9.9") + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg1.get_capture() + self.verify_tunneled_4o4(self.pg1, rx, tx, + self.pg1.local_ip4, "2.2.2.2") + + # + # Send tunneled packets that match the created tunnel and + # are decapped and forwarded. This tests the decap lookup + # does not happen in the encap table + # + self.vapi.cli("clear trace") + tx = self.create_tunnel_stream_4o4(self.pg1, + "2.2.2.2", + self.pg1.local_ip4, + self.pg0.local_ip4, + self.pg0.remote_ip4) + self.pg1.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_decapped_4o4(self.pg0, rx, tx) + + # + # test case cleanup + # + route_tun_dst.remove_vpp_config() + route_via_tun.remove_vpp_config() + gre_if.remove_vpp_config() + + def test_gre_l2(self): + """ GRE tunnel L2 Tests """ + + # + # Add routes to resolve the tunnel destinations + # + route_tun1_dst = IpRoute(self, "2.2.2.2", 32, + [IpPath(self.pg0.remote_ip4, + self.pg0.sw_if_index)]) + route_tun2_dst = IpRoute(self, "2.2.2.3", 32, + [IpPath(self.pg0.remote_ip4, + self.pg0.sw_if_index)]) + + route_tun1_dst.add_vpp_config() + route_tun2_dst.add_vpp_config() + + # + # Create 2 L2 GRE tunnels and x-connect them + # + gre_if1 = VppGreInterface(self, self.pg0.local_ip4, + "2.2.2.2", + is_teb=1) + gre_if2 = VppGreInterface(self, self.pg0.local_ip4, + "2.2.2.3", + is_teb=1) + gre_if1.add_vpp_config() + gre_if2.add_vpp_config() + + gre_if1.admin_up() + gre_if2.admin_up() + + self.vapi.sw_interface_set_l2_xconnect(gre_if1.sw_if_index, + gre_if2.sw_if_index, + enable=1) + self.vapi.sw_interface_set_l2_xconnect(gre_if2.sw_if_index, + gre_if1.sw_if_index, + enable=1) + + # + # Send in tunnel encapped L2. expect out tunnel encapped L2 + # in both directions + # + self.vapi.cli("clear trace") + tx = self.create_tunnel_stream_l2o4(self.pg0, + "2.2.2.2", + self.pg0.local_ip4) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_tunneled_l2o4(self.pg0, rx, tx, + self.pg0.local_ip4, + "2.2.2.3") + + self.vapi.cli("clear trace") + tx = self.create_tunnel_stream_l2o4(self.pg0, + "2.2.2.3", + self.pg0.local_ip4) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_tunneled_l2o4(self.pg0, rx, tx, + self.pg0.local_ip4, + "2.2.2.2") + + self.vapi.sw_interface_set_l2_xconnect(gre_if1.sw_if_index, + gre_if2.sw_if_index, + enable=0) + self.vapi.sw_interface_set_l2_xconnect(gre_if2.sw_if_index, + gre_if1.sw_if_index, + enable=0) + + # + # Create a VLAN sub-interfaces on the GRE TEB interfaces + # then x-connect them + # + gre_if_11 = VppDot1QSubint(self, gre_if1, 11) + gre_if_12 = VppDot1QSubint(self, gre_if2, 12) + + # gre_if_11.add_vpp_config() + # gre_if_12.add_vpp_config() + + gre_if_11.admin_up() + gre_if_12.admin_up() + + self.vapi.sw_interface_set_l2_xconnect(gre_if_11.sw_if_index, + gre_if_12.sw_if_index, + enable=1) + self.vapi.sw_interface_set_l2_xconnect(gre_if_12.sw_if_index, + gre_if_11.sw_if_index, + enable=1) + + # + # Configure both to pop thier respective VLAN tags, + # so that during the x-coonect they will subsequently push + # + self.vapi.sw_interface_set_l2_tag_rewrite(gre_if_12.sw_if_index, + L2_VTR_OP.L2_POP_1, + 12) + self.vapi.sw_interface_set_l2_tag_rewrite(gre_if_11.sw_if_index, + L2_VTR_OP.L2_POP_1, + 11) + + # + # Send traffic in both directiond - expect the VLAN tags to + # be swapped. + # + self.vapi.cli("clear trace") + tx = self.create_tunnel_stream_vlano4(self.pg0, + "2.2.2.2", + self.pg0.local_ip4, + 11) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_tunneled_vlano4(self.pg0, rx, tx, + self.pg0.local_ip4, + "2.2.2.3", + 12) + + self.vapi.cli("clear trace") + tx = self.create_tunnel_stream_vlano4(self.pg0, + "2.2.2.3", + self.pg0.local_ip4, + 12) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_tunneled_vlano4(self.pg0, rx, tx, + self.pg0.local_ip4, + "2.2.2.2", + 11) + + # + # Cleanup Test resources + # + gre_if_11.remove_vpp_config() + gre_if_12.remove_vpp_config() + gre_if1.remove_vpp_config() + gre_if2.remove_vpp_config() + route_tun1_dst.add_vpp_config() + route_tun2_dst.add_vpp_config() + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_gre_interface.py b/test/vpp_gre_interface.py new file mode 100644 index 00000000000..b6a66332a99 --- /dev/null +++ b/test/vpp_gre_interface.py @@ -0,0 +1,34 @@ + +from vpp_interface import VppInterface +import socket + + +class VppGreInterface(VppInterface): + """ + VPP GRE interface + """ + + def __init__(self, test, src_ip, dst_ip, outer_fib_id=0, is_teb=0): + """ Create VPP loopback interface """ + self._test = test + self.t_src = src_ip + self.t_dst = dst_ip + self.t_outer_fib = outer_fib_id + self.t_is_teb = is_teb + + def add_vpp_config(self): + s = socket.inet_pton(socket.AF_INET, self.t_src) + d = socket.inet_pton(socket.AF_INET, self.t_dst) + r = self.test.vapi.gre_tunnel_add_del(s, d, + outer_fib_id=self.t_outer_fib, + is_teb=self.t_is_teb) + self._sw_if_index = r.sw_if_index + self.post_init_setup() + + def remove_vpp_config(self): + s = socket.inet_pton(socket.AF_INET, self.t_src) + d = socket.inet_pton(socket.AF_INET, self.t_dst) + self.unconfig() + r = self.test.vapi.gre_tunnel_add_del(s, d, + outer_fib_id=self.t_outer_fib, + is_add=0) diff --git a/test/vpp_interface.py b/test/vpp_interface.py index 30ef8ae7a0a..511cf4bc2dd 100644 --- a/test/vpp_interface.py +++ b/test/vpp_interface.py @@ -176,6 +176,19 @@ class VppInterface(object): addr_len = 24 self.test.vapi.sw_interface_add_del_address( self.sw_if_index, addr, addr_len) + self.has_ip4_config = True + + def unconfig_ip4(self): + """Remove IPv4 address on the VPP interface""" + try: + if (self.has_ip4_config): + self.test.vapi.sw_interface_add_del_address( + self.sw_if_index, + self.local_ip4n, + 24, is_add=0) + except AttributeError: + self.has_ip4_config = False + self.has_ip4_config = False def configure_ipv4_neighbors(self): """For every remote host assign neighbor's MAC to IPv4 addresses.""" @@ -190,6 +203,24 @@ class VppInterface(object): addr_len = 64 self.test.vapi.sw_interface_add_del_address( self.sw_if_index, addr, addr_len, is_ipv6=1) + self.has_ip6_config = True + + def unconfig_ip6(self): + """Remove IPv6 address on the VPP interface""" + try: + if (self.has_ip6_config): + self.test.vapi.sw_interface_add_del_address( + self.sw_if_index, + self.local_ip6n, + 64, is_ipv6=1, is_add=0) + except AttributeError: + self.has_ip6_config = False + self.has_ip6_config = False + + def unconfig(self): + """Unconfigure IPv6 and IPv4 address on the VPP interface""" + self.unconfig_ip4() + self.unconfig_ip6() def set_table_ip4(self, table_id): """Set the interface in a IPv4 Table. diff --git a/test/vpp_ip_route.py b/test/vpp_ip_route.py new file mode 100644 index 00000000000..78e6aaa23b9 --- /dev/null +++ b/test/vpp_ip_route.py @@ -0,0 +1,46 @@ +""" + IP Routes + + object abstractions for representing IP routes in VPP +""" + +import socket + + +class IpPath: + + def __init__(self, nh_addr, nh_sw_if_index, nh_table_id=0): + self.nh_addr = socket.inet_pton(socket.AF_INET, nh_addr) + self.nh_itf = nh_sw_if_index + self.nh_table_id = nh_table_id + + +class IpRoute: + """ + IP Route + """ + + def __init__(self, test, dest_addr, + dest_addr_len, paths, table_id=0): + self._test = test + self.paths = paths + self.dest_addr = socket.inet_pton(socket.AF_INET, dest_addr) + self.dest_addr_len = dest_addr_len + self.table_id = table_id + + def add_vpp_config(self): + for path in self.paths: + self._test.vapi.ip_add_del_route(self.dest_addr, + self.dest_addr_len, + path.nh_addr, + path.nh_itf, + table_id=self.table_id) + + def remove_vpp_config(self): + for path in self.paths: + self._test.vapi.ip_add_del_route(self.dest_addr, + self.dest_addr_len, + path.nh_addr, + path.nh_itf, + table_id=self.table_id, + is_add=0) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 2148e94b2a8..23a108d9ca2 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -18,6 +18,9 @@ if do_import: MPLS_IETF_MAX_LABEL = 0xfffff MPLS_LABEL_INVALID = MPLS_IETF_MAX_LABEL + 1 +class L2_VTR_OP: + L2_POP_1 = 3 + class VppPapiProvider(object): """VPP-api provider using vpp-papi @@ -230,6 +233,20 @@ class VppPapiProvider(object): return self.api(vpp_papi.sw_interface_set_l2_xconnect, (rx_sw_if_index, tx_sw_if_index, enable)) + def sw_interface_set_l2_tag_rewrite(self, sw_if_index, vtr_oper, push=0, tag1=0, tag2=0): + """L2 interface vlan tag rewrite configure request + :param client_index - opaque cookie to identify the sender + :param context - sender context, to match reply w/ request + :param sw_if_index - interface the operation is applied to + :param vtr_op - Choose from l2_vtr_op_t enum values + :param push_dot1q - first pushed flag dot1q id set, else dot1ad + :param tag1 - Needed for any push or translate vtr op + :param tag2 - Needed for any push 2 or translate x-2 vtr ops + + """ + return self.api(vpp_papi.l2_interface_vlan_tag_rewrite, + (sw_if_index, vtr_oper, push, tag1, tag2)) + def sw_interface_set_flags(self, sw_if_index, admin_up_down, link_up_down=0, deleted=0): """ @@ -278,6 +295,13 @@ class VppPapiProvider(object): outer_vlan, inner_vlan)) + def delete_subif(self, sw_if_index): + """Delete subinterface + + :param sw_if_index: + """ + return self.api(vpp_papi.delete_subif, ([sw_if_index])) + def create_vlan_subif(self, sw_if_index, vlan): """ @@ -411,3 +435,30 @@ class VppPapiProvider(object): """ return self.api(vpp_papi.sw_interface_span_enable_disable, (sw_if_index_from, sw_if_index_to, enable )) + + def gre_tunnel_add_del(self, + src_address, + dst_address, + outer_fib_id=0, + is_teb=0, + is_add=1, + is_ip6=0): + """ Add a GRE tunnel + + :param src_address: + :param dst_address: + :param outer_fib_id: (Default value = 0) + :param is_add: (Default value = 1) + :param is_ipv6: (Default value = 0) + :param is_teb: (Default value = 0) + """ + + return self.api( + vpp_papi.gre_add_del_tunnel, + (is_add, + is_ip6, + is_teb, + src_address, + dst_address, + outer_fib_id) + ) diff --git a/test/vpp_sub_interface.py b/test/vpp_sub_interface.py index b387d27b49d..027a24b2a1d 100644 --- a/test/vpp_sub_interface.py +++ b/test/vpp_sub_interface.py @@ -41,6 +41,9 @@ class VppSubInterface(VppPGInterface): def add_dot1_layer(self, pkt): pass + def remove_vpp_config(self): + self.test.vapi.delete_subif(self._sw_if_index) + class VppDot1QSubint(VppSubInterface): -- cgit 1.2.3-korg