diff options
Diffstat (limited to 'scripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py')
-rwxr-xr-x | scripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py | 209 |
1 files changed, 205 insertions, 4 deletions
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) - - #--------------------------------------------------------------------------- |