aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorahothan <ahothan@cisco.com>2018-11-20 09:52:00 -0800
committerahothan <ahothan@cisco.com>2018-11-20 10:09:21 -0800
commit8b1b3e5c7fb91f450b2abe4a8201bcfec14b2bb9 (patch)
tree56e00816633de94b848e2951673e52d56903f797
parent4988edf6afb74026db81677f25877b27b8fcfc05 (diff)
NFVBENCH-111 Add support for VxLAN3.0.0
Change-Id: I7d9d7ccb6be7445e625ec520d22c5f045b56d5ff Signed-off-by: ahothan <ahothan@cisco.com>
-rw-r--r--docs/testing/user/userguide/readme.rst4
-rwxr-xr-xnfvbench/cfg.default.yaml23
-rw-r--r--nfvbench/chain_runner.py30
-rw-r--r--nfvbench/chain_workers.py3
-rw-r--r--nfvbench/chaining.py86
-rw-r--r--nfvbench/nfvbench.py21
-rwxr-xr-xnfvbench/traffic_client.py100
-rw-r--r--nfvbench/traffic_gen/trex.py78
-rw-r--r--nfvbench/traffic_server.py3
-rw-r--r--test/mock_trex.py5
-rw-r--r--test/test_chains.py2
-rw-r--r--test/test_nfvbench.py1
12 files changed, 321 insertions, 35 deletions
diff --git a/docs/testing/user/userguide/readme.rst b/docs/testing/user/userguide/readme.rst
index 9915653..04b7fb1 100644
--- a/docs/testing/user/userguide/readme.rst
+++ b/docs/testing/user/userguide/readme.rst
@@ -18,6 +18,7 @@ NFVbench supports the following main measurement capabilities:
- built-in loopback VNFs based on fast L2 or L3 forwarders running in VMs
- configurable number of flows and service chains
- configurable traffic direction (single or bi-directional)
+- can support optional VLAN tagging (dot1q) or VxLAN overlays
NDR is the highest throughput achieved without dropping packets.
@@ -191,5 +192,4 @@ NFVbench is agnostic of the virtual switch implementation and has been tested wi
Limitations
***********
-NFVbench only supports VLAN with OpenStack.
-VxLAN overlays is planned for a coming release.
+VxLAN: latency measurement and per chain stats is not available in the first VxLAN release
diff --git a/nfvbench/cfg.default.yaml b/nfvbench/cfg.default.yaml
index bc40af4..3138420 100755
--- a/nfvbench/cfg.default.yaml
+++ b/nfvbench/cfg.default.yaml
@@ -152,6 +152,23 @@ traffic_generator:
udp_src_port:
udp_dst_port:
+ # VxLAN only: optionally specify what VLAN tag to use for the VxLAN overlay
+ # This is used if the vxlan tunnels are running on a specific VLAN.
+ # 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: VNI range for VXLAN encapsulation [start_vni, end_vni] [5000, 6000]
+ # VNI can have a value from range 5000-16777216
+ # For PVP, VNIs are allocated consecutively - 2 per each chain
+ # Chain 1: 5000, 5001; Chain 2: 5002, 5003; Chain X: 5000+x, 5000+x+1
+ # For PVVP scenario VNIs allocated consecutively - 3 per each chain
+ # Chain 1: 5000, 5001, 5002; Chain 2: 5003, 5004, 5005; Chain X: 5000+x, 5000+x+1, 5000+x+1
+ vnis:
+ # VxLAN 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:
+
# 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
@@ -304,11 +321,17 @@ external_networks:
left: 'ext-lnet'
right: 'ext-rnet'
+# 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: 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
+# is not supported). Use the vtep_vlan option to enable vlan tagging for the VxLAN overlay network.
vlan_tagging: true
# Used only in the case of EXT chain and no openstack to specify the VLAN IDs to use.
diff --git a/nfvbench/chain_runner.py b/nfvbench/chain_runner.py
index 876fec2..e38cfcd 100644
--- a/nfvbench/chain_runner.py
+++ b/nfvbench/chain_runner.py
@@ -79,6 +79,24 @@ class ChainRunner(object):
gen_config.set_dest_macs(0, self.chain_manager.get_dest_macs(0))
gen_config.set_dest_macs(1, self.chain_manager.get_dest_macs(1))
+ if config.vxlan:
+ # VXLAN is discovered from the networks
+ vtep_vlan = gen_config.gen_config.vtep_vlan
+ src_vteps = gen_config.gen_config.src_vteps
+ dst_vtep = gen_config.gen_config.dst_vtep
+ int_nets = self.config.internal_networks
+ network_type = set(
+ [int_nets[net].get('network_type') for net in int_nets])
+ if 'vxlan' in network_type:
+ gen_config.set_vxlans(0, self.chain_manager.get_chain_vxlans(0))
+ gen_config.set_vxlans(1, self.chain_manager.get_chain_vxlans(1))
+ gen_config.set_vtep_vlan(0, vtep_vlan)
+ gen_config.set_vtep_vlan(1, vtep_vlan)
+ # Configuring source an remote VTEPs on TREx interfaces
+ gen_config.set_vxlan_endpoints(0, src_vteps[0], dst_vtep)
+ gen_config.set_vxlan_endpoints(1, src_vteps[1], dst_vtep)
+ self.config['vxlan_gen_config'] = gen_config
+
# get an instance of the stats manager
self.stats_manager = StatsManager(self)
LOG.info('ChainRunner initialized')
@@ -86,7 +104,9 @@ class ChainRunner(object):
def __setup_traffic(self):
self.traffic_client.setup()
if not self.config.no_traffic:
- if self.config.service_chain == ChainType.EXT and not self.config.no_arp:
+ # ARP is needed for EXT chain or VxLAN overlay unless disabled explicitly
+ if (self.config.service_chain == ChainType.EXT or self.config.vxlan) and \
+ not self.config.no_arp:
self.traffic_client.ensure_arp_successful()
self.traffic_client.ensure_end_to_end()
@@ -146,10 +166,12 @@ class ChainRunner(object):
return results
LOG.info('Starting %dx%s benchmark...', self.config.service_chain_count, self.chain_name)
- self.__setup_traffic()
- # now that the dest MAC for all VNFs is known in all cases, it is time to create
- # workers as they might be needed to extract stats prior to sending traffic
self.stats_manager.create_worker()
+ if self.config.vxlan:
+ # Configure vxlan tunnels
+ self.stats_manager.worker.config_interfaces()
+
+ self.__setup_traffic()
results[self.chain_name] = {'result': self.__get_chain_result()}
diff --git a/nfvbench/chain_workers.py b/nfvbench/chain_workers.py
index 7c669d1..0ed2648 100644
--- a/nfvbench/chain_workers.py
+++ b/nfvbench/chain_workers.py
@@ -29,6 +29,9 @@ class BasicWorker(object):
def get_version(self):
return {}
+ def config_interfaces(self):
+ return {}
+
def close(self):
pass
diff --git a/nfvbench/chaining.py b/nfvbench/chaining.py
index 8d717aa..a02bb17 100644
--- a/nfvbench/chaining.py
+++ b/nfvbench/chaining.py
@@ -288,6 +288,16 @@ class ChainNetwork(object):
raise ChainException('Trying to retrieve VLAN id for non VLAN network')
return self.network['provider:segmentation_id']
+ def get_vxlan(self):
+ """
+ Extract VNI for this network.
+
+ :return: VNI ID for this network
+ """
+ if self.network['provider:network_type'] != 'vxlan':
+ raise ChainException('Trying to retrieve VNI for non VXLAN network')
+ return self.network['provider:segmentation_id']
+
def delete(self):
"""Delete this network."""
if not self.reuse and self.network:
@@ -631,6 +641,20 @@ class Chain(object):
port_index = -1
return self.networks[port_index].get_vlan()
+ def get_vxlan(self, port_index):
+ """Get the VXLAN id on a given port.
+
+ port_index: left port is 0, right port is 1
+ return: the vxlan_id or None if there is no vxlan
+ """
+ # for port 1 we need to return the VLAN 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_vxlan()
+
def get_dest_mac(self, port_index):
"""Get the dest MAC on a given port.
@@ -834,6 +858,12 @@ class ChainManager(object):
re_vlan = "[0-9]*$"
self.vlans = [self._check_list('vlans[0]', config.vlans[0], re_vlan),
self._check_list('vlans[1]', config.vlans[1], re_vlan)]
+ if config.vxlan:
+ # make sure there are 2 entries
+ if len(config.vnis) != 2:
+ raise ChainException('The config vnis property must be a list with 2 VNIs')
+ self.vnis = [self._check_list('vnis[0]', config.vnis[0], re_vlan),
+ self._check_list('vnis[1]', config.vnis[1], re_vlan)]
def _get_dest_macs_from_config(self):
re_mac = "[0-9a-fA-F]{2}([-:])[0-9a-fA-F]{2}(\\1[0-9a-fA-F]{2}){4}$"
@@ -930,6 +960,39 @@ class ChainManager(object):
if initial_instance_count:
LOG.info('All instances are active')
+ def _get_vxlan_net_cfg(self, chain_id):
+ int_nets = self.config.internal_networks
+ net_left = int_nets.left
+ net_right = int_nets.right
+ vnis = self.generator_config.vnis
+ chain_id += 1
+ seg_id_left = vnis[0]
+ if self.config.service_chain == ChainType.PVP:
+ if chain_id > 1:
+ seg_id_left = ((chain_id - 1) * 2) + seg_id_left
+ seg_id_right = seg_id_left + 1
+ if (seg_id_left and seg_id_right) > vnis[1]:
+ raise Exception('Segmentation ID is more than allowed '
+ 'value: {}'.format(vnis[1]))
+ net_left['segmentation_id'] = seg_id_left
+ net_right['segmentation_id'] = seg_id_right
+ net_cfg = [net_left, net_right]
+ else:
+ # PVVP
+ net_middle = int_nets.middle
+ if chain_id > 1:
+ seg_id_left = ((chain_id - 1) * 3) + seg_id_left
+ seg_id_middle = seg_id_left + 1
+ seg_id_right = seg_id_left + 2
+ if (seg_id_left and seg_id_right and seg_id_middle) > vnis[1]:
+ raise Exception('Segmentation ID is more than allowed '
+ 'value: {}'.format(vnis[1]))
+ net_left['segmentation_id'] = seg_id_left
+ net_middle['segmentation_id'] = seg_id_middle
+ net_right['segmentation_id'] = seg_id_right
+ net_cfg = [net_left, net_middle, net_right]
+ return net_cfg
+
def get_networks(self, chain_id=None):
"""Get the networks for given EXT, PVP or PVVP chain.
@@ -952,10 +1015,15 @@ class ChainManager(object):
else:
lookup_only = False
int_nets = self.config.internal_networks
- if self.config.service_chain == ChainType.PVP:
- net_cfg = [int_nets.left, int_nets.right]
+ network_type = set([int_nets[net].get('network_type') for net in int_nets])
+ if self.config.vxlan and 'vxlan' in network_type:
+ net_cfg = self._get_vxlan_net_cfg()
else:
- net_cfg = [int_nets.left, int_nets.middle, int_nets.right]
+ # VLAN
+ if self.config.service_chain == ChainType.PVP:
+ net_cfg = [int_nets.left, int_nets.right]
+ else:
+ net_cfg = [int_nets.left, int_nets.middle, int_nets.right]
networks = []
try:
for cfg in net_cfg:
@@ -1048,6 +1116,18 @@ class ChainManager(object):
# no openstack
return self.vlans[port_index]
+ def get_chain_vxlans(self, port_index):
+ """Get the list of per chain VNIs id on a given port.
+
+ port_index: left port is 0, right port is 1
+ return: a VNIs ID list indexed by the chain index or None if no vlan tagging
+ """
+ if self.chains:
+ return [self.chains[chain_index].get_vxlan(port_index)
+ for chain_index in range(self.chain_count)]
+ # no openstack
+ return self.vnis[port_index]
+
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 0d6da7f..f06c593 100644
--- a/nfvbench/nfvbench.py
+++ b/nfvbench/nfvbench.py
@@ -66,6 +66,16 @@ class NFVBench(object):
self.specs.set_openstack_spec(openstack_spec)
self.vni_ports = []
sys.stdout.flush()
+ self.check_options()
+
+ def check_options(self):
+ if self.base_config.vxlan:
+ if self.base_config.vlan_tagging:
+ raise Exception(
+ 'Inner VLAN tagging is not currently supported for VXLAN')
+ vtep_vlan = self.base_config.traffic_generator.get('vtep_vlan')
+ if vtep_vlan is None:
+ LOG.warning('Warning: VXLAN mode enabled, but VTEP vlan is not defined')
def set_notifier(self, notifier):
self.notifier = notifier
@@ -216,7 +226,7 @@ class NFVBench(object):
self.config_plugin.validate_config(config, self.specs.openstack)
-def parse_opts_from_cli():
+def _parse_opts_from_cli():
parser = argparse.ArgumentParser()
parser.add_argument('--status', dest='status',
@@ -280,7 +290,7 @@ def parse_opts_from_cli():
parser.add_argument('--inter-node', dest='inter_node',
default=None,
action='store_true',
- help='run VMs in different compute nodes (PVVP only)')
+ help='(deprecated)')
parser.add_argument('--sriov', dest='sriov',
default=None,
@@ -318,6 +328,11 @@ def parse_opts_from_cli():
action='store_true',
help='Skip vswitch configuration and retrieving of stats')
+ parser.add_argument('--vxlan', dest='vxlan',
+ default=None,
+ action='store_true',
+ help='Enable VxLan encapsulation')
+
parser.add_argument('--no-cleanup', dest='no_cleanup',
default=None,
action='store_true',
@@ -469,7 +484,7 @@ def main():
config_plugin = factory.get_config_plugin_class()(config)
config = config_plugin.get_config()
- opts, unknown_opts = parse_opts_from_cli()
+ opts, unknown_opts = _parse_opts_from_cli()
log.set_level(debug=opts.debug)
if opts.version:
diff --git a/nfvbench/traffic_client.py b/nfvbench/traffic_client.py
index 34d93cf..c9daf40 100755
--- a/nfvbench/traffic_client.py
+++ b/nfvbench/traffic_client.py
@@ -144,17 +144,26 @@ class Device(object):
identified as port 0 or port 1.
"""
- def __init__(self, port, generator_config, vtep_vlan=None):
+ def __init__(self, port, generator_config):
"""Create a new device for a given port."""
self.generator_config = generator_config
self.chain_count = generator_config.service_chain_count
self.flow_count = generator_config.flow_count / 2
self.port = port
self.switch_port = generator_config.interfaces[port].get('switch_port', None)
- self.vtep_vlan = vtep_vlan
+ self.vtep_vlan = None
+ self.vtep_src_mac = None
+ self.vxlan = False
self.pci = generator_config.interfaces[port].pci
self.mac = None
self.dest_macs = None
+ self.vtep_dst_mac = None
+ self.vtep_dst_ip = None
+ if generator_config.vteps is None:
+ self.vtep_src_ip = None
+ else:
+ self.vtep_src_ip = generator_config.vteps[port]
+ self.vnis = None
self.vlans = None
self.ip_addrs = generator_config.ip_addrs[port]
subnet = IPNetwork(self.ip_addrs)
@@ -181,6 +190,15 @@ class Device(object):
"""Get the peer device (device 0 -> device 1, or device 1 -> device 0)."""
return self.generator_config.devices[1 - self.port]
+ def set_vtep_dst_mac(self, dest_macs):
+ """Set the list of dest MACs indexed by the chain id.
+
+ This is only called in 2 cases:
+ - VM macs discovered using openstack API
+ - dest MACs provisioned in config file
+ """
+ self.vtep_dst_mac = map(str, dest_macs)
+
def set_dest_macs(self, dest_macs):
"""Set the list of dest MACs indexed by the chain id.
@@ -206,6 +224,23 @@ class Device(object):
self.vlans = vlans
LOG.info("Port %d: VLANs %s", self.port, self.vlans)
+ def set_vtep_vlan(self, vlan):
+ """Set the vtep vlan to use indexed by specific port."""
+ self.vtep_vlan = vlan
+ self.vxlan = True
+ self.vlan_tagging = None
+ LOG.info("Port %d: VTEP VLANs %s", self.port, self.vtep_vlan)
+
+ def set_vxlan_endpoints(self, src_ip, dst_ip):
+ self.vtep_dst_ip = dst_ip
+ self.vtep_src_ip = src_ip
+ LOG.info("Port %d: src_vtep %s, dst_vtep %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 get_gw_ip(self, chain_index):
"""Retrieve the IP address assigned for the gateway of a given chain."""
return self.gw_ip_block.get_ip(chain_index)
@@ -249,7 +284,14 @@ class Device(object):
'mac_discovery_gw': self.get_gw_ip(chain_idx),
'ip_src_tg_gw': self.tg_gw_ip_block.get_ip(chain_idx),
'ip_dst_tg_gw': peer.tg_gw_ip_block.get_ip(chain_idx),
- 'vlan_tag': self.vlans[chain_idx] if self.vlans else None
+ '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_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
})
# after first chain, fall back to the flow count for all other chains
cur_chain_flow_count = flows_per_chain
@@ -312,6 +354,8 @@ class GeneratorConfig(object):
self.gateway_ips = gen_config.gateway_ip_addrs
self.udp_src_port = gen_config.udp_src_port
self.udp_dst_port = gen_config.udp_dst_port
+ self.vteps = gen_config.get('vteps')
+ self.vnis = gen_config.get('vnis')
self.devices = [Device(port, self) for port in [0, 1]]
# This should normally always be [0, 1]
self.ports = [device.port for device in self.devices]
@@ -344,6 +388,18 @@ class GeneratorConfig(object):
self.devices[port_index].set_dest_macs(dest_macs)
LOG.info('Port %d: dst MAC %s', port_index, [str(mac) for mac in dest_macs])
+ def set_vtep_dest_macs(self, port_index, dest_macs):
+ """Set the list of dest MACs indexed by the chain id on given port.
+
+ port_index: the port for which dest macs must be set
+ dest_macs: a list of dest MACs indexed by chain id
+ """
+ if len(dest_macs) != self.config.service_chain_count:
+ raise TrafficClientException('Dest MAC list %s must have %d entries' %
+ (dest_macs, self.config.service_chain_count))
+ self.devices[port_index].set_vtep_dst_mac(dest_macs)
+ LOG.info('Port %d: vtep dst MAC %s', port_index, set([str(mac) for mac in dest_macs]))
+
def get_dest_macs(self):
"""Return the list of dest macs indexed by port."""
return [dev.get_dest_macs() for dev in self.devices]
@@ -359,6 +415,26 @@ class GeneratorConfig(object):
(vlans, self.config.service_chain_count))
self.devices[port_index].set_vlans(vlans)
+ def set_vxlans(self, port_index, vxlans):
+ """Set the list of vxlans (VNIs) to use indexed by the chain id on given port.
+
+ port_index: the port for which VXLANs must be set
+ VXLANs: a list of VNIs lists indexed by chain id
+ """
+ if len(vxlans) != self.config.service_chain_count:
+ raise TrafficClientException('VXLAN list %s must have %d entries' %
+ (vxlans, self.config.service_chain_count))
+ self.devices[port_index].set_vxlans(vxlans)
+
+ 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
+ """
+ self.devices[port_index].set_vtep_vlan(vlan)
+
+ def set_vxlan_endpoints(self, port_index, src_ip, dst_ip):
+ self.devices[port_index].set_vxlan_endpoints(src_ip, dst_ip)
+
@staticmethod
def __match_generator_profile(traffic_generator, generator_profile):
gen_config = AttrDict(traffic_generator)
@@ -507,6 +583,12 @@ class TrafficClient(object):
for chain, mac in enumerate(dest_macs):
mac_map[mac] = (port, chain)
unique_src_mac_count = len(mac_map)
+ if self.config.vxlan and self.config.traffic_generator.vtep_vlan:
+ get_mac_id = lambda packet: packet['binary'][60:66]
+ elif self.config.vxlan:
+ get_mac_id = lambda packet: packet['binary'][56:62]
+ else:
+ get_mac_id = lambda packet: packet['binary'][6:12]
for it in xrange(retry_count):
self.gen.clear_stats()
self.gen.start_traffic()
@@ -521,8 +603,8 @@ class TrafficClient(object):
self.gen.stop_capture()
for packet in self.gen.packet_list:
- src_mac = packet['binary'][6:12]
- src_mac = ':'.join(["%02x" % ord(x) for x in src_mac])
+ mac_id = get_mac_id(packet)
+ src_mac = ':'.join(["%02x" % ord(x) for x in mac_id])
if src_mac in mac_map:
port, chain = mac_map[src_mac]
LOG.info('Received packet from mac: %s (chain=%d, port=%d)',
@@ -540,8 +622,12 @@ class TrafficClient(object):
dest_macs = self.gen.resolve_arp()
if dest_macs:
# all dest macs are discovered, saved them into the generator config
- self.generator_config.set_dest_macs(0, dest_macs[0])
- self.generator_config.set_dest_macs(1, dest_macs[1])
+ if self.config.vxlan:
+ self.generator_config.set_vtep_dest_macs(0, dest_macs[0])
+ self.generator_config.set_vtep_dest_macs(1, dest_macs[1])
+ else:
+ self.generator_config.set_dest_macs(0, dest_macs[0])
+ self.generator_config.set_dest_macs(1, dest_macs[1])
else:
raise TrafficClientException('ARP cannot be resolved')
diff --git a/nfvbench/traffic_gen/trex.py b/nfvbench/traffic_gen/trex.py
index 6bb0c34..1f460f6 100644
--- a/nfvbench/traffic_gen/trex.py
+++ b/nfvbench/traffic_gen/trex.py
@@ -13,6 +13,7 @@
# under the License.
"""Driver module for TRex traffic generator."""
+import math
import os
import random
import time
@@ -32,10 +33,13 @@ from traffic_utils import IMIX_L2_SIZES
from traffic_utils import IMIX_RATIOS
# pylint: disable=import-error
+from trex_stl_lib.api import bind_layers
from trex_stl_lib.api import CTRexVmInsFixHwCs
from trex_stl_lib.api import Dot1Q
from trex_stl_lib.api import Ether
+from trex_stl_lib.api import FlagsField
from trex_stl_lib.api import IP
+from trex_stl_lib.api import Packet
from trex_stl_lib.api import STLClient
from trex_stl_lib.api import STLError
from trex_stl_lib.api import STLFlowLatencyStats
@@ -48,12 +52,26 @@ from trex_stl_lib.api import STLVmFixChecksumHw
from trex_stl_lib.api import STLVmFlowVar
from trex_stl_lib.api import STLVmFlowVarRepetableRandom
from trex_stl_lib.api import STLVmWrFlowVar
+from trex_stl_lib.api import ThreeBytesField
from trex_stl_lib.api import UDP
+from trex_stl_lib.api import XByteField
from trex_stl_lib.services.trex_stl_service_arp import STLServiceARP
# pylint: enable=import-error
+class VXLAN(Packet):
+ """VxLAN class."""
+
+ _VXLAN_FLAGS = ['R' * 27] + ['I'] + ['R' * 5]
+ name = "VXLAN"
+ fields_desc = [FlagsField("flags", 0x08000000, 32, _VXLAN_FLAGS),
+ ThreeBytesField("vni", 0),
+ XByteField("reserved", 0x00)]
+
+ def mysummary(self):
+ """Summary."""
+ return self.sprintf("VXLAN (vni=%VXLAN.vni%)")
class TRex(AbstractTrafficGenerator):
"""TRex traffic generator driver."""
@@ -221,8 +239,12 @@ class TRex(AbstractTrafficGenerator):
lat = trex_stats['latency'][lat_pg_id]['latency']
# dropped_pkts += lat['err_cntrs']['dropped']
latencies[port].max_usec = get_latency(lat['total_max'])
- latencies[port].min_usec = get_latency(lat['total_min'])
- latencies[port].avg_usec = get_latency(lat['average'])
+ if math.isnan(lat['total_min']):
+ latencies[port].min_usec = 0
+ latencies[port].avg_usec = 0
+ else:
+ latencies[port].min_usec = get_latency(lat['total_min'])
+ latencies[port].avg_usec = get_latency(lat['average'])
except KeyError:
pass
@@ -247,6 +269,10 @@ class TRex(AbstractTrafficGenerator):
results['max_delay_usec'] = total_max
results['avg_delay_usec'] = int(average / self.chain_count)
+ def _bind_vxlan(self):
+ bind_layers(UDP, VXLAN, dport=4789)
+ bind_layers(VXLAN, Ether)
+
def _create_pkt(self, stream_cfg, l2frame_size):
"""Create a packet of given size.
@@ -255,7 +281,20 @@ class TRex(AbstractTrafficGenerator):
# Trex will add the FCS field, so we need to remove 4 bytes from the l2 frame size
frame_size = int(l2frame_size) - 4
- pkt_base = Ether(src=stream_cfg['mac_src'], dst=stream_cfg['mac_dst'])
+ if stream_cfg['vxlan'] is True:
+ self._bind_vxlan()
+ encap_level = '1'
+ 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'])
+ pkt_base /= IP(src=stream_cfg['vtep_src_ip'], dst=stream_cfg['vtep_dst_ip'])
+ pkt_base /= UDP(sport=random.randint(1337, 32767), dport=4789)
+ pkt_base /= VXLAN(vni=stream_cfg['net_vni'])
+ 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'])
+
if stream_cfg['vlan_tag'] is not None:
pkt_base /= Dot1Q(vlan=stream_cfg['vlan_tag'])
@@ -299,11 +338,11 @@ class TRex(AbstractTrafficGenerator):
vm_param = [
src_fv,
- STLVmWrFlowVar(fv_name="ip_src", pkt_offset="IP.src"),
+ STLVmWrFlowVar(fv_name="ip_src", pkt_offset="IP:{}.src".format(encap_level)),
dst_fv,
- STLVmWrFlowVar(fv_name="ip_dst", pkt_offset="IP.dst"),
- STLVmFixChecksumHw(l3_offset="IP",
- l4_offset="UDP",
+ STLVmWrFlowVar(fv_name="ip_dst", pkt_offset="IP:{}.dst".format(encap_level)),
+ STLVmFixChecksumHw(l3_offset="IP:{}".format(encap_level),
+ l4_offset="UDP:{}".format(encap_level),
l4_type=CTRexVmInsFixHwCs.L4_TYPE_UDP)
]
pad = max(0, frame_size - len(pkt_base)) * 'x'
@@ -458,14 +497,23 @@ class TRex(AbstractTrafficGenerator):
dst_macs = [None] * chain_count
dst_macs_count = 0
# the index in the list is the chain id
- arps = [
- STLServiceARP(ctx,
- src_ip=cfg['ip_src_tg_gw'],
- dst_ip=cfg['mac_discovery_gw'],
- # will be None if no vlan tagging
- vlan=cfg['vlan_tag'])
- for cfg in stream_configs
- ]
+ if self.config.vxlan:
+ arps = [
+ STLServiceARP(ctx,
+ src_ip=device.vtep_src_ip,
+ dst_ip=device.vtep_dst_ip,
+ vlan=device.vtep_vlan)
+ for cfg in stream_configs
+ ]
+ else:
+ arps = [
+ STLServiceARP(ctx,
+ src_ip=cfg['ip_src_tg_gw'],
+ dst_ip=cfg['mac_discovery_gw'],
+ # will be None if no vlan tagging
+ vlan=cfg['vlan_tag'])
+ for cfg in stream_configs
+ ]
for attempt in range(self.config.generic_retry_count):
try:
diff --git a/nfvbench/traffic_server.py b/nfvbench/traffic_server.py
index 2239ec3..b2d8367 100644
--- a/nfvbench/traffic_server.py
+++ b/nfvbench/traffic_server.py
@@ -43,8 +43,9 @@ class TRexTrafficServer(TrafficServer):
"""
cfg = self.__save_config(generator_config, filename)
cores = generator_config.cores
+ vtep_vlan = generator_config.gen_config.get('vtep_vlan')
sw_mode = "--software" if generator_config.software_mode else ""
- vlan_opt = "--vlan" if generator_config.vlan_tagging else ""
+ vlan_opt = "--vlan" if (generator_config.vlan_tagging or vtep_vlan) else ""
subprocess.Popen(['nohup', '/bin/bash', '-c',
'./t-rex-64 -i -c {} --iom 0 --no-scapy-server --close-at-end {} '
'{} --cfg {} &> /tmp/trex.log & disown'.format(cores, sw_mode,
diff --git a/test/mock_trex.py b/test/mock_trex.py
index c128e9a..c4ce9d7 100644
--- a/test/mock_trex.py
+++ b/test/mock_trex.py
@@ -55,6 +55,11 @@ except ImportError:
api_mod.STLVmFlowVarRepetableRandom = STLDummy
api_mod.STLVmWrFlowVar = STLDummy
api_mod.UDP = STLDummy
+ api_mod.bind_layers = STLDummy
+ api_mod.FlagsField = STLDummy
+ api_mod.Packet = STLDummy
+ api_mod.ThreeBytesField = STLDummy
+ api_mod.XByteField = STLDummy
services_mod = ModuleType('trex_stl_lib.services')
stl_lib_mod.services = services_mod
diff --git a/test/test_chains.py b/test/test_chains.py
index 109b73b..a9c9ac7 100644
--- a/test/test_chains.py
+++ b/test/test_chains.py
@@ -76,6 +76,7 @@ def test_chain_runner_ext_no_openstack():
config = _get_chain_config(sc=ChainType.EXT)
specs = Specs()
config.vlans = [100, 200]
+ config.vnis = [5000, 6000]
config['traffic_generator']['mac_addrs_left'] = ['00:00:00:00:00:00']
config['traffic_generator']['mac_addrs_right'] = ['00:00:00:00:01:00']
@@ -172,6 +173,7 @@ def _check_nfvbench_openstack(sc=ChainType.PVP, l2_loopback=False):
if l2_loopback:
config.l2_loopback = True
config.vlans = [[100], [200]]
+ config.vnis = [[5000], [6000]]
factory = BasicFactory()
config_plugin = factory.get_config_plugin_class()(config)
config = config_plugin.get_config()
diff --git a/test/test_nfvbench.py b/test/test_nfvbench.py
index f532bba..04778e7 100644
--- a/test/test_nfvbench.py
+++ b/test/test_nfvbench.py
@@ -319,6 +319,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['ndr_run'] = True
config['pdr_run'] = True
config['generator_profile'] = 'dummy'