summaryrefslogtreecommitdiffstats
path: root/scripts/automation/trex_control_plane/stl/services/scapy_server
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/automation/trex_control_plane/stl/services/scapy_server')
-rw-r--r--scripts/automation/trex_control_plane/stl/services/scapy_server/field_engine.json269
-rwxr-xr-xscripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py209
-rw-r--r--scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py3
-rw-r--r--scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py45
4 files changed, 519 insertions, 7 deletions
diff --git a/scripts/automation/trex_control_plane/stl/services/scapy_server/field_engine.json b/scripts/automation/trex_control_plane/stl/services/scapy_server/field_engine.json
new file mode 100644
index 00000000..85d10e65
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/services/scapy_server/field_engine.json
@@ -0,0 +1,269 @@
+{
+ "instructions": [
+ {
+ "id": "STLVmFlowVar",
+ "parameters": ["name", "init_value", "max_value","min_value","step", "size","op"]
+ },
+ {
+ "id": "STLVmWrFlowVar",
+ "parameters": ["fv_name", "pkt_offset","offset_fixup","add_val","is_big"]
+ },
+ {
+ "id": "STLVmWrMaskFlowVar",
+ "parameters": ["fv_name", "pkt_offset", "pkt_cast_size","mask", "shift","add_value","is_big"]
+ },
+ {
+ "id": "STLVmFixIpv4",
+ "parameters": ["offset"]
+ },
+ {
+ "id": "STLVmTrimPktSize",
+ "parameters": ["fv_name"]
+ },
+ {
+ "id": "STLVmTupleGen",
+ "parameters": ["name", "ip_min", "ip_max", "port_min", "port_max", "limit_flows", "flags"]
+ },
+ {
+ "id": "STLVmFlowVarRepetableRandom",
+ "parameters": ["name", "size", "limit", "seed", "min_value", "max_value"]
+ },
+ {
+ "id": "STLVmFixChecksumHw",
+ "parameters": ["l3_offset","l4_offset","l4_type"]
+ }
+ ],
+
+ "instruction_params_meta": [
+ {
+ "id": "name",
+ "name": "Name",
+ "type": "ENUM",
+ "editable": true,
+ "required": true,
+ "defaultValue": "Not defined"
+ },
+ {
+ "id": "init_value",
+ "name": "Initial value",
+ "type": "STRING",
+ "defaultValue": "0"
+ },
+ {
+ "id": "max_value",
+ "name": "Maximum value",
+ "type": "STRING",
+ "required": true,
+ "defaultValue": "0"
+ },
+ {
+ "id": "min_value",
+ "name": "Minimum value",
+ "type": "STRING",
+ "required": true,
+ "defaultValue": "0"
+ },
+ {
+ "id": "step",
+ "name": "Step",
+ "type": "NUMBER",
+ "required": true,
+ "defaultValue": "1"
+ },
+ {
+ "id": "op",
+ "name": "Operation",
+ "type": "ENUM",
+ "defaultValue": "inc",
+ "dict": {
+ "dec": "Decrement",
+ "inc": "Increment",
+ "random": "Random"
+ },
+ "required": true
+ },
+ {
+ "id": "size",
+ "name": "Size",
+ "type": "ENUM",
+ "defaultValue": "4",
+ "dict": {
+ "1": "1",
+ "2": "2",
+ "4": "4",
+ "8": "8"
+ }
+ },
+ {
+ "id": "fv_name",
+ "name": "Variable name",
+ "type": "ENUM",
+ "required": true,
+ "editable": true
+ },
+ {
+ "id": "pkt_offset",
+ "name": "Offset",
+ "type": "ENUM",
+ "required": true,
+ "editable": true,
+ "defaultValue": 0
+ },
+ {
+ "id": "pkt_cast_size",
+ "name": "Packet cast size",
+ "type": "ENUM",
+ "defaultValue": 1,
+ "dict":{
+ "1":1,
+ "2":2,
+ "4":4
+ }
+ },
+ {
+ "id": "shift",
+ "name": "Shift",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "mask",
+ "name": "Mask",
+ "type": "STRING",
+ "defaultValue": "0xff"
+ },
+ {
+ "id": "offset_fixup",
+ "name": "offset_fixup",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "add_val",
+ "name": "add_val",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "add_value",
+ "name": "add_value",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "is_big",
+ "name": "is_big",
+ "type": "ENUM",
+ "defaultValue": "true",
+ "dict": {
+ "true": "true",
+ "false": "false"
+ }
+ },
+ {
+ "id": "offset",
+ "name": "Offset",
+ "type": "ENUM",
+ "required": true,
+ "editable": true,
+ "defaultValue": 0
+ },
+ {
+ "id": "l3_offset",
+ "name": "L3 offset",
+ "type": "STRING",
+ "required": true,
+ "autocomplete": true,
+ "defaultValue": "IP"
+ },
+ {
+ "id": "l4_offset",
+ "name": "L4 offset",
+ "type": "STRING",
+ "required": true,
+ "defaultValue": "TCP"
+ },
+ {
+ "id": "ip_min",
+ "name": "Min IP",
+ "type": "STRING",
+ "defaultValue": "0.0.0.1"
+ },
+ {
+ "id": "ip_max",
+ "name": "Max IP",
+ "type": "STRING",
+ "defaultValue": "0.0.0.10"
+ },
+ {
+ "id": "port_max",
+ "name": "Max Port number",
+ "type": "NUMBER",
+ "defaultValue": 65535
+ },
+ {
+ "id": "port_min",
+ "name": "Min Port number",
+ "type": "NUMBER",
+ "defaultValue": 1025
+ },
+ {
+ "id": "limit_flows",
+ "name": "FLows limit",
+ "type": "NUMBER",
+ "defaultValue": 100000
+ },
+ {
+ "id": "limit",
+ "name": "Limit",
+ "type": "NUMBER",
+ "defaultValue": 100
+ },
+ {
+ "id": "seed",
+ "name": "Seed",
+ "type": "String",
+ "defaultValue": "None"
+ },
+ {
+ "id": "flags",
+ "name": "Flags",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "l4_type",
+ "name": "L4 type",
+ "type": "ENUM",
+ "required": true,
+ "editable": false,
+ "defaultValue": "13",
+ "dict": {
+ "11": "L4_TYPE_UDP",
+ "13": "L4_TYPE_TCP"
+ }
+ }
+ ],
+ "supported_protocols": ["IP","TCP","UDP"],
+ "templates":[
+ {
+ "id": "simple_flow_var",
+ "name": "Simple variable",
+ "instructionIds": ["STLVmFlowVar", "STLVmWrFlowVar"]
+ },
+ {
+ "id": "rep_rand_var",
+ "name": "Repeatable random",
+ "instructionIds": ["STLVmFlowVarRepetableRandom", "STLVmWrFlowVar"]
+ }
+ ],
+ "global_params_meta":[
+ {
+ "id": "cache_size",
+ "name": "Cache size",
+ "type": "NUMBER",
+ "defaultValue": "1000"
+ }
+ ]
+}
+
diff --git a/scripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py b/scripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py
index 8d99fe92..e5f1b20c 100755
--- a/scripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py
+++ b/scripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py
@@ -1,17 +1,20 @@
import os
import sys
+
stl_pathname = os.path.abspath(os.path.join(os.pardir, os.pardir))
sys.path.append(stl_pathname)
from trex_stl_lib.api import *
+import trex_stl_lib.trex_stl_packet_builder_scapy
import tempfile
import hashlib
import base64
import numbers
import random
-import inspect
+from inspect import getdoc
import json
+import re
from pprint import pprint
# add some layers as an example
@@ -121,6 +124,45 @@ class Scapy_service_api():
"""
pass
+ def build_pkt_ex(self, client_v_handler, pkt_model_descriptor, extra_options):
+ """ build_pkt_ex(self,client_v_handler,pkt_model_descriptor, extra_options) -> Dictionary (of Offsets,Show2 and Buffer)
+ Performs calculations on the given packet and returns results for that packet.
+
+ Parameters
+ ----------
+ pkt_descriptor - An array of dictionaries describing a network packet
+ extra_options - A dictionary of extra options required for building packet
+
+ Returns
+ -------
+ - The packets offsets: each field in every layer is mapped inside the Offsets Dictionary
+ - The Show2: A description of each field and its value in every layer of the packet
+ - The Buffer: The Hexdump of packet encoded in base64
+
+ Raises
+ ------
+ will raise an exception when the Scapy string format is illegal, contains syntax error, contains non-supported
+ protocl, etc.
+ """
+ pass
+
+ def load_instruction_parameter_values(self, client_v_handler, pkt_model_descriptor, vm_instructions_model, parameter_id):
+ """ load_instruction_parameter_values(self,client_v_handler,pkt_model_descriptor, vm_instructions_model, parameter_id) -> Dictionary (of possible parameter values)
+ Returns possible valies for given pararameter id depends on current pkt structure and vm_instructions
+ model.
+
+ Parameters
+ ----------
+ pkt_descriptor - An array of dictionaries describing a network packet
+ vm_instructions_model - A dictionary of extra options required for building packet
+ parameter_id - A string of parameter id
+
+ Returns
+ -------
+ Possible parameter values map.
+
+ """
+ pass
def get_tree(self,client_v_handler):
""" get_tree(self) -> Dictionary describing an example of hierarchy in layers
@@ -372,7 +414,15 @@ class Scapy_service(Scapy_service_api):
self.version_minor = '01'
self.server_v_hashed = self._generate_version_hash(self.version_major,self.version_minor)
self.protocol_definitions = {} # protocolId -> prococol definition overrides data
+ self.field_engine_supported_protocols = {}
+ self.instruction_parameter_meta_definitions = []
+ self.field_engine_parameter_meta_definitions = []
+ self.field_engine_templates_definitions = []
+ self.field_engine_instructions_meta = []
+ self.field_engine_instruction_expressions = []
self._load_definitions_from_json()
+ self._load_field_engine_meta_from_json()
+ self._vm_instructions = dict([m for m in inspect.getmembers(trex_stl_lib.trex_stl_packet_builder_scapy, inspect.isclass) if m[1].__module__ == 'trex_stl_lib.trex_stl_packet_builder_scapy'])
def _load_definitions_from_json(self):
# load protocol definitions from a json file
@@ -382,6 +432,27 @@ class Scapy_service(Scapy_service_api):
for protocol in protocols:
self.protocol_definitions[ protocol['id'] ] = protocol
+ def _load_field_engine_meta_from_json(self):
+ # load protocol definitions from a json file
+ self.instruction_parameter_meta_definitions = []
+ self.field_engine_supported_protocols = {}
+ self.field_engine_parameter_meta_definitions = []
+ self.field_engine_templates_definitions = []
+ with open('field_engine.json', 'r') as f:
+ metas = json.load(f)
+ self.instruction_parameter_meta_definitions = metas["instruction_params_meta"]
+ self.field_engine_instructions_meta = metas["instructions"]
+ self._append_intructions_help()
+ self.field_engine_supported_protocols = metas["supported_protocols"]
+ self.field_engine_parameter_meta_definitions = metas["global_params_meta"]
+ self.field_engine_templates_definitions = metas["templates"]
+
+
+ def _append_intructions_help(self):
+ for instruction_meta in self.field_engine_instructions_meta:
+ clazz = eval(instruction_meta['id'])
+ instruction_meta['help'] = base64.b64encode(getdoc(clazz.__init__)).decode('ascii')
+
def _all_protocol_structs(self):
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
@@ -708,6 +779,134 @@ class Scapy_service(Scapy_service_api):
pkt = self._packet_model_to_scapy_packet(pkt_model_descriptor)
return self._pkt_data(pkt)
+
+ def build_pkt_ex(self, client_v_handler, pkt_model_descriptor, extra_options):
+ res = self.build_pkt(client_v_handler, pkt_model_descriptor)
+ pkt = self._packet_model_to_scapy_packet(pkt_model_descriptor)
+
+ field_engine = {}
+ field_engine['instructions'] = []
+ field_engine['error'] = None
+ try:
+ field_engine['instructions'] = self._generate_vm_instructions(pkt, extra_options['field_engine'])
+ except AssertionError as e:
+ field_engine['error'] = e.message
+ except CTRexPacketBuildException as e:
+ field_engine['error'] = e.message
+
+ field_engine['vm_instructions_expressions'] = self.field_engine_instruction_expressions
+ res['field_engine'] = field_engine
+ return res
+
+ def load_instruction_parameter_values(self, client_v_handler, pkt_model_descriptor, vm_instructions_model, parameter_id):
+
+ given_protocol_ids = [str(proto['id']) for proto in pkt_model_descriptor]
+
+ values = {}
+ if parameter_id == "name":
+ values = self._curent_pkt_protocol_fields(given_protocol_ids, "_")
+
+ if parameter_id == "fv_name":
+ values = self._existed_flow_var_names(vm_instructions_model['field_engine']['instructions'])
+
+ if parameter_id == "pkt_offset":
+ values = self._curent_pkt_protocol_fields(given_protocol_ids, ".")
+
+ if parameter_id == "offset":
+ for ip_idx in range(given_protocol_ids.count("IP")):
+ value = "IP:{0}".format(ip_idx)
+ values[value] = value
+
+ return {"map": values}
+
+ def _existed_flow_var_names(self, instructions):
+ return dict((instruction['parameters']['name'], instruction['parameters']['name']) for instruction in instructions if self._nameParamterExist(instruction))
+
+ def _nameParamterExist(self, instruction):
+ try:
+ instruction['parameters']['name']
+ return True
+ except KeyError:
+ return False
+
+ def _curent_pkt_protocol_fields(self, given_protocol_ids, delimiter):
+ given_protocol_classes = [c for c in Packet.__subclasses__() if c.__name__ in given_protocol_ids]
+ protocol_fields = {}
+ for protocol_class in given_protocol_classes:
+ protocol_name = protocol_class.__name__
+ protocol_count = given_protocol_ids.count(protocol_name)
+ for field_desc in protocol_class.fields_desc:
+ if delimiter == '.' and protocol_count > 1:
+ for idx in range(protocol_count):
+ formatted_name = "{0}:{1}{2}{3}".format(protocol_name, idx, delimiter, field_desc.name)
+ protocol_fields[formatted_name] = formatted_name
+ else:
+ formatted_name = "{0}{1}{2}".format(protocol_name, delimiter, field_desc.name)
+ protocol_fields[formatted_name] = formatted_name
+
+ return protocol_fields
+
+ def _generate_vm_instructions(self, pkt, field_engine_model_descriptor):
+ self.field_engine_instruction_expressions = []
+ instructions = []
+ instructions_def = field_engine_model_descriptor['instructions']
+ for instruction_def in instructions_def:
+ instruction_id = instruction_def['id']
+ instruction_class = self._vm_instructions[instruction_id]
+ parameters = {k: self._sanitize_value(k, v) for (k, v) in instruction_def['parameters'].iteritems()}
+ instructions.append(instruction_class(**parameters))
+
+ fe_parameters = field_engine_model_descriptor['global_parameters']
+
+ cache_size = None
+ if "cache_size" in fe_parameters:
+ assert self._is_int(fe_parameters['cache_size']), 'Cache size must be a number'
+ cache_size = int(fe_parameters['cache_size'])
+
+
+ pkt_builder = STLPktBuilder(pkt=pkt, vm=STLScVmRaw(instructions, cache_size=cache_size))
+ pkt_builder.compile()
+ return pkt_builder.get_vm_data()
+
+ def _sanitize_value(self, param_id, val):
+ if param_id == "pkt_offset":
+ if self._is_int(val):
+ return int(val)
+ elif val == "Ether.src":
+ return 0
+ elif val == "Ether.dst":
+ return 6
+ elif val == "Ether.type":
+ return 12
+ else:
+ if val == "None" or val == "none":
+ return None
+ if val == "true":
+ return True
+ elif val == "false":
+ return False
+ elif re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", str(val.lower())):
+ return int(str(val).replace(":", ""), 16)
+
+ if self._is_int(val):
+ return int(val)
+
+ str_val = str(val)
+ return int(str_val, 16) if str_val.startswith("0x") else str_val
+
+ def _get_instruction_parameter_meta(self, param_id):
+ for meta in self.instruction_parameter_meta_definitions:
+ if meta['id'] == param_id:
+ return meta
+ raise Scapy_Exception("Unable to get meta for {0}" % param_id)
+
+ def _is_int(self, val):
+ try:
+ int(val)
+ return True
+ except ValueError:
+ return False
+
# @deprecated. to be removed
def get_all(self,client_v_handler):
if not (self._verify_version_handler(client_v_handler)):
@@ -796,7 +995,11 @@ class Scapy_service(Scapy_service_api):
"name": protoDef.get('name') or pkt_class.name,
"fields": self._get_fields_definition(pkt_class, protoDef.get('fields') or [])
})
- res = {"protocols": protocols}
+ res = {"protocols": protocols,
+ "feInstructionParameters": self.instruction_parameter_meta_definitions,
+ "feInstructions": self.field_engine_instructions_meta,
+ "feParameters": self.field_engine_parameter_meta_definitions,
+ "feTemplates": self.field_engine_templates_definitions}
return res
def get_payload_classes(self,client_v_handler, pkt_model_descriptor):
@@ -889,8 +1092,6 @@ class Scapy_service(Scapy_service_api):
pcap_bin = tmpPcap.read()
return bytes_to_b64(pcap_bin)
-
-
#---------------------------------------------------------------------------
diff --git a/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py b/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py
index 1db2c62b..e48880e8 100644
--- a/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py
+++ b/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py
@@ -53,6 +53,9 @@ def get_version_handler():
def build_pkt(model_def):
return pass_result(service.build_pkt(v_handler, model_def))
+def build_pkt_ex(model_def, instructions_def):
+ return pass_result(service.build_pkt_ex(v_handler, model_def, instructions_def))
+
def build_pkt_get_scapy(model_def):
return build_pkt_to_scapy(build_pkt(model_def))
diff --git a/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py b/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py
index 91a457dc..e1094a79 100644
--- a/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py
+++ b/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py
@@ -113,15 +113,23 @@ def test_get_all():
def test_get_definitions_all():
get_definitions(None)
- def_classnames = [pdef['id'] for pdef in get_definitions(None)['protocols']]
+ defs = get_definitions(None)
+ def_classnames = [pdef['id'] for pdef in defs['protocols']]
assert("IP" in def_classnames)
assert("Dot1Q" in def_classnames)
assert("TCP" in def_classnames)
+ # All instructions should have a help description.
+ fe_instructions = defs['feInstructions']
+ for instruction in fe_instructions:
+ print(instruction['help'])
+ assert("help" in instruction)
+
def test_get_definitions_ether():
res = get_definitions(["Ether"])
- assert(len(res) == 1)
- assert(res['protocols'][0]['id'] == "Ether")
+ protocols = res['protocols']
+ assert(len(protocols) == 1)
+ assert(protocols[0]['id'] == "Ether")
def test_get_payload_classes():
eth_payloads = get_payload_classes([{"id":"Ether"}])
@@ -250,3 +258,34 @@ def test_ip_definitions():
assert(fields[9]['id'] == 'chksum')
assert(fields[9]['auto'] == True)
+def test_generate_vm_instructions():
+ ip_pkt_model = [
+ layer_def("Ether"),
+ layer_def("IP", src="16.0.0.1", dst="48.0.0.1")
+ ]
+ ip_instructions_model = {"field_engine": {"instructions": [{"id": "STLVmFlowVar",
+ "parameters": {"op": "inc", "min_value": "192.168.0.10",
+ "size": "1", "name": "ip_src",
+ "step": "1",
+ "max_value": "192.168.0.100"}},
+ {"id": "STLVmWrFlowVar",
+ "parameters": {"pkt_offset": "IP.src", "is_big": "true",
+ "add_val": "0", "offset_fixup": "0",
+ "fv_name": "ip_src"}},
+ {"id": "STLVmFlowVar",
+ "parameters": {"op": "dec", "min_value": "32",
+ "size": "1", "name": "ip_ttl",
+ "step": "4", "max_value": "64"}},
+ {"id": "STLVmWrFlowVar",
+ "parameters": {"pkt_offset": "IP.ttl", "is_big": "true",
+ "add_val": "0", "offset_fixup": "0",
+ "fv_name": "ip_ttl"}}],
+ "global_parameters": {}}}
+ res = build_pkt_ex(ip_pkt_model, ip_instructions_model)
+ src_instruction = res['field_engine']['instructions']['instructions'][0]
+ assert(src_instruction['min_value'] == 3232235530)
+ assert(src_instruction['max_value'] == 3232235620)
+
+ ttl_instruction = res['field_engine']['instructions']['instructions'][2]
+ assert(ttl_instruction['min_value'] == 32)
+ assert(ttl_instruction['max_value'] == 64)