Merge "trex: Support of SCAPY frame definition"
authorMartin Klozik <martinx.klozik@intel.com>
Wed, 11 Apr 2018 08:54:37 +0000 (08:54 +0000)
committerGerrit Code Review <gerrit@opnfv.org>
Wed, 11 Apr 2018 08:54:37 +0000 (08:54 +0000)
conf/03_traffic.conf
docs/testing/developer/devguide/design/trafficgen_integration_guide.rst
docs/testing/developer/devguide/design/vswitchperf_design.rst
docs/testing/user/configguide/trafficgen.rst
tools/pkt_gen/trex/trex.py

index 8aff2e3..756902f 100644 (file)
@@ -112,7 +112,7 @@ LOG_FILE_TRAFFIC_GEN = 'traffic-gen.log'
 #                      NOTE: It can be modified by vsperf in some scenarios.
 #                      Data type: str
 #                      Default value: "90.90.90.90".
-#        'proto'     - Specifies deflaut protocol type.
+#        'proto'     - Specifies protocol type.
 #                      Please check particular traffic generator implementation
 #                      for supported protocol types.
 #                      Data type: str
@@ -171,6 +171,34 @@ LOG_FILE_TRAFFIC_GEN = 'traffic-gen.log'
 #                      details.
 #                      Data type: str
 #                      Default value: ''
+#    'scapy'         - A dictionary with definition of a frame content for both traffic
+#                      directions. The frame content is defined by a SCAPY notation.
+#                      NOTE: It is supported only by the T-Rex traffic generator.
+#                      Following keywords can be used to refer to the related parts of
+#                      the TRAFFIC dictionary:
+#                           Ether_src   - refers to TRAFFIC['l2']['srcmac']
+#                           Ether_dst   - refers to TRAFFIC['l2']['dstmac']
+#                           IP_proto    - refers to TRAFFIC['l3']['proto']
+#                           IP_PROTO    - refers to upper case version of TRAFFIC['l3']['proto']
+#                           IP_src      - refers to TRAFFIC['l3']['srcip']
+#                           IP_dst      - refers to TRAFFIC['l3']['dstip']
+#                           IP_PROTO_sport - refers to TRAFFIC['l4']['srcport']
+#                           IP_PROTO_dport - refers to TRAFFIC['l4']['dstport']
+#                           Dot1Q_prio  - refers to TRAFFIC['vlan']['priority']
+#                           Dot1Q_id    - refers to TRAFFIC['vlan']['cfi']
+#                           Dot1Q_vlan  - refers to TRAFFIC['vlan']['id']
+#        '0'         - A string with the frame definition for the 1st direction.
+#                      Data type: str
+#                      Default value: 'Ether(src={Ether_src}, dst={Ether_dst})/'
+#                                     'Dot1Q(prio={Dot1Q_prio}, id={Dot1Q_id}, vlan={Dot1Q_vlan})/'
+#                                     'IP(proto={IP_proto}, src={IP_src}, dst={IP_dst})/'
+#                                     '{IP_PROTO}(sport={IP_PROTO_sport}, dport={IP_PROTO_dport})'
+#        '1'         - A string with the frame definition for the 2nd direction.
+#                      Data type: str
+#                      Default value: 'Ether(src={Ether_dst}, dst={Ether_src})/'
+#                                     'Dot1Q(prio={Dot1Q_prio}, id={Dot1Q_id}, vlan={Dot1Q_vlan})/'
+#                                     'IP(proto={IP_proto}, src={IP_dst}, dst={IP_src})/'
+#                                     '{IP_PROTO}(sport={IP_PROTO_dport}, dport={IP_PROTO_sport})',
 TRAFFIC = {
     'traffic_type' : 'rfc2544_throughput',
     'frame_rate' : 100,
@@ -210,6 +238,17 @@ TRAFFIC = {
         'count': 1,
         'filter': '',
     },
+    'scapy': {
+        'enabled': False,
+        '0' : 'Ether(src={Ether_src}, dst={Ether_dst})/'
+              'Dot1Q(prio={Dot1Q_prio}, id={Dot1Q_id}, vlan={Dot1Q_vlan})/'
+              'IP(proto={IP_proto}, src={IP_src}, dst={IP_dst})/'
+              '{IP_PROTO}(sport={IP_PROTO_sport}, dport={IP_PROTO_dport})',
+        '1' : 'Ether(src={Ether_dst}, dst={Ether_src})/'
+              'Dot1Q(prio={Dot1Q_prio}, id={Dot1Q_id}, vlan={Dot1Q_vlan})/'
+              'IP(proto={IP_proto}, src={IP_dst}, dst={IP_src})/'
+              '{IP_PROTO}(sport={IP_PROTO_dport}, dport={IP_PROTO_sport})',
+    }
 }
 
 #path to traffic generators directory.
index c88b80e..a7a29d0 100644 (file)
@@ -199,6 +199,9 @@ functions:
       Note: There are parameters specific to testing of tunnelling protocols,
       which are discussed in detail at :ref:`integration-tests` userguide.
 
+      Note: A detailed description of the ``TRAFFIC`` dictionary can be found at
+      :ref:`configuration-of-traffic-dictionary`.
+
       * param **traffic_type**: One of the supported traffic types,
         e.g. **rfc2544_throughput**, **rfc2544_continuous**
         or **rfc2544_back2back**.
@@ -224,6 +227,8 @@ functions:
         **dstport** and l4 on/off switch **enabled**.
       * param **vlan**: A dictionary with vlan specific parameters,
         e.g. **priority**, **cfi**, **id** and vlan on/off switch **enabled**.
+      * param **scapy**: A dictionary with definition of the frame content for both traffic
+        directions. The frame content is defined by a SCAPY notation.
 
     * param **tests**: Number of times the test is executed.
     * param **duration**: Duration of continuous test or per iteration duration
index 96ffcf6..de236ee 100644 (file)
@@ -439,6 +439,34 @@ Detailed description of ``TRAFFIC`` dictionary items follows:
                       details.
                       Data type: str
                       Default value: ''
+        'scapy'     - A dictionary with definition of a frame content for both traffic
+                      directions. The frame content is defined by a SCAPY notation.
+                      NOTE: It is supported only by the T-Rex traffic generator.
+                      Following keywords can be used to refer to the related parts of
+                      the TRAFFIC dictionary:
+                           Ether_src   - refers to TRAFFIC['l2']['srcmac']
+                           Ether_dst   - refers to TRAFFIC['l2']['dstmac']
+                           IP_proto    - refers to TRAFFIC['l3']['proto']
+                           IP_PROTO    - refers to upper case version of TRAFFIC['l3']['proto']
+                           IP_src      - refers to TRAFFIC['l3']['srcip']
+                           IP_dst      - refers to TRAFFIC['l3']['dstip']
+                           IP_PROTO_sport - refers to TRAFFIC['l4']['srcport']
+                           IP_PROTO_dport - refers to TRAFFIC['l4']['dstport']
+                           Dot1Q_prio  - refers to TRAFFIC['vlan']['priority']
+                           Dot1Q_id    - refers to TRAFFIC['vlan']['cfi']
+                           Dot1Q_vlan  - refers to TRAFFIC['vlan']['id']
+            '0'     - A string with the frame definition for the 1st direction.
+                      Data type: str
+                      Default value: 'Ether(src={Ether_src}, dst={Ether_dst})/'
+                                     'Dot1Q(prio={Dot1Q_prio}, id={Dot1Q_id}, vlan={Dot1Q_vlan})/'
+                                     'IP(proto={IP_proto}, src={IP_src}, dst={IP_dst})/'
+                                     '{IP_PROTO}(sport={IP_PROTO_sport}, dport={IP_PROTO_dport})'
+            '1'     - A string with the frame definition for the 2nd direction.
+                      Data type: str
+                      Default value: 'Ether(src={Ether_dst}, dst={Ether_src})/'
+                                     'Dot1Q(prio={Dot1Q_prio}, id={Dot1Q_id}, vlan={Dot1Q_vlan})/'
+                                     'IP(proto={IP_proto}, src={IP_dst}, dst={IP_src})/'
+                                     '{IP_PROTO}(sport={IP_PROTO_dport}, dport={IP_PROTO_sport})',
 
 .. _configuration-of-guest-options:
 
index 52b1b4a..c1df27d 100644 (file)
@@ -75,8 +75,22 @@ and is configured as follows:
             'count': 1,
             'filter': '',
         },
+        'scapy': {
+            'enabled': False,
+            '0' : 'Ether(src={Ether_src}, dst={Ether_dst})/'
+                  'Dot1Q(prio={Dot1Q_prio}, id={Dot1Q_id}, vlan={Dot1Q_vlan})/'
+                  'IP(proto={IP_proto}, src={IP_src}, dst={IP_dst})/'
+                  '{IP_PROTO}(sport={IP_PROTO_sport}, dport={IP_PROTO_dport})',
+            '1' : 'Ether(src={Ether_dst}, dst={Ether_src})/'
+                  'Dot1Q(prio={Dot1Q_prio}, id={Dot1Q_id}, vlan={Dot1Q_vlan})/'
+                  'IP(proto={IP_proto}, src={IP_dst}, dst={IP_src})/'
+                  '{IP_PROTO}(sport={IP_PROTO_dport}, dport={IP_PROTO_sport})',
+        }
     }
 
+A detailed description of the ``TRAFFIC`` dictionary can be found at
+:ref:`configuration-of-traffic-dictionary`.
+
 The framesize parameter can be overridden from the configuration
 files by adding the following to your custom configuration file
 ``10_custom.conf``:
@@ -907,3 +921,72 @@ The duration and maximum number of attempted verification trials can be set to c
 behavior of this step. If the verification step fails, it will resume the binary search
 with new values where the maximum output will be the last attempted frame rate minus the
 current set thresh hold.
+
+Scapy frame definition
+~~~~~~~~~~~~~~~~~~~~~~
+
+It is possible to use a SCAPY frame definition to generate various network protocols
+by the **T-Rex** traffic generator. In case that particular network protocol layer
+is disabled by the TRAFFIC dictionary (e.g. TRAFFIC['vlan']['enabled'] = False),
+then disabled layer will be removed from the scapy format definition by VSPERF.
+
+The scapy frame definition can refer to values defined by the TRAFFIC dictionary
+by following keywords. These keywords are used in next examples.
+
+* ``Ether_src`` - refers to ``TRAFFIC['l2']['srcmac']``
+* ``Ether_dst`` - refers to ``TRAFFIC['l2']['dstmac']``
+* ``IP_proto`` - refers to ``TRAFFIC['l3']['proto']``
+* ``IP_PROTO`` - refers to upper case version of ``TRAFFIC['l3']['proto']``
+* ``IP_src`` - refers to ``TRAFFIC['l3']['srcip']``
+* ``IP_dst`` - refers to ``TRAFFIC['l3']['dstip']``
+* ``IP_PROTO_sport`` - refers to ``TRAFFIC['l4']['srcport']``
+* ``IP_PROTO_dport`` - refers to ``TRAFFIC['l4']['dstport']``
+* ``Dot1Q_prio`` - refers to ``TRAFFIC['vlan']['priority']``
+* ``Dot1Q_id`` - refers to ``TRAFFIC['vlan']['cfi']``
+* ``Dot1Q_vlan`` - refers to ``TRAFFIC['vlan']['id']``
+
+In following examples of SCAPY frame definition only relevant parts of TRAFFIC
+dictionary are shown. The rest of the TRAFFIC dictionary is set to default values
+as they are defined in ``conf/03_traffic.conf``.
+
+Please check official documentation of SCAPY project for details about SCAPY frame
+definition and supported network layers at: http://www.secdev.org/projects/scapy
+
+#. Generate ICMP frames:
+
+   .. code-block:: console
+
+        'scapy': {
+            'enabled': True,
+            '0' : 'Ether(src={Ether_src}, dst={Ether_dst})/IP(proto={IP_proto}, src={IP_src}, dst={IP_dst})/ICMP()',
+            '1' : 'Ether(src={Ether_dst}, dst={Ether_src})/IP(proto={IP_proto}, src={IP_dst}, dst={IP_src})/ICMP()',
+        }
+
+#. Generate IPv6 ICMP Echo Request
+
+   .. code-block:: console
+
+        'l3' : {
+            'srcip': 'feed::01',
+            'dstip': 'feed::02',
+        },
+        'scapy': {
+            'enabled': True,
+            '0' : 'Ether(src={Ether_src}, dst={Ether_dst})/IPv6(src={IP_src}, dst={IP_dst})/ICMPv6EchoRequest()',
+            '1' : 'Ether(src={Ether_dst}, dst={Ether_src})/IPv6(src={IP_dst}, dst={IP_src})/ICMPv6EchoRequest()',
+        }
+
+#. Generate SCTP frames:
+
+   Example uses default SCAPY frame definition, which can reflect ``TRAFFIC['l3']['proto']`` settings. The same
+   approach can be used to generate other protocols, e.g. TCP.
+
+   .. code-block:: console
+
+        'l3' : {
+            'proto' : 'sctp',
+        },
+        'scapy': {
+            'enabled': True,
+        }
+
index e0ce4c4..0a3de3c 100644 (file)
 """
 Trex Traffic Generator Model
 """
+
 # pylint: disable=undefined-variable
 import logging
 import subprocess
 import sys
 import time
 import os
+import re
 from collections import OrderedDict
 # pylint: disable=unused-import
 import netaddr
@@ -69,6 +71,20 @@ _EMPTY_STATS = {
               'tx_pps': 0.0,
               'tx_util': 0.0,}}
 
+# Default frame definition, which can be overridden by TRAFFIC['scapy'].
+# The content of the frame and its network layers are driven by TRAFFIC
+# dictionary, i.e. 'l2', 'l3, 'l4' and 'vlan' parts.
+_SCAPY_FRAME = {
+    '0' : 'Ether(src={Ether_src}, dst={Ether_dst})/'
+          'Dot1Q(prio={Dot1Q_prio}, id={Dot1Q_id}, vlan={Dot1Q_vlan})/'
+          'IP(proto={IP_proto}, src={IP_src}, dst={IP_dst})/'
+          '{IP_PROTO}(sport={IP_PROTO_sport}, dport={IP_PROTO_dport})',
+    '1' : 'Ether(src={Ether_dst}, dst={Ether_src})/'
+          'Dot1Q(prio={Dot1Q_prio}, id={Dot1Q_id}, vlan={Dot1Q_vlan})/'
+          'IP(proto={IP_proto}, src={IP_dst}, dst={IP_src})/'
+          '{IP_PROTO}(sport={IP_PROTO_dport}, dport={IP_PROTO_sport})',
+}
+
 
 class Trex(ITrafficGenerator):
     """Trex Traffic generator wrapper."""
@@ -165,35 +181,77 @@ class Trex(ITrafficGenerator):
         self._logger.info("T-Rex: In trex disconnect method")
         self._stlclient.disconnect(stop_traffic=True, release_ports=True)
 
-    @staticmethod
-    def create_packets(traffic, ports_info):
+    def create_packets(self, traffic, ports_info):
         """Create base packet according to traffic specification.
            If traffic haven't specified srcmac and dstmac fields
-           packet will be create with mac address of trex server.
+           packet will be created with mac address of trex server.
         """
-        mac_add = [li['hw_mac'] for li in ports_info]
-
-        if traffic and traffic['l2']['framesize'] > 0:
-            if traffic['l2']['dstmac'] == '00:00:00:00:00:00' and \
-               traffic['l2']['srcmac'] == '00:00:00:00:00:00':
-                base_pkt_a = Ether(src=mac_add[0], dst=mac_add[1])/ \
-                             IP(proto=traffic['l3']['proto'], src=traffic['l3']['srcip'],
-                                dst=traffic['l3']['dstip'])/ \
-                             UDP(dport=traffic['l4']['dstport'], sport=traffic['l4']['srcport'])
-                base_pkt_b = Ether(src=mac_add[1], dst=mac_add[0])/ \
-                             IP(proto=traffic['l3']['proto'], src=traffic['l3']['dstip'],
-                                dst=traffic['l3']['srcip'])/ \
-                             UDP(dport=traffic['l4']['srcport'], sport=traffic['l4']['dstport'])
-            else:
-                base_pkt_a = Ether(src=traffic['l2']['srcmac'], dst=traffic['l2']['dstmac'])/ \
-                             IP(proto=traffic['l3']['proto'], src=traffic['l3']['dstip'],
-                                dst=traffic['l3']['srcip'])/ \
-                             UDP(dport=traffic['l4']['dstport'], sport=traffic['l4']['srcport'])
+        if not traffic or traffic['l2']['framesize'] <= 0:
+            return (None, None)
 
-                base_pkt_b = Ether(src=traffic['l2']['dstmac'], dst=traffic['l2']['srcmac'])/ \
-                             IP(proto=traffic['l3']['proto'], src=traffic['l3']['dstip'],
-                                dst=traffic['l3']['srcip'])/ \
-                             UDP(dport=traffic['l4']['srcport'], sport=traffic['l4']['dstport'])
+        if traffic['l2']['dstmac'] == '00:00:00:00:00:00' and \
+           traffic['l2']['srcmac'] == '00:00:00:00:00:00':
+
+            mac_add = [li['hw_mac'] for li in ports_info]
+            src_mac = mac_add[0]
+            dst_mac = mac_add[1]
+        else:
+            src_mac = traffic['l2']['srcmac']
+            dst_mac = traffic['l2']['dstmac']
+
+        if traffic['scapy']['enabled']:
+            base_pkt_a = traffic['scapy']['0']
+            base_pkt_b = traffic['scapy']['1']
+        else:
+            base_pkt_a = _SCAPY_FRAME['0']
+            base_pkt_b = _SCAPY_FRAME['1']
+
+        # check and remove network layers disabled by TRAFFIC dictionary
+        # Note: In general, it is possible to remove layers from scapy object by
+        # e.g. del base_pkt_a['IP']. However it doesn't work for all layers
+        # (e.g. Dot1Q). Thus it is safer to modify string with scapy frame definition
+        # directly, before it is converted to the real scapy object.
+        if not traffic['vlan']['enabled']:
+            self._logger.info('VLAN headers are disabled by TRAFFIC')
+            base_pkt_a = re.sub(r'(^|\/)Dot1Q?\([^\)]*\)', '', base_pkt_a)
+            base_pkt_b = re.sub(r'(^|\/)Dot1Q?\([^\)]*\)', '', base_pkt_b)
+        if not traffic['l3']['enabled']:
+            self._logger.info('IP headers are disabled by TRAFFIC')
+            base_pkt_a = re.sub(r'(^|\/)IP(v6)?\([^\)]*\)', '', base_pkt_a)
+            base_pkt_b = re.sub(r'(^|\/)IP(v6)?\([^\)]*\)', '', base_pkt_b)
+        if not traffic['l4']['enabled']:
+            self._logger.info('%s headers are disabled by TRAFFIC',
+                              traffic['l3']['proto'].upper())
+            base_pkt_a = re.sub(r'(^|\/)(UDP|TCP|SCTP|{{IP_PROTO}}|{})\([^\)]*\)'.format(
+                traffic['l3']['proto'].upper()), '', base_pkt_a)
+            base_pkt_b = re.sub(r'(^|\/)(UDP|TCP|SCTP|{{IP_PROTO}}|{})\([^\)]*\)'.format(
+                traffic['l3']['proto'].upper()), '', base_pkt_b)
+
+        # pylint: disable=eval-used
+        base_pkt_a = eval(base_pkt_a.format(
+            Ether_src=repr(src_mac),
+            Ether_dst=repr(dst_mac),
+            Dot1Q_prio=traffic['vlan']['priority'],
+            Dot1Q_id=traffic['vlan']['cfi'],
+            Dot1Q_vlan=traffic['vlan']['id'],
+            IP_proto=repr(traffic['l3']['proto']),
+            IP_PROTO=traffic['l3']['proto'].upper(),
+            IP_src=repr(traffic['l3']['srcip']),
+            IP_dst=repr(traffic['l3']['dstip']),
+            IP_PROTO_sport=traffic['l4']['srcport'],
+            IP_PROTO_dport=traffic['l4']['dstport']))
+        base_pkt_b = eval(base_pkt_b.format(
+            Ether_src=repr(src_mac),
+            Ether_dst=repr(dst_mac),
+            Dot1Q_prio=traffic['vlan']['priority'],
+            Dot1Q_id=traffic['vlan']['cfi'],
+            Dot1Q_vlan=traffic['vlan']['id'],
+            IP_proto=repr(traffic['l3']['proto']),
+            IP_PROTO=traffic['l3']['proto'].upper(),
+            IP_src=repr(traffic['l3']['srcip']),
+            IP_dst=repr(traffic['l3']['dstip']),
+            IP_PROTO_sport=traffic['l4']['srcport'],
+            IP_PROTO_dport=traffic['l4']['dstport']))
 
         return (base_pkt_a, base_pkt_b)
 
@@ -299,7 +357,7 @@ class Trex(ITrafficGenerator):
         if settings.getValue('TRAFFICGEN_TREX_PROMISCUOUS'):
             self._stlclient.set_port_attr(my_ports, promiscuous=True)
 
-        packet_1, packet_2 = Trex.create_packets(traffic, ports_info)
+        packet_1, packet_2 = self.create_packets(traffic, ports_info)
         self.show_packet_info(packet_1, packet_2)
         stream_1, stream_2, stream_1_lat, stream_2_lat = Trex.create_streams(packet_1, packet_2, traffic)
         self._stlclient.add_streams(stream_1, ports=[0])