aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormklyus <mklyus@cisco.com>2019-10-11 08:35:51 +0300
committermklyus <mklyus@cisco.com>2020-01-31 11:20:59 +0300
commit423f360415e2834dd8de065434023b822e2ca3f8 (patch)
treed7a85ebdc6c4b4be982288f5c6ba2f925919f334
parentae838f98fa020d0ad0aa37ab58e02456889c3375 (diff)
MPLS support + loop_vm_arp test fix4.1.0
Change-Id: I17b1b2a97f0bc185d3906250d5f91b4c8fcb9686 Signed-off-by: Max Klyus <mklyus@cisco.com>
-rw-r--r--docs/testing/user/userguide/index.rst1
-rw-r--r--docs/testing/user/userguide/mpls.rst93
-rwxr-xr-xnfvbench/cfg.default.yaml80
-rw-r--r--nfvbench/chain_runner.py25
-rw-r--r--nfvbench/chaining.py36
-rw-r--r--nfvbench/nfvbench.py16
-rw-r--r--nfvbench/specs.py2
-rwxr-xr-xnfvbench/traffic_client.py84
-rw-r--r--nfvbench/traffic_gen/trex_gen.py22
-rw-r--r--nfvbench/utils.py4
-rw-r--r--pylint.rc4
-rw-r--r--test-requirements.txt1
-rw-r--r--test/test_chains.py1
-rw-r--r--test/test_nfvbench.py1
14 files changed, 321 insertions, 49 deletions
diff --git a/docs/testing/user/userguide/index.rst b/docs/testing/user/userguide/index.rst
index 84c79b0..a61fa46 100644
--- a/docs/testing/user/userguide/index.rst
+++ b/docs/testing/user/userguide/index.rst
@@ -25,6 +25,7 @@ Table of Content
examples
advanced
pvpl3
+ mpls
extchains
fluentd
sriov
diff --git a/docs/testing/user/userguide/mpls.rst b/docs/testing/user/userguide/mpls.rst
new file mode 100644
index 0000000..eaa9541
--- /dev/null
+++ b/docs/testing/user/userguide/mpls.rst
@@ -0,0 +1,93 @@
+==========================
+MPLS encapsulation feature
+==========================
+
+This feature allows to generate packets with standard MPLS L2VPN double stack MPLS labels, where the outer label is transport and the inner label is VPN.
+The top layer of a packets encapsulated inside MPLS L2VPN seems to be an Ethernet layer with the rest of the IP stack inside.
+Please refer to RFC-3031 for more details.
+The whole MPLS packet structure looks like the following:
+
+###[ Ethernet ]###
+ dst = ['00:8a:96:bb:14:28']
+ src = 3c:fd:fe:a3:48:7c
+ type = 0x8847
+###[ MPLS ]### <-------------- Outer Label
+ label = 16303
+ cos = 1
+ s = 0
+ ttl = 255
+###[ MPLS ]### <-------------- Inner Label
+ label = 5010
+ cos = 1
+ s = 1
+ ttl = 255
+###[ Ethernet ]###
+ dst = fa:16:3e:bd:02:b5
+ src = 3c:fd:fe:a3:48:7c
+ type = 0x800
+###[ IP ]###
+ version = 4
+ ihl = None
+ tos = 0x0
+ len = None
+ id = 1
+ flags =
+ frag = 0
+ ttl = 64
+ proto = udp
+ chksum = None
+ src = 16.0.0.1
+ dst = 48.0.0.1
+ \options \
+###[ UDP ]###
+ sport = 53
+ dport = 53
+ len = None
+ chksum = None
+
+Example: nfvbench generates mpls traffic port A ----> port B. This example assumes openstack is at the other end of the mpls tunnels.
+Packets generated and sent to port B are delivered to the MPLS domain infrastructure which will transport that packet to the other end
+of the MPLS transport tunnel using the outer label. At that point, the outer label is decapsulated and the inner label is used to
+select the destination openstack network. After decapsulation of the inner label, the resulting L2 frame is then forwarded to the
+destination VM corresponding to the destination MAC. When the VM receives the packet, it is sent back to far end port of the traffic
+generator (port B) using either L2 forwarding or L3 routing though the peer virtual interface. The return packet is then encapsulated
+with the inner label first then outer label to reach nfvbench on port B.
+
+Only 2 MPLS labels stack is supported. If more than two labels stack is required then these operations should be handled by MPLS transport
+domain where nfvbench is attached next-hop mpls router and rest of the mpls domain should be configured accordingly to be able
+pop/swap/push labels and deliver packet to the proper destination based on an initial transport label injected by nfvbench, VPN label
+should stay unchanged until its delivered to PE (compute node).
+Set nfvbench 'mpls' parameter to 'true' to enable MPLS encapsulation.
+When this option is enabled internal networks 'network type' parameter value should be 'mpls'
+MPLS and VxLAN encapsulations are mutual exclusive features if 'mpls' is 'true' then 'vxlan' should be set to 'false' and vise versa.
+no_flow_stats, no_latency_stats, no_latency_streams parameters should be set to 'true' because these features are not supported at the moment.
+In future when these features will be supported they will require special NIC hardware.
+
+Example of 1-chain MPLS configuration:
+ internal_networks:
+ left:
+ network_type: mpls
+ segmentation_id: 5010
+ mpls_transport_labels: 16303
+ physical_network: phys_sriov0
+ right:
+ network_type: mpls
+ segmentation_id: 5011
+ mpls_transport_labels: 16303
+ physical_network: phys_sriov1
+
+Example of 2-chain MPLS configuration:
+ internal_networks:
+ left:
+ network_type: mpls
+ segmentation_id: [5010, 5020]
+ mpls_transport_labels: [16303, 16304]
+ physical_network: phys_sriov0
+ right:
+ network_type: mpls
+ segmentation_id: [5011, 5021]
+ mpls_transport_labels: [16303, 16304]
+ physical_network: phys_sriov1
+
+Example of how to run:
+nfvbench --rate 50000pps --duration 30 --mpls
diff --git a/nfvbench/cfg.default.yaml b/nfvbench/cfg.default.yaml
index 2abc8dc..97f98cd 100755
--- a/nfvbench/cfg.default.yaml
+++ b/nfvbench/cfg.default.yaml
@@ -187,11 +187,14 @@ traffic_generator:
# Leave empty if there is no VLAN tagging required, or specify the VLAN id to use
# for all VxLAN tunneled traffic
vtep_vlan:
- # VxLAN only: local/source vteps IP addresses for port 0 and 1 ['10.1.1.230', '10.1.1.231']
+ # VxLAN and MPLS only: local/source vteps IP addresses for port 0 and 1 ['10.1.1.230', '10.1.1.231']
src_vteps:
# VxLAN only: remote IP address of the remote VTEPs that terminate all tunnels originating from local VTEPs
dst_vtep:
-
+ # The encapsulated L3/MPLS packet needs to traverse L3 or MPLS fabric to reach to its final dst_vtep.
+ # This parameter is required to resolve first next-hop MAC address if it next-hop is not its final dst_vtep.
+ # This parameter is mandatory for MPLS only
+ vtep_gateway_ips:
# L2 ADDRESSING OF UDP PACKETS
# Lists of dest MAC addresses to use on each traffic generator port (one dest MAC per chain)
# Leave empty for PVP, PVVP, EXT with ARP
@@ -369,7 +372,7 @@ loop_vm_name: 'nfvbench-loop-vm'
# segmentation_id: 2001
# physical_network: phys_sriov1
#
-# For multi-chaining and non shared network mode (VLAN, SRIOV, VxLAN):
+# For multi-chaining and non shared network mode (VLAN, SRIOV, VxLAN, MPLS):
# - the segmentation_id field if provided must be a list of values (as many as chains)
# - segmentation_id auto-indexing:
# the segmentation_id field can also be a single value that represents the base value from which
@@ -379,23 +382,42 @@ loop_vm_name: 'nfvbench-loop-vm'
# - the physical_network can be a single name (all VFs to be allocated on same physnet)
# of a list of physnet names to use different PFs
#
-# Example of 2-chain configuration:
-# internal_networks:
-# left:
-# segmentation_id: [2000, 2001]
-# physical_network: phys_sriov0
-# right:
-# segmentation_id: [2010, 2011]
-# physical_network: phys_sriov1
+# Example of 2-chain VLAN configuration:
+# internal_networks:
+# left:
+# segmentation_id: [2000, 2001]
+# physical_network: phys_sriov0
+# right:
+# segmentation_id: [2010, 2011]
+# physical_network: phys_sriov1
+# Equivalent to (using auto-indexing):
+# internal_networks:
+# left:
+# segmentation_id: 2000
+# physical_network: phys_sriov0
+# right:
+# segmentation_id: 2010
+# physical_network: phys_sriov1
#
-# Equivalent to (using auto-indexing):
-# internal_networks:
-# left:
-# segmentation_id: 2000
-# physical_network: phys_sriov0
-# right:
-# segmentation_id: 2010
-# physical_network: phys_sriov1
+# - mpls_transport_labels is used only when MPLS encapsulation is enabled (mpls: true)
+# this parameter doesn't support auto-indexing because this is not a typical scenario
+# expected the list of values in a range 256-1048575, one value per chain is expected
+#
+# In the bellow configuration example 'segmentation_id; contains the inner MPLS label for each chain
+# and 'mpls_transport_labels' contains the outer transport MPLS label for each chain
+# Example of 2-chain MPLS configuration:
+# internal_networks:
+# left:
+# network_type: mpls
+# segmentation_id: [2000, 2001]
+# mpls_transport_labels: [10000, 10000]
+# physical_network: phys_sriov0
+# right:
+# network_type: mpls
+# segmentation_id: [2010, 2011]
+# mpls_transport_labels: [11000, 11000]
+# physical_network: phys_sriov1
+
internal_networks:
left:
@@ -405,6 +427,7 @@ internal_networks:
network_type: 'vlan'
segmentation_id:
physical_network:
+ mpls_transport_labels:
right:
name: 'nfvbench-rnet'
subnet: 'nfvbench-rsubnet'
@@ -412,6 +435,7 @@ internal_networks:
network_type: 'vlan'
segmentation_id:
physical_network:
+ mpls_transport_labels:
middle:
name: 'nfvbench-mnet'
subnet: 'nfvbench-msubnet'
@@ -419,6 +443,7 @@ internal_networks:
network_type: 'vlan'
segmentation_id:
physical_network:
+ mpls_transport_labels:
# IDLE INTERFACES: PVP, PVVP and non shared net only.
# By default each test VM will have 2 virtual interfaces for looping traffic.
@@ -436,7 +461,7 @@ idle_networks:
# Prefix for all idle networks, the final name will append the chain ID and idle index
# e.g. "nfvbench-idle-net.0.4" chain 0 idle index 4
name: 'nfvbench-idle-net'
- # Subnet name to use for all idle subnetworks
+ # Subnet name to use for all idle subnetworks
subnet: 'nfvbench-idle-subnet'
# CIDR to use for all idle networks (value should not matter)
cidr: '192.169.1.0/24'
@@ -537,7 +562,7 @@ use_sriov_middle_net: false
# external_networks:
# left: ['ext-lnet', 'ext-lnet2']
# right: ['ext-rnet', 'ext-rnet2']
-#
+#
external_networks:
left:
right:
@@ -578,14 +603,23 @@ edge_networks:
# Use 'true' to enable VXLAN encapsulation support and sent by the traffic generator
# When this option enabled internal networks 'network type' parameter value should be 'vxlan'
+# VxLAN and MPLS encapsulations are mutual exclusive if 'vxlan' is true then 'mpls' should be false
+# and vise versa
vxlan: false
-
+# Use 'true' to enable MPLS encapsulation support and sent by the traffic generator
+# When this option enabled internal networks 'network type' parameter value should be 'mpls'
+# MPLS and VxLAN encapsulations are mutual exclusive if 'mpls' is 'true' then 'vxlan' should be set to 'false'
+# and vise versa. no_flow_stats, no_latency_stats, no_latency_streams should be set to 'true' because these
+# features are not supported at the moment. In future when these features will be supported they will require
+# special NIC hardware. Only 2 label stack supported at the moment where one label is transport and another
+# is VPN for more details please refer to 'mpls_transport_labels' and 'segmentation_id' in networks configuration
+mpls: false
# Use 'true' to enable VLAN tagging of packets generated and sent by the traffic generator
# Leave empty or set to false if you do not want the traffic generator to insert the VLAN tag (this is
# needed for example if VLAN tagging is enabled on switch (access mode) or if you want to hook
# directly to a NIC).
# By default is set to true (which is the nominal use case with TOR and trunk mode to Trex ports)
-# If VxLAN is enabled, this option should be set to false (vlan tagging for encapsulated packets
+# If VxLAN or MPLS are enabled, this option should be set to false (vlan tagging for encapsulated packets
# is not supported). Use the vtep_vlan option to enable vlan tagging for the VxLAN overlay network.
vlan_tagging: true
diff --git a/nfvbench/chain_runner.py b/nfvbench/chain_runner.py
index bb35426..62a3751 100644
--- a/nfvbench/chain_runner.py
+++ b/nfvbench/chain_runner.py
@@ -96,6 +96,22 @@ class ChainRunner(object):
gen_config.set_vxlan_endpoints(1, src_vteps[1], dst_vtep)
self.config['vxlan_gen_config'] = gen_config
+ if config.mpls:
+ # MPLS VPN is discovered from the networks
+ src_vteps = gen_config.gen_config.src_vteps
+ vtep_gateway_ips = gen_config.gen_config.vtep_gateway_ips
+ gen_config.set_mpls_inner_labels(0, self.chain_manager.get_chain_mpls_inner_labels(0))
+ gen_config.set_mpls_inner_labels(1, self.chain_manager.get_chain_mpls_inner_labels(1))
+ outer_mpls_labels_left = self.config.internal_networks.left.mpls_transport_labels
+ outer_mpls_labels_right = self.config.internal_networks.right.mpls_transport_labels
+ if outer_mpls_labels_left or outer_mpls_labels_right:
+ gen_config.set_mpls_outer_labels(0, outer_mpls_labels_left)
+ gen_config.set_mpls_outer_labels(1, outer_mpls_labels_right)
+ # Configuring source an remote VTEPs on TREx interfaces
+ gen_config.set_mpls_peers(0, src_vteps[0], vtep_gateway_ips[0])
+ gen_config.set_mpls_peers(1, src_vteps[1], vtep_gateway_ips[1])
+ self.config['mpls_gen_config'] = gen_config
+
# get an instance of the stats manager
self.stats_manager = StatsManager(self)
LOG.info('ChainRunner initialized')
@@ -103,8 +119,8 @@ class ChainRunner(object):
def __setup_traffic(self):
self.traffic_client.setup()
if not self.config.no_traffic:
- # ARP is needed for EXT chain or VxLAN overlay unless disabled explicitly
- if (self.config.service_chain == ChainType.EXT or
+ # ARP is needed for EXT chain or VxLAN overlay or MPLS unless disabled explicitly
+ if (self.config.service_chain == ChainType.EXT or self.config.mpls or
self.config.vxlan or self.config.l3_router or self.config.loop_vm_arp)\
and not self.config.no_arp:
self.traffic_client.ensure_arp_successful()
@@ -167,10 +183,9 @@ class ChainRunner(object):
LOG.info('Starting %dx%s benchmark...', self.config.service_chain_count, self.chain_name)
self.stats_manager.create_worker()
- if self.config.vxlan:
- # Configure vxlan tunnels
+ if self.config.vxlan or self.config.mpls:
+ # Configure vxlan or mpls tunnels
self.stats_manager.worker.config_interfaces()
-
self.__setup_traffic()
results[self.chain_name] = {'result': self.__get_chain_result()}
diff --git a/nfvbench/chaining.py b/nfvbench/chaining.py
index 71693be..b9ed48b 100644
--- a/nfvbench/chaining.py
+++ b/nfvbench/chaining.py
@@ -370,6 +370,15 @@ class ChainNetwork(object):
return self.network['provider:segmentation_id']
+ def get_mpls_inner_label(self):
+ """
+ Extract MPLS VPN Label for this network.
+
+ :return: MPLS VPN Label for this network
+ """
+
+ return self.network['provider:segmentation_id']
+
def delete(self):
"""Delete this network."""
if not self.reuse and self.network:
@@ -931,6 +940,20 @@ class Chain(object):
port_index = -1
return self.networks[port_index].get_vxlan()
+ def get_mpls_inner_label(self, port_index):
+ """Get the MPLS VPN Label on a given port.
+
+ port_index: left port is 0, right port is 1
+ return: the mpls_label_id or None if there is no mpls
+ """
+ # for port 1 we need to return the MPLS Label of the last network in the chain
+ # The networks array contains 2 networks for PVP [left, right]
+ # and 3 networks in the case of PVVP [left.middle,right]
+ if port_index:
+ # this will pick the last item in array
+ port_index = -1
+ return self.networks[port_index].get_mpls_inner_label()
+
def get_dest_mac(self, port_index):
"""Get the dest MAC on a given port.
@@ -1248,7 +1271,6 @@ class ChainManager(object):
for chain in self.chains:
instances.extend(chain.get_instances())
initial_instance_count = len(instances)
- # Give additional 10 seconds per VM
max_retries = (self.config.check_traffic_time_sec + (initial_instance_count - 1) * 10 +
self.config.generic_poll_sec - 1) / self.config.generic_poll_sec
retry = 0
@@ -1426,6 +1448,18 @@ class ChainManager(object):
# no openstack
raise ChainException('VxLAN is only supported with OpenStack and with admin user')
+ def get_chain_mpls_inner_labels(self, port_index):
+ """Get the list of per chain MPLS VPN Labels on a given port.
+
+ port_index: left port is 0, right port is 1
+ return: a MPLSs ID list indexed by the chain index or None if no mpls
+ """
+ if self.chains and self.is_admin:
+ return [self.chains[chain_index].get_mpls_inner_label(port_index)
+ for chain_index in range(self.chain_count)]
+ # no openstack
+ raise ChainException('MPLS is only supported with OpenStack and with admin user')
+
def get_dest_macs(self, port_index):
"""Get the list of per chain dest MACs on a given port.
diff --git a/nfvbench/nfvbench.py b/nfvbench/nfvbench.py
index 168c545..50b96b6 100644
--- a/nfvbench/nfvbench.py
+++ b/nfvbench/nfvbench.py
@@ -233,11 +233,14 @@ class NFVBench(object):
raise Exception('vif_multiqueue_size (%d) must be in [1..8]' %
config.vif_multiqueue_size)
- # VxLAN sanity checks
- if config.vxlan:
+ # VxLAN and MPLS sanity checks
+ if config.vxlan or config.mpls:
if config.vlan_tagging:
config.vlan_tagging = False
- LOG.info('VxLAN: vlan_tagging forced to False '
+ config.no_latency_streams = True
+ config.no_latency_stats = True
+ config.no_flow_stats = True
+ LOG.info('VxLAN or MPLS: vlan_tagging forced to False '
'(inner VLAN tagging must be disabled)')
self.config_plugin.validate_config(config, self.specs.openstack)
@@ -359,6 +362,11 @@ def _parse_opts_from_cli():
action='store_true',
help='Enable VxLan encapsulation')
+ parser.add_argument('--mpls', dest='mpls',
+ default=None,
+ action='store_true',
+ help='Enable MPLS encapsulation')
+
parser.add_argument('--no-cleanup', dest='no_cleanup',
default=None,
action='store_true',
@@ -602,6 +610,8 @@ def main():
config.compute_nodes = opts.hypervisor
if opts.vxlan:
config.vxlan = True
+ if opts.mpls:
+ config.mpls = True
if opts.restart:
config.restart = True
if opts.service_mode:
diff --git a/nfvbench/specs.py b/nfvbench/specs.py
index 75fe703..ec5e24e 100644
--- a/nfvbench/specs.py
+++ b/nfvbench/specs.py
@@ -17,11 +17,13 @@
class Encaps(object):
VLAN = "VLAN"
VxLAN = "VxLAN"
+ MPLS = "MPLS"
NO_ENCAPS = "NONE"
encaps_mapping = {
'VLAN': VLAN,
'VXLAN': VxLAN,
+ 'MPLS': MPLS,
'NONE': NO_ENCAPS
}
diff --git a/nfvbench/traffic_client.py b/nfvbench/traffic_client.py
index a8573b0..062d414 100755
--- a/nfvbench/traffic_client.py
+++ b/nfvbench/traffic_client.py
@@ -26,6 +26,9 @@ from netaddr import IPNetwork
from trex.stl.api import Ether
from trex.stl.api import STLError
from trex.stl.api import UDP
+# pylint: disable=wrong-import-order
+from scapy.contrib.mpls import MPLS # flake8: noqa
+# pylint: enable=wrong-import-order
# pylint: enable=import-error
from .log import LOG
@@ -36,7 +39,6 @@ from .stats_collector import IterationCollector
from .traffic_gen import traffic_utils as utils
from .utils import cast_integer
-
class TrafficClientException(Exception):
"""Generic traffic client exception."""
@@ -158,6 +160,9 @@ class Device(object):
self.vtep_vlan = None
self.vtep_src_mac = None
self.vxlan = False
+ self.mpls = False
+ self.inner_labels = None
+ self.outer_labels = None
self.pci = generator_config.interfaces[port].pci
self.mac = None
self.dest_macs = None
@@ -241,10 +246,25 @@ class Device(object):
LOG.info("Port %d: src_vtep %s, dst_vtep %s", self.port,
self.vtep_src_ip, self.vtep_dst_ip)
+ def set_mpls_peers(self, src_ip, dst_ip):
+ self.mpls = True
+ self.vtep_dst_ip = dst_ip
+ self.vtep_src_ip = src_ip
+ LOG.info("Port %d: src_mpls_vtep %s, mpls_peer_ip %s", self.port,
+ self.vtep_src_ip, self.vtep_dst_ip)
+
def set_vxlans(self, vnis):
self.vnis = vnis
LOG.info("Port %d: VNIs %s", self.port, self.vnis)
+ def set_mpls_inner_labels(self, labels):
+ self.inner_labels = labels
+ LOG.info("Port %d: MPLS Inner Labels %s", self.port, self.inner_labels)
+
+ def set_mpls_outer_labels(self, labels):
+ self.outer_labels = labels
+ LOG.info("Port %d: MPLS Outer Labels %s", self.port, self.outer_labels)
+
def set_gw_ip(self, gateway_ip):
self.gw_ip_block = IpBlock(gateway_ip,
self.generator_config.gateway_ip_addrs_step,
@@ -296,11 +316,15 @@ class Device(object):
'vlan_tag': self.vlans[chain_idx] if self.vlans else None,
'vxlan': self.vxlan,
'vtep_vlan': self.vtep_vlan if self.vtep_vlan else None,
- 'vtep_src_mac': self.mac if self.vxlan is True else None,
- 'vtep_dst_mac': self.vtep_dst_mac if self.vxlan is True else None,
+ 'vtep_src_mac': self.mac if (self.vxlan or self.mpls) else None,
+ 'vtep_dst_mac': self.vtep_dst_mac if (self.vxlan or self.mpls) else None,
'vtep_dst_ip': self.vtep_dst_ip if self.vxlan is True else None,
'vtep_src_ip': self.vtep_src_ip if self.vxlan is True else None,
- 'net_vni': self.vnis[chain_idx] if self.vxlan is True else None
+ 'net_vni': self.vnis[chain_idx] if self.vxlan is True else None,
+ 'mpls': self.mpls,
+ 'mpls_outer_label': self.outer_labels[chain_idx] if self.mpls is True else None,
+ 'mpls_inner_label': self.inner_labels[chain_idx] if self.mpls is True else None
+
})
# after first chain, fall back to the flow count for all other chains
cur_chain_flow_count = flows_per_chain
@@ -445,6 +469,28 @@ class GeneratorConfig(object):
(vxlans, self.config.service_chain_count))
self.devices[port_index].set_vxlans(vxlans)
+ def set_mpls_inner_labels(self, port_index, labels):
+ """Set the list of MPLS Labels to use indexed by the chain id on given port.
+
+ port_index: the port for which Labels must be set
+ Labels: a list of Labels lists indexed by chain id
+ """
+ if len(labels) != self.config.service_chain_count:
+ raise TrafficClientException('Inner MPLS list %s must have %d entries' %
+ (labels, self.config.service_chain_count))
+ self.devices[port_index].set_mpls_inner_labels(labels)
+
+ def set_mpls_outer_labels(self, port_index, labels):
+ """Set the list of MPLS Labels to use indexed by the chain id on given port.
+
+ port_index: the port for which Labels must be set
+ Labels: a list of Labels lists indexed by chain id
+ """
+ if len(labels) != self.config.service_chain_count:
+ raise TrafficClientException('Outer MPLS list %s must have %d entries' %
+ (labels, self.config.service_chain_count))
+ self.devices[port_index].set_mpls_outer_labels(labels)
+
def set_vtep_vlan(self, port_index, vlan):
"""Set the vtep vlan to use indexed by the chain id on given port.
port_index: the port for which VLAN must be set
@@ -454,6 +500,9 @@ class GeneratorConfig(object):
def set_vxlan_endpoints(self, port_index, src_ip, dst_ip):
self.devices[port_index].set_vxlan_endpoints(src_ip, dst_ip)
+ def set_mpls_peers(self, port_index, src_ip, dst_ip):
+ self.devices[port_index].set_mpls_peers(src_ip, dst_ip)
+
@staticmethod
def __match_generator_profile(traffic_generator, generator_profile):
gen_config = AttrDict(traffic_generator)
@@ -607,6 +656,9 @@ class TrafficClient(object):
get_mac_id = lambda packet: packet['binary'][60:66]
elif self.config.vxlan:
get_mac_id = lambda packet: packet['binary'][56:62]
+ elif self.config.mpls:
+ get_mac_id = lambda packet: packet['binary'][24:30]
+ # mpls_transport_label = lambda packet: packet['binary'][14:18]
else:
get_mac_id = lambda packet: packet['binary'][6:12]
for it in range(retry_count):
@@ -624,11 +676,18 @@ class TrafficClient(object):
for packet in self.gen.packet_list:
mac_id = get_mac_id(packet).decode('latin-1')
src_mac = ':'.join(["%02x" % ord(x) for x in mac_id])
- if src_mac in mac_map and self.is_udp(packet):
- port, chain = mac_map[src_mac]
- LOG.info('Received packet from mac: %s (chain=%d, port=%d)',
- src_mac, chain, port)
- mac_map.pop(src_mac, None)
+ if self.config.mpls:
+ if src_mac in mac_map and self.is_mpls(packet):
+ port, chain = mac_map[src_mac]
+ LOG.info('Received mpls packet from mac: %s (chain=%d, port=%d)',
+ src_mac, chain, port)
+ mac_map.pop(src_mac, None)
+ else:
+ if src_mac in mac_map and self.is_udp(packet):
+ port, chain = mac_map[src_mac]
+ LOG.info('Received udp packet from mac: %s (chain=%d, port=%d)',
+ src_mac, chain, port)
+ mac_map.pop(src_mac, None)
if not mac_map:
LOG.info('End-to-end connectivity established')
@@ -645,12 +704,16 @@ class TrafficClient(object):
pkt = Ether(packet['binary'])
return UDP in pkt
+ def is_mpls(self, packet):
+ pkt = Ether(packet['binary'])
+ return MPLS in pkt
+
def ensure_arp_successful(self):
"""Resolve all IP using ARP and throw an exception in case of failure."""
dest_macs = self.gen.resolve_arp()
if dest_macs:
# all dest macs are discovered, saved them into the generator config
- if self.config.vxlan:
+ if self.config.vxlan or self.config.mpls:
self.generator_config.set_vtep_dest_macs(0, dest_macs[0])
self.generator_config.set_vtep_dest_macs(1, dest_macs[1])
else:
@@ -676,6 +739,7 @@ class TrafficClient(object):
self.run_config['rates'][idx] = {'rate_pps': self.__convert_rates(rate)['rate_pps']}
self.gen.clear_streamblock()
+
if self.config.no_latency_streams:
LOG.info("Latency streams are disabled")
self.gen.create_traffic(frame_size, self.run_config['rates'], bidirectional,
diff --git a/nfvbench/traffic_gen/trex_gen.py b/nfvbench/traffic_gen/trex_gen.py
index c2e0854..af70cde 100644
--- a/nfvbench/traffic_gen/trex_gen.py
+++ b/nfvbench/traffic_gen/trex_gen.py
@@ -20,6 +20,9 @@ import time
import traceback
from itertools import count
+# pylint: disable=import-error
+from scapy.contrib.mpls import MPLS # flake8: noqa
+# pylint: enable=import-error
from nfvbench.log import LOG
from nfvbench.traffic_server import TRexTrafficServer
from nfvbench.utils import cast_integer
@@ -363,6 +366,17 @@ class TRex(AbstractTrafficGenerator):
op="random")
vm_param = [vxlan_udp_src_fv,
STLVmWrFlowVar(fv_name="vxlan_udp_src", pkt_offset="UDP.sport")]
+ elif stream_cfg['mpls'] is True:
+ encap_level = '0'
+ pkt_base = Ether(src=stream_cfg['vtep_src_mac'], dst=stream_cfg['vtep_dst_mac'])
+ if stream_cfg['vtep_vlan'] is not None:
+ pkt_base /= Dot1Q(vlan=stream_cfg['vtep_vlan'])
+ if stream_cfg['mpls_outer_label'] is not None:
+ pkt_base /= MPLS(label=stream_cfg['mpls_outer_label'], cos=1, s=0, ttl=255)
+ if stream_cfg['mpls_inner_label'] is not None:
+ pkt_base /= MPLS(label=stream_cfg['mpls_inner_label'], cos=1, s=1, ttl=255)
+ # Flow stats and MPLS labels randomization TBD
+ pkt_base /= Ether(src=stream_cfg['mac_src'], dst=stream_cfg['mac_dst'])
else:
encap_level = '0'
pkt_base = Ether(src=stream_cfg['mac_src'], dst=stream_cfg['mac_dst'])
@@ -443,7 +457,7 @@ class TRex(AbstractTrafficGenerator):
if l2frame == 'IMIX':
for ratio, l2_frame_size in zip(IMIX_RATIOS, IMIX_L2_SIZES):
pkt = self._create_pkt(stream_cfg, l2_frame_size)
- if e2e:
+ if e2e or stream_cfg['mpls']:
streams.append(STLStream(packet=pkt,
mode=STLTXCont(pps=ratio)))
else:
@@ -466,8 +480,10 @@ class TRex(AbstractTrafficGenerator):
else:
l2frame_size = int(l2frame)
pkt = self._create_pkt(stream_cfg, l2frame_size)
- if e2e:
+ if e2e or stream_cfg['mpls']:
streams.append(STLStream(packet=pkt,
+ # Flow stats is disabled for MPLS now
+ # flow_stats=STLFlowStats(pg_id=pg_id),
mode=STLTXCont()))
else:
if stream_cfg['vxlan'] is True:
@@ -648,7 +664,7 @@ class TRex(AbstractTrafficGenerator):
dst_macs = [None] * chain_count
dst_macs_count = 0
# the index in the list is the chain id
- if self.config.vxlan:
+ if self.config.vxlan or self.config.mpls:
arps = [
ServiceARP(ctx,
src_ip=device.vtep_src_ip,
diff --git a/nfvbench/utils.py b/nfvbench/utils.py
index 3974fd7..c8c485f 100644
--- a/nfvbench/utils.py
+++ b/nfvbench/utils.py
@@ -113,8 +113,8 @@ def get_intel_pci(nic_slot=None, nic_ports=None):
if nic_slot and nic_ports:
dmidecode = subprocess.check_output(['dmidecode', '-t', 'slot'])
- regex = r"(?<=SlotID:%s).*?(....:..:..\..)" % nic_slot
- match = re.search(regex, dmidecode, flags=re.DOTALL)
+ regex = r"(?<=SlotID:{}).*?(....:..:..\..)".format(nic_slot)
+ match = re.search(regex, dmidecode.decode('utf-8'), flags=re.DOTALL)
if not match:
return None
diff --git a/pylint.rc b/pylint.rc
index 09324be..c54981f 100644
--- a/pylint.rc
+++ b/pylint.rc
@@ -197,7 +197,7 @@ indent-string=' '
max-line-length=100
# Maximum number of lines in a module
-max-module-lines=1500
+max-module-lines=1600
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
@@ -361,7 +361,7 @@ max-args=12
max-attributes=32
# Maximum number of boolean expressions in a if statement
-max-bool-expr=5
+max-bool-expr=6
# Maximum number of branch for function / method body
max-branches=30
diff --git a/test-requirements.txt b/test-requirements.txt
index 18f4952..a7e52ce 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -7,6 +7,7 @@ hacking<0.11,>=0.10.0
coverage>=3.6
discover
python-subunit>=0.0.18
+scapy>=2.3.1
sphinx>=1.4.0
sphinx_rtd_theme>=0.1.9
oslosphinx>=2.5.0 # Apache-2.0
diff --git a/test/test_chains.py b/test/test_chains.py
index 3cf75cb..a9df54f 100644
--- a/test/test_chains.py
+++ b/test/test_chains.py
@@ -72,6 +72,7 @@ def _get_chain_config(sc=ChainType.PVP, scc=1, shared_net=True, rate='1Mpps'):
config.no_flow_stats = False
config.no_latency_stats = False
config.no_latency_streams = False
+ config.loop_vm_arp = True
return config
def test_chain_runner_ext_no_openstack():
diff --git a/test/test_nfvbench.py b/test/test_nfvbench.py
index 4a8a574..fa0e098 100644
--- a/test/test_nfvbench.py
+++ b/test/test_nfvbench.py
@@ -341,6 +341,7 @@ def _get_dummy_tg_config(chain_type, rate, scc=1, fc=10, step_ip='0.0.0.1',
def _get_traffic_client():
config = _get_dummy_tg_config('PVP', 'ndr_pdr')
config['vxlan'] = False
+ config['mpls'] = False
config['ndr_run'] = True
config['pdr_run'] = True
config['generator_profile'] = 'dummy'