diff options
author | Neale Ranns <nranns@cisco.com> | 2016-11-02 14:20:04 +0000 |
---|---|---|
committer | Damjan Marion <dmarion.lists@gmail.com> | 2016-12-02 11:09:36 +0000 |
commit | ad422ed7eaafe993d5b530395cb11a708f2ed922 (patch) | |
tree | 6122391863894f619d13037581f8365ca3796f8f /test | |
parent | caffe0980adc852e6f6afaa2723bd5dd14658de3 (diff) |
MPLS infrastructure improvments
- deprecate MPLSoEth and MPLSoGRE; replace with generic MPLS tunnel.
- deprecates CLI 'mpls encap ..'; replace with addition of MPLS out label to a route/tunnel.
- support for MPLS 'routes', e.g. MPLS x-connects.
- deprecates CLI 'mpls decap ..'; replace with 'mpls route .. '
Change-Id: Ibda46544912f880d0200f22bf9ff9b52828fcc2f
Signed-off-by: Neale Ranns <nranns@cisco.com>
Diffstat (limited to 'test')
-rw-r--r-- | test/patches/scapy-2.3.3/mpls.py.patch | 13 | ||||
-rw-r--r-- | test/test_gre.py | 21 | ||||
-rw-r--r-- | test/test_mpls.py | 522 | ||||
-rw-r--r-- | test/vpp_ip_route.py | 75 | ||||
-rw-r--r-- | test/vpp_papi_provider.py | 183 |
5 files changed, 767 insertions, 47 deletions
diff --git a/test/patches/scapy-2.3.3/mpls.py.patch b/test/patches/scapy-2.3.3/mpls.py.patch new file mode 100644 index 00000000..5c819110 --- /dev/null +++ b/test/patches/scapy-2.3.3/mpls.py.patch @@ -0,0 +1,13 @@ +diff --git a/scapy/contrib/mpls.py b/scapy/contrib/mpls.py +index 640a0c5..6af1d4a 100644 +--- a/scapy/contrib/mpls.py ++++ b/scapy/contrib/mpls.py +@@ -18,6 +18,8 @@ class MPLS(Packet): + + def guess_payload_class(self, payload): + if len(payload) >= 1: ++ if not self.s: ++ return MPLS + ip_version = (ord(payload[0]) >> 4) & 0xF + if ip_version == 4: + return IP diff --git a/test/test_gre.py b/test/test_gre.py index f54b6e01..0b508285 100644 --- a/test/test_gre.py +++ b/test/test_gre.py @@ -7,7 +7,7 @@ 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_ip_route import IpRoute, RoutePath from vpp_papi_provider import L2_VTR_OP from scapy.packet import Raw @@ -317,7 +317,7 @@ class TestGRE(VppTestCase): gre_if.config_ip4() route_via_tun = IpRoute(self, "4.4.4.4", 32, - [IpPath("0.0.0.0", gre_if.sw_if_index)]) + [RoutePath("0.0.0.0", gre_if.sw_if_index)]) route_via_tun.add_vpp_config() @@ -346,7 +346,8 @@ class TestGRE(VppTestCase): # 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)]) + [RoutePath(self.pg0.remote_ip4, + self.pg0.sw_if_index)]) route_tun_dst.add_vpp_config() # @@ -487,7 +488,7 @@ class TestGRE(VppTestCase): # 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)]) + [RoutePath("0.0.0.0", gre_if.sw_if_index)]) route_via_tun.add_vpp_config() # @@ -495,8 +496,8 @@ class TestGRE(VppTestCase): # 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)]) + paths=[RoutePath(self.pg1.remote_ip4, + self.pg1.sw_if_index)]) route_tun_dst.add_vpp_config() # @@ -548,11 +549,11 @@ class TestGRE(VppTestCase): # 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)]) + [RoutePath(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)]) + [RoutePath(self.pg0.remote_ip4, + self.pg0.sw_if_index)]) route_tun1_dst.add_vpp_config() route_tun2_dst.add_vpp_config() diff --git a/test/test_mpls.py b/test/test_mpls.py index 45af4704..d1b1b919 100644 --- a/test/test_mpls.py +++ b/test/test_mpls.py @@ -6,6 +6,7 @@ from logging import * from framework import VppTestCase, VppTestRunner from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint +from vpp_ip_route import IpRoute, RoutePath, MplsRoute, MplsIpBind from scapy.packet import Raw from scapy.layers.l2 import Ether, Dot1Q, ARP @@ -13,6 +14,7 @@ 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 """ @@ -24,7 +26,7 @@ class TestMPLS(VppTestCase): super(TestMPLS, self).setUp() # create 2 pg interfaces - self.create_pg_interfaces(range(3)) + self.create_pg_interfaces(range(2)) # setup both interfaces # assign them different tables. @@ -35,31 +37,51 @@ class TestMPLS(VppTestCase): 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.config_ip6() i.resolve_ndp() + i.enable_mpls() table_id += 1 def tearDown(self): super(TestMPLS, self).tearDown() - def create_stream_ip4(self, src_if, mpls_label, mpls_ttl): + # the default of 64 matches the IP packet TTL default + def create_stream_labelled_ip4(self, src_if, mpls_labels, mpls_ttl=255): + 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) + + for ii in range(len(mpls_labels)): + if ii == len(mpls_labels) - 1: + p = p / MPLS(label=mpls_labels[ii], ttl=mpls_ttl, s=1) + else: + p = p / MPLS(label=mpls_labels[ii], ttl=mpls_ttl, s=0) + p = (p / 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_ip4(self, src_if, 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) / - MPLS(label=mpls_label, ttl=mpls_ttl) / - IP(src=src_if.remote_ip4, dst=src_if.remote_ip4) / + IP(src=src_if.remote_ip4, dst=dst_ip) / 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): + def create_stream_labelled_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, @@ -74,8 +96,18 @@ class TestMPLS(VppTestCase): 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(IPv6)): + capture.remove(p) + return capture + def verify_capture_ip4(self, src_if, capture, sent): try: + capture = self.verify_filter(capture, sent) + self.assertEqual(len(capture), len(sent)) for i in range(len(capture)): @@ -83,8 +115,8 @@ class TestMPLS(VppTestCase): rx = capture[i] # the rx'd packet has the MPLS label popped - eth = rx[Ether]; - self.assertEqual(eth.type, 0x800); + eth = rx[Ether] + self.assertEqual(eth.type, 0x800) tx_ip = tx[IP] rx_ip = rx[IP] @@ -92,10 +124,95 @@ class TestMPLS(VppTestCase): 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) + self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl) except: - raise; + raise + + def verify_mpls_stack(self, rx, mpls_labels, ttl=255, num=0): + # the rx'd packet has the MPLS label popped + eth = rx[Ether] + self.assertEqual(eth.type, 0x8847) + + rx_mpls = rx[MPLS] + + for ii in range(len(mpls_labels)): + self.assertEqual(rx_mpls.label, mpls_labels[ii]) + self.assertEqual(rx_mpls.cos, 0) + if ii == num: + self.assertEqual(rx_mpls.ttl, ttl) + else: + self.assertEqual(rx_mpls.ttl, 255) + + if ii == len(mpls_labels) - 1: + self.assertEqual(rx_mpls.s, 1) + else: + # not end of stack + self.assertEqual(rx_mpls.s, 0) + # pop the label to expose the next + rx_mpls = rx_mpls[MPLS].payload + + def verify_capture_labelled_ip4(self, src_if, capture, sent, + mpls_labels): + try: + capture = self.verify_filter(capture, sent) + + self.assertEqual(len(capture), len(sent)) + + for i in range(len(capture)): + tx = sent[i] + rx = capture[i] + tx_ip = tx[IP] + rx_ip = rx[IP] + + # the MPLS TTL is copied from the IP + self.verify_mpls_stack( + rx, mpls_labels, rx_ip.ttl, len(mpls_labels) - 1) + + 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_tunneled_ip4(self, src_if, capture, sent, mpls_labels): + try: + capture = self.verify_filter(capture, sent) + + self.assertEqual(len(capture), len(sent)) + + for i in range(len(capture)): + tx = sent[i] + rx = capture[i] + tx_ip = tx[IP] + rx_ip = rx[IP] + + # the MPLS TTL is 255 since it enters a new tunnel + self.verify_mpls_stack( + rx, mpls_labels, 255, len(mpls_labels) - 1) + + 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_labelled(self, src_if, capture, sent, + mpls_labels, ttl=254, num=0): + try: + capture = self.verify_filter(capture, sent) + + self.assertEqual(len(capture), len(sent)) + + for i in range(len(capture)): + rx = capture[i] + self.verify_mpls_stack(rx, mpls_labels, ttl, num) + except: + raise def verify_capture_ip6(self, src_if, capture, sent): try: @@ -106,8 +223,8 @@ class TestMPLS(VppTestCase): rx = capture[i] # the rx'd packet has the MPLS label popped - eth = rx[Ether]; - self.assertEqual(eth.type, 0x86DD); + eth = rx[Ether] + self.assertEqual(eth.type, 0x86DD) tx_ip = tx[IPv6] rx_ip = rx[IPv6] @@ -118,8 +235,373 @@ class TestMPLS(VppTestCase): self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim) except: - raise; + raise + + def test_swap(self): + """ MPLS label swap tests """ + + # + # A simple MPLS xconnect - eos label in label out + # + route_32_eos = MplsRoute(self, 32, 1, + [RoutePath(self.pg0.remote_ip4, + self.pg0.sw_if_index, + labels=[33])]) + route_32_eos.add_vpp_config() + + # + # a stream that matches the route for 10.0.0.1 + # PG0 is in the default table + # + self.vapi.cli("clear trace") + tx = self.create_stream_labelled_ip4(self.pg0, [32]) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_capture_labelled_ip4(self.pg0, rx, tx, [33]) + + # + # A simple MPLS xconnect - non-eos label in label out + # + route_32_neos = MplsRoute(self, 32, 0, + [RoutePath(self.pg0.remote_ip4, + self.pg0.sw_if_index, + labels=[33])]) + route_32_neos.add_vpp_config() + + # + # a stream that matches the route for 10.0.0.1 + # PG0 is in the default table + # + self.vapi.cli("clear trace") + tx = self.create_stream_labelled_ip4(self.pg0, [32, 99]) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_capture_labelled(self.pg0, rx, tx, [33, 99]) + + # + # An MPLS xconnect - EOS label in IP out + # + route_33_eos = MplsRoute(self, 33, 1, + [RoutePath(self.pg0.remote_ip4, + self.pg0.sw_if_index, + labels=[])]) + route_33_eos.add_vpp_config() + + self.vapi.cli("clear trace") + tx = self.create_stream_labelled_ip4(self.pg0, [33]) + 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) + + # + # An MPLS xconnect - non-EOS label in IP out - an invalid configuration + # so this traffic should be dropped. + # + route_33_neos = MplsRoute(self, 33, 0, + [RoutePath(self.pg0.remote_ip4, + self.pg0.sw_if_index, + labels=[])]) + route_33_neos.add_vpp_config() + + self.vapi.cli("clear trace") + tx = self.create_stream_labelled_ip4(self.pg0, [33, 99]) + 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 non-EOS packets popped and forwarded") + error(packet.show()) + raise + + # + # A recursive EOS x-connect, which resolves through another x-connect + # + route_34_eos = MplsRoute(self, 34, 1, + [RoutePath("0.0.0.0", + 0xffffffff, + nh_via_label=32, + labels=[44, 45])]) + route_34_eos.add_vpp_config() + + self.vapi.cli("clear trace") + tx = self.create_stream_labelled_ip4(self.pg0, [34]) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_capture_labelled_ip4(self.pg0, rx, tx, [33, 44, 45]) + + # + # A recursive non-EOS x-connect, which resolves through another x-connect + # + route_34_neos = MplsRoute(self, 34, 0, + [RoutePath("0.0.0.0", + 0xffffffff, + nh_via_label=32, + labels=[44, 46])]) + route_34_neos.add_vpp_config() + + self.vapi.cli("clear trace") + tx = self.create_stream_labelled_ip4(self.pg0, [34, 99]) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + # it's the 2nd (counting from 0) lael in the stack that is swapped + self.verify_capture_labelled(self.pg0, rx, tx, [33, 44, 46, 99], num=2) + + # + # an recursive IP route that resolves through the recursive non-eos x-connect + # + ip_10_0_0_1 = IpRoute(self, "10.0.0.1", 32, + [RoutePath("0.0.0.0", + 0xffffffff, + nh_via_label=34, + labels=[55])]) + ip_10_0_0_1.add_vpp_config() + + self.vapi.cli("clear trace") + tx = self.create_stream_ip4(self.pg0, "10.0.0.1") + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_capture_labelled_ip4(self.pg0, rx, tx, [33, 44, 46, 55]) + + ip_10_0_0_1.remove_vpp_config() + route_34_neos.remove_vpp_config() + route_34_eos.remove_vpp_config() + route_33_neos.remove_vpp_config() + route_33_eos.remove_vpp_config() + route_32_neos.remove_vpp_config() + route_32_eos.remove_vpp_config() + + def test_bind(self): + """ MPLS Local Label Binding test """ + + # + # Add a non-recursive route with a single out label + # + route_10_0_0_1 = IpRoute(self, "10.0.0.1", 32, + [RoutePath(self.pg0.remote_ip4, + self.pg0.sw_if_index, + labels=[45])]) + route_10_0_0_1.add_vpp_config() + + # bind a local label to the route + binding = MplsIpBind(self, 44, "10.0.0.1", 32) + binding.add_vpp_config() + + # non-EOS stream + self.vapi.cli("clear trace") + tx = self.create_stream_labelled_ip4(self.pg0, [44, 99]) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_capture_labelled(self.pg0, rx, tx, [45, 99]) + + # EOS stream + self.vapi.cli("clear trace") + tx = self.create_stream_labelled_ip4(self.pg0, [44]) + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_capture_labelled(self.pg0, rx, tx, [45]) + + # IP stream + self.vapi.cli("clear trace") + tx = self.create_stream_ip4(self.pg0, "10.0.0.1") + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_capture_labelled_ip4(self.pg0, rx, tx, [45]) + + # + # cleanup + # + binding.remove_vpp_config() + route_10_0_0_1.remove_vpp_config() + + def test_imposition(self): + """ MPLS label imposition test """ + + # + # Add a non-recursive route with a single out label + # + route_10_0_0_1 = IpRoute(self, "10.0.0.1", 32, + [RoutePath(self.pg0.remote_ip4, + self.pg0.sw_if_index, + labels=[32])]) + route_10_0_0_1.add_vpp_config() + + # + # a stream that matches the route for 10.0.0.1 + # PG0 is in the default table + # + self.vapi.cli("clear trace") + tx = self.create_stream_ip4(self.pg0, "10.0.0.1") + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32]) + + # + # Add a non-recursive route with a 3 out labels + # + route_10_0_0_2 = IpRoute(self, "10.0.0.2", 32, + [RoutePath(self.pg0.remote_ip4, + self.pg0.sw_if_index, + labels=[32, 33, 34])]) + route_10_0_0_2.add_vpp_config() + + # + # a stream that matches the route for 10.0.0.1 + # PG0 is in the default table + # + self.vapi.cli("clear trace") + tx = self.create_stream_ip4(self.pg0, "10.0.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_labelled_ip4(self.pg0, rx, tx, [32, 33, 34]) + + # + # add a recursive path, with ouput label, via the 1 label route + # + route_11_0_0_1 = IpRoute(self, "11.0.0.1", 32, + [RoutePath("10.0.0.1", + 0xffffffff, + labels=[44])]) + route_11_0_0_1.add_vpp_config() + + # + # a stream that matches the route for 11.0.0.1, should pick up + # the label stack for 11.0.0.1 and 10.0.0.1 + # + self.vapi.cli("clear trace") + tx = self.create_stream_ip4(self.pg0, "11.0.0.1") + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + rx = self.pg0.get_capture() + self.verify_capture_labelled_ip4(self.pg0, rx, tx, [32, 44]) + + # + # add a recursive path, with 2 labels, via the 3 label route + # + route_11_0_0_2 = IpRoute(self, "11.0.0.2", 32, + [RoutePath("10.0.0.2", + 0xffffffff, + labels=[44, 45])]) + route_11_0_0_2.add_vpp_config() + + # + # a stream that matches the route for 11.0.0.1, should pick up + # the label stack for 11.0.0.1 and 10.0.0.1 + # + self.vapi.cli("clear trace") + tx = self.create_stream_ip4(self.pg0, "11.0.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_labelled_ip4( + self.pg0, rx, tx, [32, 33, 34, 44, 45]) + + # + # cleanup + # + route_11_0_0_2.remove_vpp_config() + route_11_0_0_1.remove_vpp_config() + route_10_0_0_2.remove_vpp_config() + route_10_0_0_1.remove_vpp_config() + + def test_tunnel(self): + """ MPLS Tunnel Tests """ + + # + # Create a tunnel with a single out label + # + nh_addr = socket.inet_pton(socket.AF_INET, self.pg0.remote_ip4) + + reply = self.vapi.mpls_tunnel_add_del(0xffffffff, # don't know the if index yet + 1, # IPv4 next-hop + nh_addr, + self.pg0.sw_if_index, + 0, # next-hop-table-id + 1, # next-hop-weight + 2, # num-out-labels, + [44, 46]) + self.vapi.sw_interface_set_flags(reply.sw_if_index, admin_up_down=1) + + # + # add an unlabelled route through the new tunnel + # + dest_addr = socket.inet_pton(socket.AF_INET, "10.0.0.3") + nh_addr = socket.inet_pton(socket.AF_INET, "0.0.0.0") + dest_addr_len = 32 + + self.vapi.ip_add_del_route(dest_addr, + dest_addr_len, + nh_addr, # all zeros next-hop - tunnel is p2p + reply.sw_if_index, # sw_if_index of the new tunnel + 0, # table-id + 0, # next-hop-table-id + 1, # next-hop-weight + 0, # num-out-labels, + []) # out-label + + self.vapi.cli("clear trace") + tx = self.create_stream_ip4(self.pg0, "10.0.0.3") + self.pg0.add_stream(tx) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg0.get_capture() + self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [44, 46]) def test_v4_exp_null(self): """ MPLS V4 Explicit NULL test """ @@ -128,7 +610,7 @@ class TestMPLS(VppTestCase): # The first test case has an MPLS TTL of 0 # all packet should be dropped # - tx = self.create_stream_ip4(self.pg0, 0, 0) + tx = self.create_stream_labelled_ip4(self.pg0, [0], 0) self.pg0.add_stream(tx) self.pg_enable_capture(self.pg_interfaces) @@ -137,7 +619,7 @@ class TestMPLS(VppTestCase): rx = self.pg0.get_capture() try: - self.assertEqual(0, len(rx)); + self.assertEqual(0, len(rx)) except: error("MPLS TTL=0 packets forwarded") error(packet.show()) @@ -148,7 +630,7 @@ class TestMPLS(VppTestCase): # PG0 is in the default table # self.vapi.cli("clear trace") - tx = self.create_stream_ip4(self.pg0, 0, 2) + tx = self.create_stream_labelled_ip4(self.pg0, [0]) self.pg0.add_stream(tx) self.pg_enable_capture(self.pg_interfaces) @@ -163,7 +645,7 @@ class TestMPLS(VppTestCase): # 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) + tx = self.create_stream_labelled_ip4(self.pg1, [0]) self.pg1.add_stream(tx) self.pg_enable_capture(self.pg_interfaces) @@ -180,7 +662,7 @@ class TestMPLS(VppTestCase): # PG0 is in the default table # self.vapi.cli("clear trace") - tx = self.create_stream_ip6(self.pg0, 2, 2) + tx = self.create_stream_labelled_ip6(self.pg0, 2, 2) self.pg0.add_stream(tx) self.pg_enable_capture(self.pg_interfaces) @@ -195,7 +677,7 @@ class TestMPLS(VppTestCase): # 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) + tx = self.create_stream_labelled_ip6(self.pg1, 2, 2) self.pg1.add_stream(tx) self.pg_enable_capture(self.pg_interfaces) diff --git a/test/vpp_ip_route.py b/test/vpp_ip_route.py index 78e6aaa2..1dc8c1ab 100644 --- a/test/vpp_ip_route.py +++ b/test/vpp_ip_route.py @@ -6,13 +6,19 @@ import socket +# from vnet/vnet/mpls/mpls_types.h +MPLS_IETF_MAX_LABEL = 0xfffff +MPLS_LABEL_INVALID = MPLS_IETF_MAX_LABEL + 1 -class IpPath: - def __init__(self, nh_addr, nh_sw_if_index, nh_table_id=0): +class RoutePath: + + def __init__(self, nh_addr, nh_sw_if_index, nh_table_id=0, labels=[], nh_via_label=MPLS_LABEL_INVALID): 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 + self.nh_via_label = nh_via_label + self.nh_labels = labels class IpRoute: @@ -34,7 +40,11 @@ class IpRoute: self.dest_addr_len, path.nh_addr, path.nh_itf, - table_id=self.table_id) + table_id=self.table_id, + next_hop_out_label_stack=path.nh_labels, + next_hop_n_out_labels=len( + path.nh_labels), + next_hop_via_label=path.nh_via_label) def remove_vpp_config(self): for path in self.paths: @@ -44,3 +54,62 @@ class IpRoute: path.nh_itf, table_id=self.table_id, is_add=0) + + +class MplsIpBind: + """ + MPLS to IP Binding + """ + + def __init__(self, test, local_label, dest_addr, dest_addr_len): + self._test = test + self.dest_addr = socket.inet_pton(socket.AF_INET, dest_addr) + self.dest_addr_len = dest_addr_len + self.local_label = local_label + + def add_vpp_config(self): + self._test.vapi.mpls_ip_bind_unbind(self.local_label, + self.dest_addr, + self.dest_addr_len) + + def remove_vpp_config(self): + self._test.vapi.mpls_ip_bind_unbind(self.local_label, + self.dest_addr, + self.dest_addr_len, + is_bind=0) + + +class MplsRoute: + """ + MPLS Route + """ + + def __init__(self, test, local_label, eos_bit, paths, table_id=0): + self._test = test + self.paths = paths + self.local_label = local_label + self.eos_bit = eos_bit + self.table_id = table_id + + def add_vpp_config(self): + for path in self.paths: + self._test.vapi.mpls_route_add_del(self.local_label, + self.eos_bit, + 1, + path.nh_addr, + path.nh_itf, + table_id=self.table_id, + next_hop_out_label_stack=path.nh_labels, + next_hop_n_out_labels=len( + path.nh_labels), + next_hop_via_label=path.nh_via_label) + + def remove_vpp_config(self): + for path in self.paths: + self._test.vapi.mpls_route_add_del(self.local_label, + self.eos_bit, + 1, + 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 9db26d9f..d29f90c1 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -1,4 +1,5 @@ import os +import array from logging import error from hook import Hook @@ -21,6 +22,7 @@ MPLS_LABEL_INVALID = MPLS_IETF_MAX_LABEL + 1 class L2_VTR_OP: L2_POP_1 = 3 +need_swap = True if os.sys.byteorder == 'little' else False class VppPapiProvider(object): """VPP-api provider using vpp-papi @@ -368,12 +370,15 @@ class VppPapiProvider(object): next_hop_address, next_hop_sw_if_index=0xFFFFFFFF, table_id=0, - resolve_attempts=0, - classify_table_index=0xFFFFFFFF, - next_hop_out_label=MPLS_LABEL_INVALID, next_hop_table_id=0, + next_hop_weight=1, + next_hop_n_out_labels = 0, + next_hop_out_label_stack = [], + next_hop_via_label = MPLS_LABEL_INVALID, create_vrf_if_needed=0, - resolve_if_needed=0, + is_resolve_host=0, + is_resolve_attached=0, + classify_table_index=0xFFFFFFFF, is_add=1, is_drop=0, is_unreach=0, @@ -382,10 +387,7 @@ class VppPapiProvider(object): is_local=0, is_classify=0, is_multipath=0, - is_resolve_host=0, - is_resolve_attached=0, - not_last=0, - next_hop_weight=1): + not_last=0): """ :param dst_address_length: @@ -395,10 +397,8 @@ class VppPapiProvider(object): :param next_hop_sw_if_index: (Default value = 0xFFFFFFFF) :param vrf_id: (Default value = 0) :param lookup_in_vrf: (Default value = 0) - :param resolve_attempts: (Default value = 0) :param classify_table_index: (Default value = 0xFFFFFFFF) :param create_vrf_if_needed: (Default value = 0) - :param resolve_if_needed: (Default value = 0) :param is_add: (Default value = 1) :param is_drop: (Default value = 0) :param is_ipv6: (Default value = 0) @@ -411,16 +411,18 @@ class VppPapiProvider(object): :param next_hop_weight: (Default value = 1) """ + stack = array.array('I', next_hop_out_label_stack) + if need_swap: + stack.byteswap() + stack = stack.tostring() + return self.api( vpp_papi.ip_add_del_route, (next_hop_sw_if_index, table_id, - resolve_attempts, classify_table_index, - next_hop_out_label, next_hop_table_id, create_vrf_if_needed, - resolve_if_needed, is_add, is_drop, is_unreach, @@ -435,7 +437,10 @@ class VppPapiProvider(object): next_hop_weight, dst_address_length, dst_address, - next_hop_address)) + next_hop_address, + next_hop_n_out_labels, + next_hop_via_label, + stack)) def ip_neighbor_add_del(self, sw_if_index, @@ -505,3 +510,153 @@ class VppPapiProvider(object): dst_address, outer_fib_id) ) + + def mpls_route_add_del( + self, + label, + eos, + next_hop_proto_is_ip4, + next_hop_address, + next_hop_sw_if_index=0xFFFFFFFF, + table_id=0, + next_hop_table_id=0, + next_hop_weight=1, + next_hop_n_out_labels = 0, + next_hop_out_label_stack = [], + next_hop_via_label = MPLS_LABEL_INVALID, + create_vrf_if_needed=0, + is_resolve_host=0, + is_resolve_attached=0, + is_add=1, + is_drop=0, + is_multipath=0, + classify_table_index=0xFFFFFFFF, + is_classify=0, + not_last=0): + """ + + :param dst_address_length: + :param next_hop_sw_if_index: (Default value = 0xFFFFFFFF) + :param dst_address: + :param next_hop_address: + :param next_hop_sw_if_index: (Default value = 0xFFFFFFFF) + :param vrf_id: (Default value = 0) + :param lookup_in_vrf: (Default value = 0) + :param classify_table_index: (Default value = 0xFFFFFFFF) + :param create_vrf_if_needed: (Default value = 0) + :param is_add: (Default value = 1) + :param is_drop: (Default value = 0) + :param is_ipv6: (Default value = 0) + :param is_local: (Default value = 0) + :param is_classify: (Default value = 0) + :param is_multipath: (Default value = 0) + :param is_resolve_host: (Default value = 0) + :param is_resolve_attached: (Default value = 0) + :param not_last: (Default value = 0) + :param next_hop_weight: (Default value = 1) + + """ + stack = array.array('I', next_hop_out_label_stack) + if need_swap: + stack.byteswap() + stack = stack.tostring() + + return self.api( + vpp_papi.mpls_route_add_del, + (label, + eos, + table_id, + classify_table_index, + create_vrf_if_needed, + is_add, + is_classify, + is_multipath, + is_resolve_host, + is_resolve_attached, + next_hop_proto_is_ip4, + next_hop_weight, + next_hop_address, + next_hop_n_out_labels, + next_hop_sw_if_index, + next_hop_table_id, + next_hop_via_label, + stack)) + + def mpls_ip_bind_unbind( + self, + label, + dst_address, + dst_address_length, + table_id=0, + ip_table_id=0, + is_ip4=1, + create_vrf_if_needed=0, + is_bind=1): + """ + """ + return self.api( + vpp_papi.mpls_ip_bind_unbind, + (table_id, + label, + ip_table_id, + create_vrf_if_needed, + is_bind, + is_ip4, + dst_address_length, + dst_address)) + + def mpls_tunnel_add_del( + self, + tun_sw_if_index, + next_hop_proto_is_ip4, + next_hop_address, + next_hop_sw_if_index=0xFFFFFFFF, + next_hop_table_id=0, + next_hop_weight=1, + next_hop_n_out_labels = 0, + next_hop_out_label_stack = [], + next_hop_via_label = MPLS_LABEL_INVALID, + create_vrf_if_needed=0, + is_add=1, + l2_only=0): + """ + + :param dst_address_length: + :param next_hop_sw_if_index: (Default value = 0xFFFFFFFF) + :param dst_address: + :param next_hop_address: + :param next_hop_sw_if_index: (Default value = 0xFFFFFFFF) + :param vrf_id: (Default value = 0) + :param lookup_in_vrf: (Default value = 0) + :param classify_table_index: (Default value = 0xFFFFFFFF) + :param create_vrf_if_needed: (Default value = 0) + :param is_add: (Default value = 1) + :param is_drop: (Default value = 0) + :param is_ipv6: (Default value = 0) + :param is_local: (Default value = 0) + :param is_classify: (Default value = 0) + :param is_multipath: (Default value = 0) + :param is_resolve_host: (Default value = 0) + :param is_resolve_attached: (Default value = 0) + :param not_last: (Default value = 0) + :param next_hop_weight: (Default value = 1) + + """ + stack = array.array('I', next_hop_out_label_stack) + if need_swap: + stack.byteswap() + stack = stack.tostring() + + return self.api( + vpp_papi.mpls_tunnel_add_del, + (tun_sw_if_index, + is_add, + l2_only, + next_hop_proto_is_ip4, + next_hop_weight, + next_hop_address, + next_hop_n_out_labels, + next_hop_sw_if_index, + next_hop_table_id, + stack)) + |