aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorNeale Ranns <nranns@cisco.com>2017-10-21 06:34:22 -0700
committerJohn Lo <loj@cisco.com>2017-12-20 16:26:04 +0000
commit55d03788290d51c79686218ef5de2be8ff6ce976 (patch)
treea242784d0b1ff9817f476dbc9b0a199cf848caea /test
parent4c8a45491dc38a1075881be225df3be14801ffe9 (diff)
L2 Emulation
L2 Emulation is a feautre that is applied to L2 ports to 'extract' IP packets from the L2 path and inject them into the L3 path (i.e. into the appropriate ip[4|6]_input node). L3 routes in the table_id for that interface should then be configured as DVR routes, therefore the forwarded packet has the L2 header preserved and togehter the L3 routed system behaves like an L2 bridge. Change-Id: I8effd7e2f4c67ee277b73c7bc79aa3e5a3e34d03 Signed-off-by: Neale Ranns <nranns@cisco.com>
Diffstat (limited to 'test')
-rw-r--r--test/test_dvr.py242
-rw-r--r--test/vpp_papi_provider.py12
2 files changed, 226 insertions, 28 deletions
diff --git a/test/test_dvr.py b/test/test_dvr.py
index f5d5e54a15f..e8a9c04a1bb 100644
--- a/test/test_dvr.py
+++ b/test/test_dvr.py
@@ -5,7 +5,8 @@ import unittest
from framework import VppTestCase, VppTestRunner
from vpp_sub_interface import VppSubInterface, VppDot1QSubint
-from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto
+from vpp_ip_route import VppIpRoute, VppRoutePath, DpoProto, VppIpMRoute, \
+ VppMRoutePath, MRouteEntryFlags, MRouteItfFlags
from vpp_papi_provider import L2_VTR_OP
from scapy.packet import Raw
@@ -35,6 +36,39 @@ class TestDVR(VppTestCase):
super(TestDVR, self).tearDown()
+ def send_and_assert_no_replies(self, intf, pkts):
+ self.vapi.cli("clear trace")
+ intf.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ for i in self.pg_interfaces:
+ i.get_capture(0)
+ i.assert_nothing_captured()
+
+ def send_and_expect(self, input, pkts, output):
+ self.vapi.cli("clear trace")
+ input.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ rx = output.get_capture(len(pkts))
+ return rx
+
+ def assert_same_mac_addr(self, tx, rx):
+ t_eth = tx[Ether]
+ for p in rx:
+ r_eth = p[Ether]
+ self.assertEqual(t_eth.src, r_eth.src)
+ self.assertEqual(t_eth.dst, r_eth.dst)
+
+ def assert_has_vlan_tag(self, tag, rx):
+ for p in rx:
+ r_1q = p[Dot1Q]
+ self.assertEqual(tag, r_1q.vlan)
+
+ def assert_has_no_tag(self, rx):
+ for p in rx:
+ self.assertFalse(p.haslayer(Dot1Q))
+
def test_dvr(self):
""" Distributed Virtual Router """
@@ -110,27 +144,20 @@ class TestDVR(VppTestCase):
#
# Add routes to bridge the traffic via a tagged interface
#
- route_no_tag = VppIpRoute(
+ route_with_tag = VppIpRoute(
self, ip_tag_bridged, 32,
[VppRoutePath("0.0.0.0",
sub_if_on_pg3.sw_if_index,
proto=DpoProto.DPO_PROTO_ETHERNET)])
- route_no_tag.add_vpp_config()
+ route_with_tag.add_vpp_config()
#
# Inject the packet that arrives and leaves on a non-tagged interface
# Since it's 'bridged' expect that the MAC headed is unchanged.
#
- self.pg0.add_stream(pkt_tag)
-
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
-
- rx = self.pg3.get_capture(1)
-
- self.assertEqual(rx[0][Ether].dst, pkt_tag[Ether].dst)
- self.assertEqual(rx[0][Ether].src, pkt_tag[Ether].src)
- self.assertEqual(rx[0][Dot1Q].vlan, 93)
+ rx = self.send_and_expect(self.pg0, pkt_tag * 65, self.pg3)
+ self.assert_same_mac_addr(pkt_tag, rx)
+ self.assert_has_vlan_tag(93, rx)
#
# Tag to tag
@@ -143,14 +170,9 @@ class TestDVR(VppTestCase):
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
- self.pg2.add_stream(pkt_tag_to_tag)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- rx = self.pg3.get_capture(1)
-
- self.assertEqual(rx[0][Ether].dst, pkt_tag_to_tag[Ether].dst)
- self.assertEqual(rx[0][Ether].src, pkt_tag_to_tag[Ether].src)
- self.assertEqual(rx[0][Dot1Q].vlan, 93)
+ rx = self.send_and_expect(self.pg2, pkt_tag_to_tag * 65, self.pg3)
+ self.assert_same_mac_addr(pkt_tag_to_tag, rx)
+ self.assert_has_vlan_tag(93, rx)
#
# Tag to non-Tag
@@ -163,14 +185,178 @@ class TestDVR(VppTestCase):
UDP(sport=1234, dport=1234) /
Raw('\xa5' * 100))
- self.pg2.add_stream(pkt_tag_to_non_tag)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
- rx = self.pg1.get_capture(1)
+ rx = self.send_and_expect(self.pg2, pkt_tag_to_non_tag * 65, self.pg1)
+ self.assert_same_mac_addr(pkt_tag_to_tag, rx)
+ self.assert_has_no_tag(rx)
+
+ #
+ # cleanup
+ #
+ self.vapi.sw_interface_set_l2_bridge(self.pg0.sw_if_index, 1,
+ enable=0)
+ self.vapi.sw_interface_set_l2_bridge(self.pg1.sw_if_index, 1,
+ enable=0)
+ self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg2.sw_if_index,
+ 1, enable=0)
+ self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index,
+ 1, enable=0)
+ self.vapi.sw_interface_set_l2_bridge(self.loop0.sw_if_index,
+ 1, bvi=1, enable=0)
+
+ #
+ # the explicit route delete is require so it happens before
+ # the sbu-interface delete. subinterface delete is required
+ # because that object type does not use the object registry
+ #
+ route_no_tag.remove_vpp_config()
+ route_with_tag.remove_vpp_config()
+ sub_if_on_pg3.remove_vpp_config()
+ sub_if_on_pg2.remove_vpp_config()
+
+ def test_l2_emulation(self):
+ """ L2 Emulation """
+
+ #
+ # non distinct L3 packets, in the tag/non-tag combos
+ #
+ pkt_no_tag = (Ether(src=self.pg0.remote_mac,
+ dst=self.pg1.remote_mac) /
+ IP(src="2.2.2.2",
+ dst="1.1.1.1") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+ pkt_to_tag = (Ether(src=self.pg0.remote_mac,
+ dst=self.pg2.remote_mac) /
+ IP(src="2.2.2.2",
+ dst="1.1.1.2") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+ pkt_from_tag = (Ether(src=self.pg3.remote_mac,
+ dst=self.pg2.remote_mac) /
+ Dot1Q(vlan=93) /
+ IP(src="2.2.2.2",
+ dst="1.1.1.1") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+ pkt_from_to_tag = (Ether(src=self.pg3.remote_mac,
+ dst=self.pg2.remote_mac) /
+ Dot1Q(vlan=93) /
+ IP(src="2.2.2.2",
+ dst="1.1.1.2") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+ pkt_bcast = (Ether(src=self.pg0.remote_mac,
+ dst="ff:ff:ff:ff:ff:ff") /
+ IP(src="2.2.2.2",
+ dst="255.255.255.255") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+
+ #
+ # A couple of sub-interfaces for tags
+ #
+ sub_if_on_pg2 = VppDot1QSubint(self, self.pg2, 92)
+ sub_if_on_pg3 = VppDot1QSubint(self, self.pg3, 93)
+ sub_if_on_pg2.admin_up()
+ sub_if_on_pg3.admin_up()
+
+ #
+ # Put all the interfaces into a new bridge domain
+ #
+ self.vapi.sw_interface_set_l2_bridge(self.pg0.sw_if_index, 1)
+ self.vapi.sw_interface_set_l2_bridge(self.pg1.sw_if_index, 1)
+ self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg2.sw_if_index, 1)
+ self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index, 1)
+ self.vapi.sw_interface_set_l2_tag_rewrite(sub_if_on_pg2.sw_if_index,
+ L2_VTR_OP.L2_POP_1,
+ 92)
+ self.vapi.sw_interface_set_l2_tag_rewrite(sub_if_on_pg3.sw_if_index,
+ L2_VTR_OP.L2_POP_1,
+ 93)
+
+ #
+ # Disable UU flooding, learning and ARM terminaation. makes this test
+ # easier as unicast packets are dropped if not extracted.
+ #
+ self.vapi.bridge_flags(1, 0, (1 << 0) | (1 << 3) | (1 << 4))
+
+ #
+ # Add a DVR route to steer traffic at L3
+ #
+ route_1 = VppIpRoute(self, "1.1.1.1", 32,
+ [VppRoutePath("0.0.0.0",
+ self.pg1.sw_if_index,
+ proto=DpoProto.DPO_PROTO_ETHERNET)])
+ route_2 = VppIpRoute(self, "1.1.1.2", 32,
+ [VppRoutePath("0.0.0.0",
+ sub_if_on_pg2.sw_if_index,
+ proto=DpoProto.DPO_PROTO_ETHERNET)])
+ route_1.add_vpp_config()
+ route_2.add_vpp_config()
+
+ #
+ # packets are dropped because bridge does not flood unkown unicast
+ #
+ self.send_and_assert_no_replies(self.pg0, pkt_no_tag)
+
+ #
+ # Enable L3 extraction on pgs
+ #
+ self.vapi.sw_interface_set_l2_emulation(self.pg0.sw_if_index)
+ self.vapi.sw_interface_set_l2_emulation(self.pg1.sw_if_index)
+ self.vapi.sw_interface_set_l2_emulation(sub_if_on_pg2.sw_if_index)
+ self.vapi.sw_interface_set_l2_emulation(sub_if_on_pg3.sw_if_index)
+
+ #
+ # now we expect the packet forward according to the DVR route
+ #
+ rx = self.send_and_expect(self.pg0, pkt_no_tag * 65, self.pg1)
+ self.assert_same_mac_addr(pkt_no_tag, rx)
+ self.assert_has_no_tag(rx)
+
+ rx = self.send_and_expect(self.pg0, pkt_to_tag * 65, self.pg2)
+ self.assert_same_mac_addr(pkt_to_tag, rx)
+ self.assert_has_vlan_tag(92, rx)
+
+ rx = self.send_and_expect(self.pg3, pkt_from_tag * 65, self.pg1)
+ self.assert_same_mac_addr(pkt_from_tag, rx)
+ self.assert_has_no_tag(rx)
+
+ rx = self.send_and_expect(self.pg3, pkt_from_to_tag * 65, self.pg2)
+ self.assert_same_mac_addr(pkt_from_tag, rx)
+ self.assert_has_vlan_tag(92, rx)
+
+ #
+ # but broadcast packets are still flooded
+ #
+ self.send_and_expect(self.pg0, pkt_bcast * 33, self.pg2)
+
+ #
+ # cleanup
+ #
+ self.vapi.sw_interface_set_l2_emulation(self.pg0.sw_if_index,
+ enable=0)
+ self.vapi.sw_interface_set_l2_emulation(self.pg1.sw_if_index,
+ enable=0)
+ self.vapi.sw_interface_set_l2_emulation(sub_if_on_pg2.sw_if_index,
+ enable=0)
+ self.vapi.sw_interface_set_l2_emulation(sub_if_on_pg3.sw_if_index,
+ enable=0)
+
+ self.vapi.sw_interface_set_l2_bridge(self.pg0.sw_if_index,
+ 1, enable=0)
+ self.vapi.sw_interface_set_l2_bridge(self.pg1.sw_if_index,
+ 1, enable=0)
+ self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg2.sw_if_index,
+ 1, enable=0)
+ self.vapi.sw_interface_set_l2_bridge(sub_if_on_pg3.sw_if_index,
+ 1, enable=0)
+
+ route_1.remove_vpp_config()
+ route_2.remove_vpp_config()
+ sub_if_on_pg3.remove_vpp_config()
+ sub_if_on_pg2.remove_vpp_config()
- self.assertEqual(rx[0][Ether].dst, pkt_tag_to_tag[Ether].dst)
- self.assertEqual(rx[0][Ether].src, pkt_tag_to_tag[Ether].src)
- self.assertFalse(rx[0].haslayer(Dot1Q))
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index aa06cfef4e9..a03677970bb 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -603,6 +603,18 @@ class VppPapiProvider(object):
'tag1': tag1,
'tag2': tag2})
+ def sw_interface_set_l2_emulation(
+ self,
+ sw_if_index,
+ enable=1):
+ """L2 Emulation
+ :param sw_if_index - interface the operation is applied to
+
+ """
+ return self.api(self.papi.l2_emulation,
+ {'sw_if_index': sw_if_index,
+ 'enable': enable})
+
def sw_interface_set_flags(self, sw_if_index, admin_up_down):
"""