NFVBENCH-158 Allow multiple UDP ports in traffic generation
[nfvbench.git] / nfvbench / traffic_gen / trex_gen.py
index 53786f7..189c3e5 100644 (file)
@@ -20,17 +20,14 @@ 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
 from nfvbench.utils import timeout
 from nfvbench.utils import TimeoutError
-from traffic_base import AbstractTrafficGenerator
-from traffic_base import TrafficGeneratorException
-import traffic_utils as utils
-from traffic_utils import IMIX_AVG_L2_FRAME_SIZE
-from traffic_utils import IMIX_L2_SIZES
-from traffic_utils import IMIX_RATIOS
 
 # pylint: disable=import-error
 from trex.common.services.trex_service_arp import ServiceARP
@@ -57,9 +54,15 @@ from trex.stl.api import ThreeBytesField
 from trex.stl.api import UDP
 from trex.stl.api import XByteField
 
-
 # pylint: enable=import-error
 
+from .traffic_base import AbstractTrafficGenerator
+from .traffic_base import TrafficGeneratorException
+from . import traffic_utils as utils
+from .traffic_utils import IMIX_AVG_L2_FRAME_SIZE
+from .traffic_utils import IMIX_L2_SIZES
+from .traffic_utils import IMIX_RATIOS
+
 class VXLAN(Packet):
     """VxLAN class."""
 
@@ -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'])
@@ -373,19 +387,28 @@ class TRex(AbstractTrafficGenerator):
         udp_args = {}
         if stream_cfg['udp_src_port']:
             udp_args['sport'] = int(stream_cfg['udp_src_port'])
+            udp_args['sport_step'] = int(stream_cfg['udp_port_step'])
+            udp_args['sport_max'] = int(stream_cfg['udp_src_port_max'])
         if stream_cfg['udp_dst_port']:
             udp_args['dport'] = int(stream_cfg['udp_dst_port'])
-        pkt_base /= IP() / UDP(**udp_args)
+            udp_args['dport_step'] = int(stream_cfg['udp_port_step'])
+            udp_args['dport_max'] = int(stream_cfg['udp_dst_port_max'])
 
+        pkt_base /= IP(src=stream_cfg['ip_src_addr'], dst=stream_cfg['ip_dst_addr']) / \
+                    UDP(dport=udp_args['dport'], sport=udp_args['sport'])
+        if stream_cfg['ip_src_static'] is True:
+            src_max_ip_value = stream_cfg['ip_src_addr']
+        else:
+            src_max_ip_value = stream_cfg['ip_src_addr_max']
         if stream_cfg['ip_addrs_step'] == 'random':
-            src_fv = STLVmFlowVarRepeatableRandom(
+            src_fv_ip = STLVmFlowVarRepeatableRandom(
                 name="ip_src",
                 min_value=stream_cfg['ip_src_addr'],
-                max_value=stream_cfg['ip_src_addr_max'],
+                max_value=src_max_ip_value,
                 size=4,
                 seed=random.randint(0, 32767),
                 limit=stream_cfg['ip_src_count'])
-            dst_fv = STLVmFlowVarRepeatableRandom(
+            dst_fv_ip = STLVmFlowVarRepeatableRandom(
                 name="ip_dst",
                 min_value=stream_cfg['ip_dst_addr'],
                 max_value=stream_cfg['ip_dst_addr_max'],
@@ -393,14 +416,14 @@ class TRex(AbstractTrafficGenerator):
                 seed=random.randint(0, 32767),
                 limit=stream_cfg['ip_dst_count'])
         else:
-            src_fv = STLVmFlowVar(
+            src_fv_ip = STLVmFlowVar(
                 name="ip_src",
                 min_value=stream_cfg['ip_src_addr'],
-                max_value=stream_cfg['ip_src_addr'],
+                max_value=src_max_ip_value,
                 size=4,
                 op="inc",
                 step=stream_cfg['ip_addrs_step'])
-            dst_fv = STLVmFlowVar(
+            dst_fv_ip = STLVmFlowVar(
                 name="ip_dst",
                 min_value=stream_cfg['ip_dst_addr'],
                 max_value=stream_cfg['ip_dst_addr_max'],
@@ -408,18 +431,49 @@ class TRex(AbstractTrafficGenerator):
                 op="inc",
                 step=stream_cfg['ip_addrs_step'])
 
-        vm_param.extend([
-            src_fv,
+        if stream_cfg['udp_port_step'] == 'random':
+            src_fv_port = STLVmFlowVarRepeatableRandom(
+                name="p_src",
+                min_value=udp_args['sport'],
+                max_value=udp_args['sport_max'],
+                size=2,
+                seed=random.randint(0, 32767),
+                limit=udp_args['udp_src_count'])
+            dst_fv_port = STLVmFlowVarRepeatableRandom(
+                name="p_dst",
+                min_value=udp_args['dport'],
+                max_value=udp_args['dport_max'],
+                size=2,
+                seed=random.randint(0, 32767),
+                limit=stream_cfg['udp_dst_count'])
+        else:
+            src_fv_port = STLVmFlowVar(
+                name="p_src",
+                min_value=udp_args['sport'],
+                max_value=udp_args['sport_max'],
+                size=2,
+                op="inc",
+                step=udp_args['sport_step'])
+            dst_fv_port = STLVmFlowVar(
+                name="p_dst",
+                min_value=udp_args['dport'],
+                max_value=udp_args['dport_max'],
+                size=2,
+                op="inc",
+                step=udp_args['dport_step'])
+        vm_param = [
+            src_fv_ip,
             STLVmWrFlowVar(fv_name="ip_src", pkt_offset="IP:{}.src".format(encap_level)),
-            dst_fv,
-            STLVmWrFlowVar(fv_name="ip_dst", pkt_offset="IP:{}.dst".format(encap_level))
-        ])
-
-        for encap in range(int(encap_level), -1, -1):
-            # Fixing the checksums for all encap levels
-            vm_param.append(STLVmFixChecksumHw(l3_offset="IP:{}".format(encap),
-                                               l4_offset="UDP:{}".format(encap),
-                                               l4_type=CTRexVmInsFixHwCs.L4_TYPE_UDP))
+            src_fv_port,
+            STLVmWrFlowVar(fv_name="p_src", pkt_offset="UDP:{}.sport".format(encap_level)),
+            dst_fv_ip,
+            STLVmWrFlowVar(fv_name="ip_dst", pkt_offset="IP:{}.dst".format(encap_level)),
+            dst_fv_port,
+            STLVmWrFlowVar(fv_name="p_dst", pkt_offset="UDP:{}.dport".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'
 
         return STLPktBuilder(pkt=pkt_base / pad,
@@ -443,7 +497,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 +520,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:
@@ -511,7 +567,7 @@ class TRex(AbstractTrafficGenerator):
     def __connect_after_start(self):
         # after start, Trex may take a bit of time to initialize
         # so we need to retry a few times
-        for it in xrange(self.config.generic_retry_count):
+        for it in range(self.config.generic_retry_count):
             try:
                 time.sleep(1)
                 self.client.connect()
@@ -519,7 +575,7 @@ class TRex(AbstractTrafficGenerator):
             except Exception as ex:
                 if it == (self.config.generic_retry_count - 1):
                     raise
-                LOG.info("Retrying connection to TRex (%s)...", ex.message)
+                LOG.info("Retrying connection to TRex (%s)...", ex.msg)
 
     def connect(self):
         """Connect to the TRex server."""
@@ -584,7 +640,7 @@ class TRex(AbstractTrafficGenerator):
             if os.path.isfile(logpath):
                 # Wait for TRex to finish writing error message
                 last_size = 0
-                for _ in xrange(self.config.generic_retry_count):
+                for _ in range(self.config.generic_retry_count):
                     size = os.path.getsize(logpath)
                     if size == last_size:
                         # probably not writing anymore
@@ -609,7 +665,7 @@ class TRex(AbstractTrafficGenerator):
         LOG.info("Restarting TRex ...")
         self.__stop_server()
         # Wait for server stopped
-        for _ in xrange(self.config.generic_retry_count):
+        for _ in range(self.config.generic_retry_count):
             time.sleep(1)
             if not self.client.is_connected():
                 LOG.info("TRex is stopped...")
@@ -625,7 +681,7 @@ class TRex(AbstractTrafficGenerator):
                     self.client.release(ports=ports)
                 self.client.server_shutdown()
             except STLError as e:
-                LOG.warn('Unable to stop TRex. Error: %s', e)
+                LOG.warning('Unable to stop TRex. Error: %s', e)
         else:
             LOG.info('Using remote TRex. Unable to stop TRex')
 
@@ -648,7 +704,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,
@@ -690,12 +746,12 @@ class TRex(AbstractTrafficGenerator):
                     arp_dest_macs[port] = dst_macs
                     LOG.info('ARP resolved successfully for port %s', port)
                     break
-                else:
-                    retry = attempt + 1
-                    LOG.info('Retrying ARP for: %s (retry %d/%d)',
-                             unresolved, retry, self.config.generic_retry_count)
-                    if retry < self.config.generic_retry_count:
-                        time.sleep(self.config.generic_poll_sec)
+
+                retry = attempt + 1
+                LOG.info('Retrying ARP for: %s (retry %d/%d)',
+                         unresolved, retry, self.config.generic_retry_count)
+                if retry < self.config.generic_retry_count:
+                    time.sleep(self.config.generic_poll_sec)
             else:
                 LOG.error('ARP timed out for port %s (resolved %d out of %d)',
                           port,
@@ -833,8 +889,8 @@ class TRex(AbstractTrafficGenerator):
         bpf_filter = "ether dst %s or ether dst %s" % (src_mac_list[0], src_mac_list[1])
         # ports must be set in service in order to enable capture
         self.client.set_service_mode(ports=self.port_handle)
-        self.capture_id = self.client.start_capture(rx_ports=self.port_handle,
-                                                    bpf_filter=bpf_filter)
+        self.capture_id = self.client.start_capture \
+            (rx_ports=self.port_handle, bpf_filter=bpf_filter)
 
     def fetch_capture_packets(self):
         """Fetch capture packets in capture mode."""