aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorNeale Ranns <nranns@cisco.com>2017-03-01 15:12:11 -0800
committerNeale Ranns <nranns@cisco.com>2017-04-07 09:12:12 +0000
commit0f26c5a0138ac86d7ebd197c31a09d8d624c35fe (patch)
tree5cee4659885432c439e44d7346fd5e482df5f0e6 /test
parentc14f31c2c62fb66d98aef4402a6f1bda09683fd3 (diff)
MPLS Mcast
1 - interface-DPO Used in the Data-plane to change a packet's input interface 2 - MPLS multicast FIB entry Same as a unicast entry but it links to a replicate not a load-balance DPO 3 - Multicast MPLS tunnel Update MPLS tunnels to use a FIB path-list to describe the endpoint[s]. Use the path-list to generate the forwarding chain (DPOs) to link to . 4 - Resolve a path via a local label (of an mLDP LSP) For IP multicast entries to use an LSP in the replication list, we need to decribe the 'resolve-via-label' where the label is that of a multicast LSP. 5 - MPLS disposition path sets RPF-ID For a interface-less LSP (i.e. mLDP not RSVP-TE) at the tail of the LSP we still need to perform an RPF check. An MPLS disposition DPO performs the MPLS pop validation checks and sets the RPF-ID in the packet. 6 - RPF check with per-entry RPF-ID An RPF-ID is used instead of a real interface SW if index in the case the IP traffic arrives from an LSP that does not have an associated interface. Change-Id: Ib92e177be919147bafeb599729abf3d1abc2f4b3 Signed-off-by: Neale Ranns <nranns@cisco.com>
Diffstat (limited to 'test')
-rw-r--r--test/test_ip_mcast.py1
-rw-r--r--test/test_mpls.py277
-rw-r--r--test/vpp_ip_route.py38
-rw-r--r--test/vpp_mpls_tunnel_interface.py46
-rw-r--r--test/vpp_papi_provider.py16
5 files changed, 351 insertions, 27 deletions
diff --git a/test/test_ip_mcast.py b/test/test_ip_mcast.py
index 36d597a7..c1397d70 100644
--- a/test/test_ip_mcast.py
+++ b/test/test_ip_mcast.py
@@ -622,6 +622,7 @@ class TestIPMcast(VppTestCase):
(MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
MRouteItfFlags.MFIB_ITF_FLAG_NEGATE_SIGNAL))
+ self.vapi.cli("clear trace")
tx = self._mcast_connected_send_stream("232.1.1.1")
signals = self.vapi.mfib_signal_dump()
diff --git a/test/test_mpls.py b/test/test_mpls.py
index fc832644..700b7091 100644
--- a/test/test_mpls.py
+++ b/test/test_mpls.py
@@ -5,7 +5,9 @@ import socket
from framework import VppTestCase, VppTestRunner
from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \
- VppMplsIpBind
+ VppMplsIpBind, VppIpMRoute, VppMRoutePath, \
+ MRouteItfFlags, MRouteEntryFlags
+from vpp_mpls_tunnel_interface import VppMPLSTunnelInterface
from scapy.packet import Raw
from scapy.layers.l2 import Ether
@@ -21,7 +23,7 @@ class TestMPLS(VppTestCase):
super(TestMPLS, self).setUp()
# create 2 pg interfaces
- self.create_pg_interfaces(range(2))
+ self.create_pg_interfaces(range(4))
# setup both interfaces
# assign them different tables.
@@ -53,10 +55,12 @@ class TestMPLS(VppTestCase):
mpls_labels,
mpls_ttl=255,
ping=0,
- ip_itf=None):
+ ip_itf=None,
+ dst_ip=None,
+ n=257):
self.reset_packet_infos()
pkts = []
- for i in range(0, 257):
+ for i in range(0, n):
info = self.create_packet_info(src_if, src_if)
payload = self.info_to_payload(info)
p = Ether(dst=src_if.local_mac, src=src_if.remote_mac)
@@ -67,9 +71,14 @@ class TestMPLS(VppTestCase):
else:
p = p / MPLS(label=mpls_labels[ii], ttl=mpls_ttl, s=0)
if not ping:
- p = (p / IP(src=src_if.local_ip4, dst=src_if.remote_ip4) /
- UDP(sport=1234, dport=1234) /
- Raw(payload))
+ if not dst_ip:
+ p = (p / IP(src=src_if.local_ip4, dst=src_if.remote_ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw(payload))
+ else:
+ p = (p / IP(src=src_if.local_ip4, dst=dst_ip) /
+ UDP(sport=1234, dport=1234) /
+ Raw(payload))
else:
p = (p / IP(src=ip_itf.remote_ip4,
dst=ip_itf.local_ip4) /
@@ -254,6 +263,13 @@ class TestMPLS(VppTestCase):
except:
raise
+ def send_and_assert_no_replies(self, intf, pkts, remark):
+ intf.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ for i in self.pg_interfaces:
+ i.assert_nothing_captured(remark=remark)
+
def test_swap(self):
""" MPLS label swap tests """
@@ -278,7 +294,7 @@ class TestMPLS(VppTestCase):
self.pg_start()
rx = self.pg0.get_capture()
- self.verify_capture_labelled_ip4(self.pg0, rx, tx, [33])
+ self.verify_capture_labelled(self.pg0, rx, tx, [33])
#
# A simple MPLS xconnect - non-eos label in label out
@@ -358,7 +374,7 @@ class TestMPLS(VppTestCase):
self.pg_start()
rx = self.pg0.get_capture()
- self.verify_capture_labelled_ip4(self.pg0, rx, tx, [33, 44, 45])
+ self.verify_capture_labelled(self.pg0, rx, tx, [33, 44, 45], num=2)
#
# A recursive non-EOS x-connect, which resolves through another
@@ -576,25 +592,19 @@ class TestMPLS(VppTestCase):
#
# 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)
+ mpls_tun = VppMPLSTunnelInterface(self,
+ [VppRoutePath(self.pg0.remote_ip4,
+ self.pg0.sw_if_index,
+ labels=[44, 46])])
+ mpls_tun.add_vpp_config()
+ mpls_tun.admin_up()
#
# add an unlabelled route through the new tunnel
#
route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
[VppRoutePath("0.0.0.0",
- reply.sw_if_index)])
+ mpls_tun._sw_if_index)])
route_10_0_0_3.add_vpp_config()
self.vapi.cli("clear trace")
@@ -738,6 +748,229 @@ class TestMPLS(VppTestCase):
route_35_eos.remove_vpp_config()
route_34_eos.remove_vpp_config()
+ def test_interface_rx(self):
+ """ MPLS Interface Receive """
+
+ #
+ # Add a non-recursive route that will forward the traffic
+ # post-interface-rx
+ #
+ route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
+ table_id=1,
+ paths=[VppRoutePath(self.pg1.remote_ip4,
+ self.pg1.sw_if_index)])
+ route_10_0_0_1.add_vpp_config()
+
+ #
+ # An interface receive label that maps traffic to RX on interface
+ # pg1
+ # by injecting the packet in on pg0, which is in table 0
+ # doing an interface-rx on pg1 and matching a route in table 1
+ # if the packet egresses, then we must have swapped to pg1
+ # so as to have matched the route in table 1
+ #
+ route_34_eos = VppMplsRoute(self, 34, 1,
+ [VppRoutePath("0.0.0.0",
+ self.pg1.sw_if_index,
+ is_interface_rx=1)])
+ route_34_eos.add_vpp_config()
+
+ #
+ # ping an interface in the default table
+ # PG0 is in the default table
+ #
+ self.vapi.cli("clear trace")
+ tx = self.create_stream_labelled_ip4(self.pg0, [34], n=257,
+ dst_ip="10.0.0.1")
+ self.pg0.add_stream(tx)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg1.get_capture(257)
+ self.verify_capture_ip4(self.pg1, rx, tx)
+
+ def test_mcast_mid_point(self):
+ """ MPLS Multicast Mid Point """
+
+ #
+ # Add a non-recursive route that will forward the traffic
+ # post-interface-rx
+ #
+ route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
+ table_id=1,
+ paths=[VppRoutePath(self.pg1.remote_ip4,
+ self.pg1.sw_if_index)])
+ route_10_0_0_1.add_vpp_config()
+
+ #
+ # Add a mcast entry that replicate to pg2 and pg3
+ # and replicate to a interface-rx (like a bud node would)
+ #
+ route_3400_eos = VppMplsRoute(self, 3400, 1,
+ [VppRoutePath(self.pg2.remote_ip4,
+ self.pg2.sw_if_index,
+ labels=[3401]),
+ VppRoutePath(self.pg3.remote_ip4,
+ self.pg3.sw_if_index,
+ labels=[3402]),
+ VppRoutePath("0.0.0.0",
+ self.pg1.sw_if_index,
+ is_interface_rx=1)],
+ is_multicast=1)
+ route_3400_eos.add_vpp_config()
+
+ #
+ # ping an interface in the default table
+ # PG0 is in the default table
+ #
+ self.vapi.cli("clear trace")
+ tx = self.create_stream_labelled_ip4(self.pg0, [3400], n=257,
+ dst_ip="10.0.0.1")
+ self.pg0.add_stream(tx)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg1.get_capture(257)
+ self.verify_capture_ip4(self.pg1, rx, tx)
+
+ rx = self.pg2.get_capture(257)
+ self.verify_capture_labelled(self.pg2, rx, tx, [3401])
+ rx = self.pg3.get_capture(257)
+ self.verify_capture_labelled(self.pg3, rx, tx, [3402])
+
+ def test_mcast_head(self):
+ """ MPLS Multicast Head-end """
+
+ #
+ # Create a multicast tunnel with two replications
+ #
+ mpls_tun = VppMPLSTunnelInterface(self,
+ [VppRoutePath(self.pg2.remote_ip4,
+ self.pg2.sw_if_index,
+ labels=[42]),
+ VppRoutePath(self.pg3.remote_ip4,
+ self.pg3.sw_if_index,
+ labels=[43])],
+ is_multicast=1)
+ mpls_tun.add_vpp_config()
+ mpls_tun.admin_up()
+
+ #
+ # add an unlabelled route through the new tunnel
+ #
+ route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
+ [VppRoutePath("0.0.0.0",
+ mpls_tun._sw_if_index)])
+ route_10_0_0_3.add_vpp_config()
+
+ 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.pg2.get_capture(257)
+ self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [42])
+ rx = self.pg3.get_capture(257)
+ self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [43])
+
+ #
+ # An an IP multicast route via the tunnel
+ # A (*,G).
+ # one accepting interface, pg0, 1 forwarding interface via the tunnel
+ #
+ route_232_1_1_1 = VppIpMRoute(
+ self,
+ "0.0.0.0",
+ "232.1.1.1", 32,
+ MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
+ [VppMRoutePath(self.pg0.sw_if_index,
+ MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
+ VppMRoutePath(mpls_tun._sw_if_index,
+ MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
+ route_232_1_1_1.add_vpp_config()
+
+ self.vapi.cli("clear trace")
+ tx = self.create_stream_ip4(self.pg0, "232.1.1.1")
+ self.pg0.add_stream(tx)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg2.get_capture(257)
+ self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [42])
+ rx = self.pg3.get_capture(257)
+ self.verify_capture_tunneled_ip4(self.pg0, rx, tx, [43])
+
+ def test_mcast_tail(self):
+ """ MPLS Multicast Tail """
+
+ #
+ # Add a multicast route that will forward the traffic
+ # post-disposition
+ #
+ route_232_1_1_1 = VppIpMRoute(
+ self,
+ "0.0.0.0",
+ "232.1.1.1", 32,
+ MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
+ table_id=1,
+ paths=[VppMRoutePath(self.pg1.sw_if_index,
+ MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
+ route_232_1_1_1.add_vpp_config()
+
+ #
+ # An interface receive label that maps traffic to RX on interface
+ # pg1
+ # by injecting the packet in on pg0, which is in table 0
+ # doing an rpf-id and matching a route in table 1
+ # if the packet egresses, then we must have matched the route in
+ # table 1
+ #
+ route_34_eos = VppMplsRoute(self, 34, 1,
+ [VppRoutePath("0.0.0.0",
+ self.pg1.sw_if_index,
+ nh_table_id=1,
+ rpf_id=55)],
+ is_multicast=1)
+
+ route_34_eos.add_vpp_config()
+
+ #
+ # Drop due to interface lookup miss
+ #
+ self.vapi.cli("clear trace")
+ tx = self.create_stream_labelled_ip4(self.pg0, [34],
+ dst_ip="232.1.1.1", n=1)
+ self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop none")
+
+ #
+ # set the RPF-ID of the enrtry to match the input packet's
+ #
+ route_232_1_1_1.update_rpf_id(55)
+
+ self.vapi.cli("clear trace")
+ tx = self.create_stream_labelled_ip4(self.pg0, [34],
+ dst_ip="232.1.1.1", n=257)
+ self.pg0.add_stream(tx)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg1.get_capture(257)
+ self.verify_capture_ip4(self.pg1, rx, tx)
+
+ #
+ # set the RPF-ID of the enrtry to not match the input packet's
+ #
+ route_232_1_1_1.update_rpf_id(56)
+ tx = self.create_stream_labelled_ip4(self.pg0, [34],
+ dst_ip="232.1.1.1")
+ self.send_and_assert_no_replies(self.pg0, tx, "RPF-ID drop 56")
+
class TestMPLSDisabled(VppTestCase):
""" MPLS disabled """
diff --git a/test/vpp_ip_route.py b/test/vpp_ip_route.py
index faf5f801..d6146f28 100644
--- a/test/vpp_ip_route.py
+++ b/test/vpp_ip_route.py
@@ -55,15 +55,24 @@ class VppRoutePath(object):
nh_table_id=0,
labels=[],
nh_via_label=MPLS_LABEL_INVALID,
- is_ip6=0):
+ is_ip6=0,
+ rpf_id=0,
+ is_interface_rx=0):
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
+ self.weight = 1
+ self.rpf_id = rpf_id
if is_ip6:
self.nh_addr = inet_pton(AF_INET6, nh_addr)
else:
self.nh_addr = inet_pton(AF_INET, nh_addr)
+ self.is_interface_rx = is_interface_rx
+ self.is_rpf_id = 0
+ if rpf_id != 0:
+ self.is_rpf_id = 1
+ self.nh_itf = rpf_id
class VppMRoutePath(VppRoutePath):
@@ -176,13 +185,15 @@ class VppIpMRoute(VppObject):
"""
def __init__(self, test, src_addr, grp_addr,
- grp_addr_len, e_flags, paths, table_id=0, is_ip6=0):
+ grp_addr_len, e_flags, paths, table_id=0,
+ rpf_id=0, is_ip6=0):
self._test = test
self.paths = paths
self.grp_addr_len = grp_addr_len
self.table_id = table_id
self.e_flags = e_flags
self.is_ip6 = is_ip6
+ self.rpf_id = rpf_id
if is_ip6:
self.grp_addr = inet_pton(AF_INET6, grp_addr)
@@ -199,6 +210,7 @@ class VppIpMRoute(VppObject):
self.e_flags,
path.nh_itf,
path.nh_i_flags,
+ rpf_id=self.rpf_id,
table_id=self.table_id,
is_ipv6=self.is_ip6)
self._test.registry.register(self, self._test.logger)
@@ -226,6 +238,18 @@ class VppIpMRoute(VppObject):
table_id=self.table_id,
is_ipv6=self.is_ip6)
+ def update_rpf_id(self, rpf_id):
+ self.rpf_id = rpf_id
+ self._test.vapi.ip_mroute_add_del(self.src_addr,
+ self.grp_addr,
+ self.grp_addr_len,
+ self.e_flags,
+ 0xffffffff,
+ 0,
+ rpf_id=self.rpf_id,
+ table_id=self.table_id,
+ is_ipv6=self.is_ip6)
+
def update_path_flags(self, itf, flags):
for path in self.paths:
if path.nh_itf == itf:
@@ -342,14 +366,17 @@ class VppMplsRoute(VppObject):
MPLS Route/LSP
"""
- def __init__(self, test, local_label, eos_bit, paths, table_id=0):
+ def __init__(self, test, local_label, eos_bit, paths, table_id=0,
+ is_multicast=0):
self._test = test
self.paths = paths
self.local_label = local_label
self.eos_bit = eos_bit
self.table_id = table_id
+ self.is_multicast = is_multicast
def add_vpp_config(self):
+ is_multipath = len(self.paths) > 1
for path in self.paths:
self._test.vapi.mpls_route_add_del(
self.local_label,
@@ -357,7 +384,11 @@ class VppMplsRoute(VppObject):
1,
path.nh_addr,
path.nh_itf,
+ is_multicast=self.is_multicast,
+ is_multipath=is_multipath,
table_id=self.table_id,
+ is_interface_rx=path.is_interface_rx,
+ is_rpf_id=path.is_rpf_id,
next_hop_out_label_stack=path.nh_labels,
next_hop_n_out_labels=len(
path.nh_labels),
@@ -372,6 +403,7 @@ class VppMplsRoute(VppObject):
1,
path.nh_addr,
path.nh_itf,
+ is_rpf_id=path.is_rpf_id,
table_id=self.table_id,
is_add=0)
diff --git a/test/vpp_mpls_tunnel_interface.py b/test/vpp_mpls_tunnel_interface.py
new file mode 100644
index 00000000..f2001574
--- /dev/null
+++ b/test/vpp_mpls_tunnel_interface.py
@@ -0,0 +1,46 @@
+
+from vpp_interface import VppInterface
+from vpp_ip_route import VppRoutePath
+import socket
+
+
+class VppMPLSTunnelInterface(VppInterface):
+ """
+ VPP MPLS Tunnel interface
+ """
+
+ def __init__(self, test, paths, is_multicast=0):
+ """ Create MPLS Tunnel interface """
+ self._sw_if_index = 0
+ super(VppMPLSTunnelInterface, self).__init__(test)
+ self._test = test
+ self.t_paths = paths
+ self.is_multicast = is_multicast
+
+ def add_vpp_config(self):
+ self._sw_if_index = 0xffffffff
+ for path in self.t_paths:
+ reply = self.test.vapi.mpls_tunnel_add_del(
+ self._sw_if_index,
+ 1, # IPv4 next-hop
+ path.nh_addr,
+ path.nh_itf,
+ path.nh_table_id,
+ path.weight,
+ next_hop_out_label_stack=path.nh_labels,
+ next_hop_n_out_labels=len(path.nh_labels),
+ is_multicast=self.is_multicast)
+ self._sw_if_index = reply.sw_if_index
+
+ def remove_vpp_config(self):
+ for path in self.t_paths:
+ reply = self.test.vapi.mpls_tunnel_add_del(
+ self.sw_if_index,
+ 1, # IPv4 next-hop
+ path.nh_addr,
+ path.nh_itf,
+ path.nh_table_id,
+ path.weight,
+ next_hop_out_label_stack=path.nh_labels,
+ next_hop_n_out_labels=len(path.nh_labels),
+ is_add=0)
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index e8025dff..ceb684b7 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -849,6 +849,9 @@ class VppPapiProvider(object):
create_vrf_if_needed=0,
is_resolve_host=0,
is_resolve_attached=0,
+ is_interface_rx=0,
+ is_rpf_id=0,
+ is_multicast=0,
is_add=1,
is_drop=0,
is_multipath=0,
@@ -872,6 +875,7 @@ class VppPapiProvider(object):
:param is_local: (Default value = 0)
:param is_classify: (Default value = 0)
:param is_multipath: (Default value = 0)
+ :param is_multicast: (Default value = 0)
:param is_resolve_host: (Default value = 0)
:param is_resolve_attached: (Default value = 0)
:param not_last: (Default value = 0)
@@ -889,8 +893,11 @@ class VppPapiProvider(object):
'mr_is_add': is_add,
'mr_is_classify': is_classify,
'mr_is_multipath': is_multipath,
+ 'mr_is_multicast': is_multicast,
'mr_is_resolve_host': is_resolve_host,
'mr_is_resolve_attached': is_resolve_attached,
+ 'mr_is_interface_rx': is_interface_rx,
+ 'mr_is_rpf_id': is_rpf_id,
'mr_next_hop_proto_is_ip4': next_hop_proto_is_ip4,
'mr_next_hop_weight': next_hop_weight,
'mr_next_hop': next_hop_address,
@@ -936,7 +943,8 @@ class VppPapiProvider(object):
next_hop_via_label=MPLS_LABEL_INVALID,
create_vrf_if_needed=0,
is_add=1,
- l2_only=0):
+ l2_only=0,
+ is_multicast=0):
"""
:param dst_address_length:
@@ -956,8 +964,8 @@ class VppPapiProvider(object):
: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)
+ :param is_multicast: (Default value = 0)
"""
return self.api(
@@ -965,6 +973,7 @@ class VppPapiProvider(object):
{'mt_sw_if_index': tun_sw_if_index,
'mt_is_add': is_add,
'mt_l2_only': l2_only,
+ 'mt_is_multicast': is_multicast,
'mt_next_hop_proto_is_ip4': next_hop_proto_is_ip4,
'mt_next_hop_weight': next_hop_weight,
'mt_next_hop': next_hop_address,
@@ -1469,6 +1478,7 @@ class VppPapiProvider(object):
e_flags,
next_hop_sw_if_index,
i_flags,
+ rpf_id=0,
table_id=0,
create_vrf_if_needed=0,
is_add=1,
@@ -1481,6 +1491,8 @@ class VppPapiProvider(object):
{'next_hop_sw_if_index': next_hop_sw_if_index,
'entry_flags': e_flags,
'itf_flags': i_flags,
+ 'table_id': table_id,
+ 'rpf_id': rpf_id,
'create_vrf_if_needed': create_vrf_if_needed,
'is_add': is_add,
'is_ipv6': is_ipv6,