From 995267db77f5554d5228697b8b2a862b51859fe6 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 8 Feb 2016 06:08:14 -0500 Subject: first refactor --- .../client_utils/jsonrpc_client.py | 244 ---- .../client_utils/packet_builder.py | 1209 -------------------- .../client_utils/packet_builder_interface.py | 43 - .../client_utils/scapy_packet_builder.py | 748 ------------ .../trex_control_plane/client_utils/text_tables.py | 4 +- .../trex_control_plane/client_utils/yaml_utils.py | 1 - 6 files changed, 1 insertion(+), 2248 deletions(-) delete mode 100755 scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py delete mode 100755 scripts/automation/trex_control_plane/client_utils/packet_builder.py delete mode 100644 scripts/automation/trex_control_plane/client_utils/packet_builder_interface.py delete mode 100644 scripts/automation/trex_control_plane/client_utils/scapy_packet_builder.py (limited to 'scripts/automation/trex_control_plane/client_utils') diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py deleted file mode 100755 index 9c351175..00000000 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ /dev/null @@ -1,244 +0,0 @@ -#!/router/bin/python - -import external_packages -import zmq -import json -import general_utils -import re -from time import sleep -from collections import namedtuple -from common.trex_types import * - -class bcolors: - BLUE = '\033[94m' - GREEN = '\033[32m' - YELLOW = '\033[93m' - RED = '\033[31m' - MAGENTA = '\033[35m' - ENDC = '\033[0m' - BOLD = '\033[1m' - UNDERLINE = '\033[4m' - -# sub class to describe a batch -class BatchMessage(object): - def __init__ (self, rpc_client): - self.rpc_client = rpc_client - self.batch_list = [] - - def add (self, method_name, params={}): - - id, msg = self.rpc_client.create_jsonrpc_v2(method_name, params, encode = False) - self.batch_list.append(msg) - - def invoke(self, block = False): - if not self.rpc_client.connected: - return RC_ERR("Not connected to server") - - msg = json.dumps(self.batch_list) - - return self.rpc_client.send_raw_msg(msg) - - -# JSON RPC v2.0 client -class JsonRpcClient(object): - - def __init__ (self, default_server, default_port, logger): - self.logger = logger - self.connected = False - - # default values - self.port = default_port - self.server = default_server - self.id_gen = general_utils.random_id_gen() - - - def get_connection_details (self): - rc = {} - rc['server'] = self.server - rc['port'] = self.port - - return rc - - # pretty print for JSON - def pretty_json (self, json_str, use_colors = True): - pretty_str = json.dumps(json.loads(json_str), indent = 4, separators=(',', ': '), sort_keys = True) - - if not use_colors: - return pretty_str - - try: - # int numbers - pretty_str = re.sub(r'([ ]*:[ ]+)(\-?[1-9][0-9]*[^.])',r'\1{0}\2{1}'.format(bcolors.BLUE, bcolors.ENDC), pretty_str) - # float - pretty_str = re.sub(r'([ ]*:[ ]+)(\-?[1-9][0-9]*\.[0-9]+)',r'\1{0}\2{1}'.format(bcolors.MAGENTA, bcolors.ENDC), pretty_str) - # strings - - pretty_str = re.sub(r'([ ]*:[ ]+)("[^"]*")',r'\1{0}\2{1}'.format(bcolors.RED, bcolors.ENDC), pretty_str) - pretty_str = re.sub(r"('[^']*')", r'{0}\1{1}'.format(bcolors.MAGENTA, bcolors.RED), pretty_str) - except : - pass - - return pretty_str - - def verbose_msg (self, msg): - self.logger.log("\n\n[verbose] " + msg, level = self.logger.VERBOSE_HIGH) - - - # batch messages - def create_batch (self): - return BatchMessage(self) - - def create_jsonrpc_v2 (self, method_name, params = {}, encode = True): - msg = {} - msg["jsonrpc"] = "2.0" - msg["method"] = method_name - - msg["params"] = params - - msg["id"] = self.id_gen.next() - - if encode: - return id, json.dumps(msg) - else: - return id, msg - - - def invoke_rpc_method (self, method_name, params = {}): - if not self.connected: - return RC_ERR("Not connected to server") - - id, msg = self.create_jsonrpc_v2(method_name, params) - - return self.send_raw_msg(msg) - - - # low level send of string message - def send_raw_msg (self, msg): - - self.verbose_msg("Sending Request To Server:\n\n" + self.pretty_json(msg) + "\n") - - tries = 0 - while True: - try: - self.socket.send(msg) - break - except zmq.Again: - tries += 1 - if tries > 5: - self.disconnect() - return RC_ERR("*** [RPC] - Failed to send message to server") - - - tries = 0 - while True: - try: - response = self.socket.recv() - break - except zmq.Again: - tries += 1 - if tries > 5: - self.disconnect() - return RC_ERR("*** [RPC] - Failed to get server response at {0}".format(self.transport)) - - - self.verbose_msg("Server Response:\n\n" + self.pretty_json(response) + "\n") - - # decode - - # batch ? - response_json = json.loads(response) - - if isinstance(response_json, list): - rc_batch = RC() - - for single_response in response_json: - rc = self.process_single_response(single_response) - rc_batch.add(rc) - - return rc_batch - - else: - return self.process_single_response(response_json) - - - def process_single_response (self, response_json): - - if (response_json.get("jsonrpc") != "2.0"): - return RC_ERR("Malformed Response ({0})".format(str(response_json))) - - # error reported by server - if ("error" in response_json): - if "specific_err" in response_json["error"]: - return RC_ERR(response_json["error"]["specific_err"]) - else: - return RC_ERR(response_json["error"]["message"]) - - - # if no error there should be a result - if ("result" not in response_json): - return RC_ERR("Malformed Response ({0})".format(str(response_json))) - - return RC_OK(response_json["result"]) - - - - def disconnect (self): - if self.connected: - self.socket.close(linger = 0) - self.context.destroy(linger = 0) - self.connected = False - return RC_OK() - else: - return RC_ERR("Not connected to server") - - - def connect(self, server = None, port = None): - if self.connected: - self.disconnect() - - self.context = zmq.Context() - - self.server = (server if server else self.server) - self.port = (port if port else self.port) - - # Socket to talk to server - self.transport = "tcp://{0}:{1}".format(self.server, self.port) - - self.socket = self.context.socket(zmq.REQ) - try: - self.socket.connect(self.transport) - except zmq.error.ZMQError as e: - return RC_ERR("ZMQ Error: Bad server or port name: " + str(e)) - - self.socket.setsockopt(zmq.SNDTIMEO, 1000) - self.socket.setsockopt(zmq.RCVTIMEO, 1000) - - self.connected = True - - rc = self.invoke_rpc_method('ping') - if not rc: - self.connected = False - return rc - - return RC_OK() - - - def reconnect(self): - # connect using current values - return self.connect() - - if not self.connected: - return RC_ERR("Not connected to server") - - # reconnect - return self.connect(self.server, self.port) - - - def is_connected(self): - return self.connected - - def __del__(self): - self.logger.log("Shutting down RPC client\n") - if hasattr(self, "context"): - self.context.destroy(linger=0) - diff --git a/scripts/automation/trex_control_plane/client_utils/packet_builder.py b/scripts/automation/trex_control_plane/client_utils/packet_builder.py deleted file mode 100755 index f9031436..00000000 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ /dev/null @@ -1,1209 +0,0 @@ -#!/router/bin/python - -import external_packages -import dpkt -import socket -import binascii -import copy -import random -import string -import struct -import re -import itertools -from abc import ABCMeta, abstractmethod -from collections import namedtuple -import base64 - -from packet_builder_interface import CTrexPktBuilderInterface - -class CTRexPktBuilder(CTrexPktBuilderInterface): - """ - This class defines the TRex API of building a packet using dpkt package. - Using this class the user can also define how TRex will handle the packet by specifying the VM setting. - """ - def __init__(self, max_pkt_size=dpkt.ethernet.ETH_LEN_MAX): - """ - Instantiate a CTRexPktBuilder object - - :parameters: - None - - """ - super(CTRexPktBuilder, self).__init__() - self._packet = None - self._pkt_by_hdr = {} - self._pkt_top_layer = None - self._max_pkt_size = max_pkt_size - self.vm = CTRexPktBuilder.CTRexVM() - self.metadata = "" - - def clone (self): - return copy.deepcopy(self) - - def add_pkt_layer(self, layer_name, pkt_layer): - """ - This method adds additional header to the already existing packet - - :parameters: - layer_name: str - a string representing the name of the layer. - Example: "l2", "l4_tcp", etc. - - pkt_layer : dpkt.Packet obj - a dpkt object, generally from higher layer, that will be added on top of existing layer. - - :raises: - + :exc:`ValueError`, in case the desired layer_name already exists. - - """ - assert isinstance(pkt_layer, dpkt.Packet) - if layer_name in self._pkt_by_hdr: - raise ValueError("Given layer name '{0}' already exists.".format(layer_name)) - else: - dup_pkt = copy.copy(pkt_layer) # using copy of layer to avoid cyclic packets that may lead to infinite loop - if not self._pkt_top_layer: # this is the first header added - self._packet = dup_pkt - else: - self._pkt_top_layer.data = dup_pkt - self._pkt_top_layer = dup_pkt - self._pkt_by_hdr[layer_name] = dup_pkt - return - - def set_ip_layer_addr(self, layer_name, attr, ip_addr, ip_type="ipv4"): - """ - This method sets the IP address fields of an IP header (source or destination, for both IPv4 and IPv6) - using a human readable addressing representation. - - :parameters: - layer_name: str - a string representing the name of the layer. - Example: "l3_ip", etc. - - attr: str - a string representation of the sub-field to be set: - - + "src" for source - + "dst" for destination - - ip_addr: str - a string representation of the IP address to be set. - Example: "10.0.0.1" for IPv4, or "5001::DB8:1:3333:1:1" for IPv6 - - ip_type : str - a string representation of the IP version to be set: - - + "ipv4" for IPv4 - + "ipv6" for IPv6 - - Default: **ipv4** - - :raises: - + :exc:`ValueError`, in case the desired layer_name is not an IP layer - + :exc:`KeyError`, in case the desired layer_name does not exists. - - """ - try: - layer = self._pkt_by_hdr[layer_name.lower()] - if not (isinstance(layer, dpkt.ip.IP) or isinstance(layer, dpkt.ip6.IP6)): - raise ValueError("The specified layer '{0}' is not of IPv4/IPv6 type.".format(layer_name)) - else: - decoded_ip = CTRexPktBuilder._decode_ip_addr(ip_addr, ip_type) - setattr(layer, attr, decoded_ip) - except KeyError: - raise KeyError("Specified layer '{0}' doesn't exist on packet.".format(layer_name)) - - def set_ipv6_layer_addr(self, layer_name, attr, ip_addr): - """ - This method sets the IPv6 address fields of an IP header (source or destination) - - :parameters: - layer_name: str - a string representing the name of the layer. - Example: "l3_ip", etc. - - attr: str - a string representation of the sub-field to be set: - - + "src" for source - + "dst" for destination - - ip_addr: str - a string representation of the IP address to be set. - Example: "5001::DB8:1:3333:1:1" - - :raises: - + :exc:`ValueError`, in case the desired layer_name is not an IPv6 layer - + :exc:`KeyError`, in case the desired layer_name does not exists. - - """ - self.set_ip_layer_addr(layer_name, attr, ip_addr, ip_type="ipv6") - - def set_eth_layer_addr(self, layer_name, attr, mac_addr): - """ - This method sets the ethernet address fields of an Ethernet header (source or destination) - using a human readable addressing representation. - - :parameters: - layer_name: str - a string representing the name of the layer. - Example: "l2", etc. - - attr: str - a string representation of the sub-field to be set: - + "src" for source - + "dst" for destination - - mac_addr: str - a string representation of the MAC address to be set. - Example: "00:de:34:ef:2e:f4". - - :raises: - + :exc:`ValueError`, in case the desired layer_name is not an Ethernet layer - + :exc:`KeyError`, in case the desired layer_name does not exists. - - """ - try: - layer = self._pkt_by_hdr[layer_name.lower()] - if not isinstance(layer, dpkt.ethernet.Ethernet): - raise ValueError("The specified layer '{0}' is not of Ethernet type.".format(layer_name)) - else: - decoded_mac = CTRexPktBuilder._decode_mac_addr(mac_addr) - setattr(layer, attr, decoded_mac) - except KeyError: - raise KeyError("Specified layer '{0}' doesn't exist on packet.".format(layer_name)) - - def set_layer_attr(self, layer_name, attr, val, toggle_bit=False): - """ - This method enables the user to change a value of a previously defined packet layer. - This method isn't to be used to set the data attribute of a packet with payload. - Use :func:`packet_builder.CTRexPktBuilder.set_payload` instead. - - :parameters: - layer_name: str - a string representing the name of the layer. - Example: "l2", "l4_tcp", etc. - - attr : str - a string representing the attribute to be changed on desired layer - - val : - value of attribute. - - toggle_bit : bool - Indicating if trying to set a specific bit of a field, such as "do not fragment" bit of IP layer. - - Default: **False** - - :raises: - + :exc:`KeyError`, in case of missing layer (the desired layer isn't part of packet) - + :exc:`ValueError`, in case invalid attribute to the specified layer. - - """ - try: - layer = self._pkt_by_hdr[layer_name.lower()] - if attr == 'data' and not isinstance(val, dpkt.Packet): - # Don't allow setting 'data' attribute - raise ValueError("Set a data attribute with object that is not dpkt.Packet is not allowed using " - "set_layer_attr method.\nUse set_payload method instead.") - if hasattr(layer, attr): - if toggle_bit: - setattr(layer, attr, val | getattr(layer, attr, 0)) - else: - setattr(layer, attr, val) - if attr == 'data': - # re-evaluate packet from the start, possible broken link between layers - self._reevaluate_packet(layer_name.lower()) - else: - raise ValueError("Given attr name '{0}' doesn't exists on specified layer ({1}).".format(layer_name, - attr)) - except KeyError: - raise KeyError("Specified layer '{0}' doesn't exist on packet.".format(layer_name)) - - def set_layer_bit_attr(self, layer_name, attr, val): - """ - This method enables the user to set the value of a field smaller that 1 Byte in size. - This method isn't used to set full-sized fields value (>= 1 byte). - Use :func:`packet_builder.CTRexPktBuilder.set_layer_attr` instead. - - :parameters: - layer_name: str - a string representing the name of the layer. - Example: "l2", "l4_tcp", etc. - - attr : str - a string representing the attribute to be set on desired layer - - val : int - value of attribute. - This value will be set "ontop" of the existing value using bitwise "OR" operation. - - .. tip:: It is very useful to use dpkt constants to define the values of these fields. - - :raises: - + :exc:`KeyError`, in case of missing layer (the desired layer isn't part of packet) - + :exc:`ValueError`, in case invalid attribute to the specified layer. - - """ - return self.set_layer_attr(layer_name, attr, val, True) - - def set_pkt_payload(self, payload): - """ - This method sets a payload to the topmost layer of the generated packet. - This method isn't to be used to set another networking layer to the packet. - Use :func:`packet_builder.CTRexPktBuilder.set_layer_attr` instead. - - - :parameters: - payload: - a payload to be added to the packet at the topmost layer. - this object cannot be of type dpkt.Packet. - - :raises: - + :exc:`AttributeError`, in case no underlying header to host the payload. - - """ - assert isinstance(payload, str) - try: - self._pkt_top_layer.data = payload - except AttributeError: - raise AttributeError("The so far built packet doesn't contain an option for payload attachment.\n" - "Make sure to set appropriate underlying header before adding payload") - - def load_packet(self, packet): - """ - This method enables the user to change a value of a previously defined packet layer. - - :parameters: - packet: dpkt.Packet obj - a dpkt object that represents a packet. - - - :raises: - + :exc:`CTRexPktBuilder.IPAddressError`, in case invalid ip type option specified. - - """ - assert isinstance(packet, dpkt.Packet) - self._packet = copy.copy(packet) - - self._pkt_by_hdr.clear() - self._pkt_top_layer = self._packet - # analyze packet to layers - tmp_layer = self._packet - while True: - if isinstance(tmp_layer, dpkt.Packet): - layer_name = self._gen_layer_name(type(tmp_layer).__name__) - self._pkt_by_hdr[layer_name] = tmp_layer - self._pkt_top_layer = tmp_layer - try: - # check existence of upper layer - tmp_layer = tmp_layer.data - except AttributeError: - # this is the most upper header - self._pkt_by_hdr['pkt_final_payload'] = tmp_layer.data - break - else: - self._pkt_by_hdr['pkt_final_payload'] = tmp_layer - break - return - - def load_packet_from_pcap(self, pcap_path): - """ - This method loads a pcap file into a parsed packet builder object. - - :parameters: - pcap_path: str - a path to a pcap file, containing a SINGLE packet. - - :raises: - + :exc:`IOError`, in case provided path doesn't exists. - - """ - with open(pcap_path, 'r') as f: - pcap = dpkt.pcap.Reader(f) - first_packet = True - for _, buf in pcap: - # this is an iterator, can't evaluate the number of files in advance - if first_packet: - self.load_packet(dpkt.ethernet.Ethernet(buf)) - else: - raise ValueError("Provided pcap file contains more than single packet.") - # arrive here ONLY if pcap contained SINGLE packet - return - - def load_from_stream_obj(self, stream_obj): - self.load_packet_from_byte_list(stream_obj['packet']['binary']) - - - def load_packet_from_byte_list(self, byte_list): - - buf = base64.b64decode(byte_list) - # thn, load it based on dpkt parsing - self.load_packet(dpkt.ethernet.Ethernet(buf)) - - def get_packet(self, get_ptr=False): - """ - This method provides access to the built packet, as an instance or as a pointer to packet itself. - - :parameters: - get_ptr : bool - indicate whether to get a reference to packet or a copy. - Use only in advanced modes - if set to true, metadata for packet is cleared, and any further modification is not guaranteed. - - default value : False - - :return: - + the current packet built by CTRexPktBuilder object. - + None if packet is empty - - """ - if get_ptr: - self._pkt_by_hdr = {} - self._pkt_top_layer = None - return self._packet - else: - return copy.copy(self._packet) - - def get_packet_length(self): - return len(self._packet) - - def get_layer(self, layer_name): - """ - This method provides access to a specific layer of the packet, as a **copy of the layer instance**. - - :parameters: - layer_name : str - the name given to desired layer - - :return: - + a copy of the desired layer of the current packet if exists. - + None if no such layer - - """ - layer = self._pkt_by_hdr.get(layer_name) - return copy.copy(layer) if layer else None - - - # VM access methods - def set_vm_ip_range(self, ip_layer_name, ip_field, - ip_start, ip_end, operation, - ip_init = None, add_value = 0, - is_big_endian=True, val_size=4, - ip_type="ipv4", add_checksum_inst=True, - split = False): - - if ip_field not in ["src", "dst"]: - raise ValueError("set_vm_ip_range only available for source ('src') or destination ('dst') ip addresses") - # set differences between IPv4 and IPv6 - if ip_type == "ipv4": - ip_class = dpkt.ip.IP - ip_addr_size = val_size if val_size <= 4 else 4 - elif ip_type == "ipv6": - ip_class = dpkt.ip6.IP6 - ip_addr_size = val_size if val_size <= 8 else 4 - else: - raise CTRexPktBuilder.IPAddressError() - - self._verify_layer_prop(ip_layer_name, ip_class) - trim_size = ip_addr_size*2 - start_val = int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_start, ip_type))[-trim_size:], 16) - end_val = int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_end, ip_type))[-trim_size:], 16) - - if ip_init == None: - init_val = start_val - else: - init_val = int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_init, ip_type))[-trim_size:], 16) - - - # All validations are done, start adding VM instructions - flow_var_name = "{layer}__{field}".format(layer=ip_layer_name, field=ip_field) - - hdr_offset, field_abs_offset = self._calc_offset(ip_layer_name, ip_field, ip_addr_size) - self.vm.add_flow_man_inst(flow_var_name, size=ip_addr_size, operation=operation, - init_value=init_val, - min_value=start_val, - max_value=end_val) - self.vm.add_write_flow_inst(flow_var_name, field_abs_offset) - self.vm.set_vm_off_inst_field(flow_var_name, "add_value", add_value) - self.vm.set_vm_off_inst_field(flow_var_name, "is_big_endian", is_big_endian) - if ip_type == "ipv4" and add_checksum_inst: - self.vm.add_fix_checksum_inst(self._pkt_by_hdr.get(ip_layer_name), hdr_offset) - - if split: - self.vm.set_split_by_var(flow_var_name) - - - def set_vm_eth_range(self, eth_layer_name, eth_field, - mac_init, mac_start, mac_end, add_value, - operation, val_size=4, is_big_endian=False): - if eth_field not in ["src", "dst"]: - raise ValueError("set_vm_eth_range only available for source ('src') or destination ('dst') eth addresses") - self._verify_layer_prop(eth_layer_name, dpkt.ethernet.Ethernet) - eth_addr_size = val_size if val_size <= 4 else 4 - trim_size = eth_addr_size*2 - init_val = int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_init))[-trim_size:], 16) - start_val = int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_start))[-trim_size:], 16) - end_val = int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_end))[-trim_size:], 16) - # All validations are done, start adding VM instructions - flow_var_name = "{layer}__{field}".format(layer=eth_layer_name, field=eth_field) - hdr_offset, field_abs_offset = self._calc_offset(eth_layer_name, eth_field, eth_addr_size) - self.vm.add_flow_man_inst(flow_var_name, size=8, operation=operation, - init_value=init_val, - min_value=start_val, - max_value=end_val) - self.vm.add_write_flow_inst(flow_var_name, field_abs_offset) - self.vm.set_vm_off_inst_field(flow_var_name, "add_value", add_value) - self.vm.set_vm_off_inst_field(flow_var_name, "is_big_endian", is_big_endian) - - def set_vm_custom_range(self, layer_name, hdr_field, - init_val, start_val, end_val, add_val, val_size, - operation, is_big_endian=True, range_name="", - add_checksum_inst=True): - # verify input validity for init/start/end values - for val in [init_val, start_val, end_val]: - if not isinstance(val, int): - raise ValueError("init/start/end values are expected integers, but received type '{0}'". - format(type(val))) - self._verify_layer_prop(layer_name=layer_name, field_name=hdr_field) - if not range_name: - range_name = "{layer}__{field}".format(layer=layer_name, field=hdr_field) - trim_size = val_size*2 - hdr_offset, field_abs_offset = self._calc_offset(layer_name, hdr_field, val_size) - self.vm.add_flow_man_inst(range_name, size=val_size, operation=operation, - init_value=init_val, - min_value=start_val, - max_value=end_val) - self.vm.add_write_flow_inst(range_name, field_abs_offset) - self.vm.set_vm_off_inst_field(range_name, "add_value", add_val) - self.vm.set_vm_off_inst_field(range_name, "is_big_endian", is_big_endian) - if isinstance(self._pkt_by_hdr.get(layer_name), dpkt.ip.IP) and add_checksum_inst: - self.vm.add_fix_checksum_inst(self._pkt_by_hdr.get(layer_name), hdr_offset) - - def get_vm_data(self): - return self.vm.dump() - - def compile (self): - pass - - def dump_pkt(self, encode = True): - """ - Dumps the packet as a decimal array of bytes (each item x gets value between 0-255) - - :parameters: - encode : bool - Encode using base64. (disable for debug) - - Default: **True** - - :return: - + packet representation as array of bytes - - :raises: - + :exc:`CTRexPktBuilder.EmptyPacketError`, in case packet is empty. - - """ - if self._packet is None: - raise CTRexPktBuilder.EmptyPacketError() - - if encode: - return {"binary": base64.b64encode(str(self._packet)), - "meta": self.metadata} - return {"binary": str(self._packet), - "meta": self.metadata} - - - def dump_pkt_to_pcap(self, file_path, ts=None): - """ - Dumps the packet as a decimal array of bytes (each item x gets value between 0-255) - - :parameters: - file_path : str - a path (including filename) to which to write to pcap file to. - - ts : int - a timestamp to attach to the packet when dumped to pcap file. - if ts in None, then time.time() is used to set the timestamp. - - Default: **None** - - :return: - None - - :raises: - + :exc:`CTRexPktBuilder.EmptyPacketError`, in case packet is empty. - - """ - if self._packet is None: - raise CTRexPktBuilder.EmptyPacketError() - try: - with open(file_path, 'wb') as f: - pcap_wr = dpkt.pcap.Writer(f) - pcap_wr.writepkt(self._packet, ts) - return - except IOError: - raise IOError(2, "The provided path could not be accessed") - - def get_packet_layers(self, depth_limit=Ellipsis): - if self._packet is None: - raise CTRexPktBuilder.EmptyPacketError() - cur_layer = self._packet - layer_types = [] - if depth_limit == Ellipsis: - iterator = itertools.count(1) - else: - iterator = xrange(depth_limit) - for _ in iterator: - # append current layer type - if isinstance(cur_layer, dpkt.Packet): - layer_types.append(type(cur_layer).__name__) - else: - # if not dpkt layer, refer as payload - layer_types.append("PLD") - # advance to next layer - if not hasattr(cur_layer, "data"): - break - else: - cur_layer = cur_layer.data - return layer_types - - def export_pkt(self, file_path, link_pcap=False, pcap_name=None, pcap_ts=None): - pass - - # ----- internal methods ----- # - def _reevaluate_packet(self, layer_name): - cur_layer = self._packet - known_layers = set(self._pkt_by_hdr.keys()) - found_layers = set() - while True: - pointing_layer_name = self._find_pointing_layer(known_layers, cur_layer) - found_layers.add(pointing_layer_name) - if self._pkt_by_hdr[layer_name] is cur_layer: - self._pkt_top_layer = cur_layer - disconnected_layers = known_layers.difference(found_layers) - # remove disconnected layers - for layer in disconnected_layers: - self._pkt_by_hdr.pop(layer) - break - else: - cur_layer = cur_layer.data - - def _gen_layer_name(self, layer_class_name): - assert isinstance(layer_class_name, str) - layer_name = layer_class_name.lower() - idx = 1 - while True: - tmp_name = "{name}_{id}".format(name=layer_name, id=idx) - if tmp_name not in self._pkt_by_hdr: - return tmp_name - else: - idx += 1 - - def _find_pointing_layer(self, known_layers, layer_obj): - assert isinstance(known_layers, set) - for layer in known_layers: - if self._pkt_by_hdr[layer] is layer_obj: - return layer - - def _calc_offset(self, layer_name, hdr_field, hdr_field_size): - pkt_header = self._pkt_by_hdr.get(layer_name) - hdr_offset = len(self._packet) - len(pkt_header) - inner_hdr_offsets = [] - for field in pkt_header.__hdr__: - if field[0] == hdr_field: - field_size = struct.calcsize(field[1]) - if field_size == hdr_field_size: - break - elif field_size < hdr_field_size: - raise CTRexPktBuilder.PacketLayerError(layer_name, - "The specified field '{0}' size is smaller than given range" - " size ('{1}')".format(hdr_field, hdr_field_size)) - else: - inner_hdr_offsets.append(field_size - hdr_field_size) - break - else: - inner_hdr_offsets.append(struct.calcsize(field[1])) - return hdr_offset, hdr_offset + sum(inner_hdr_offsets) - - def _verify_layer_prop(self, layer_name, layer_type=None, field_name=None): - if layer_name not in self._pkt_by_hdr: - raise CTRexPktBuilder.PacketLayerError(layer_name) - pkt_layer = self._pkt_by_hdr.get(layer_name) - if layer_type: - # check for layer type - if not isinstance(pkt_layer, layer_type): - raise CTRexPktBuilder.PacketLayerTypeError(layer_name, type(pkt_layer), layer_type) - if field_name and not hasattr(pkt_layer, field_name): - # check if field exists on certain header - raise CTRexPktBuilder.PacketLayerError(layer_name, "The specified field '{0}' does not exists on " - "given packet layer ('{1}')".format(field_name, - layer_name)) - return - - @property - def payload_gen(self): - return CTRexPktBuilder.CTRexPayloadGen(self._packet, self._max_pkt_size) - - @staticmethod - def _decode_mac_addr(mac_addr): - """ - Static method to test for MAC address validity. - - :parameters: - mac_addr : str - a string representing an MAC address, separated by ':' or '-'. - - examples: '00:de:34:ef:2e:f4', '00-de-34-ef-2e-f4 - - :return: - + an hex-string representation of the MAC address. - for example, ip 00:de:34:ef:2e:f4 will return '\x00\xdeU\xef.\xf4' - - :raises: - + :exc:`CTRexPktBuilder.MACAddressError`, in case invalid ip type option specified. - - """ - tmp_mac = mac_addr.lower().replace('-', ':') - if re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", tmp_mac): - return binascii.unhexlify(tmp_mac.replace(':', '')) - # another option for both Python 2 and 3: - # codecs.decode(tmp_mac.replace(':', ''), 'hex') - else: - raise CTRexPktBuilder.MACAddressError() - - @staticmethod - def _decode_ip_addr(ip_addr, ip_type): - """ - Static method to test for IPv4/IPv6 address validity. - - :parameters: - ip_addr : str - a string representing an IP address (IPv4/IPv6) - - ip_type : str - The type of IP to be checked. - Valid types: "ipv4", "ipv6". - - :return: - + an hex-string representation of the ip address. - for example, ip 1.2.3.4 will return '\x01\x02\x03\x04' - - :raises: - + :exc:`CTRexPktBuilder.IPAddressError`, in case invalid ip type option specified. - - """ - if ip_type == "ipv4": - try: - return socket.inet_pton(socket.AF_INET, ip_addr) - except AttributeError: # no inet_pton here, sorry - # try: - return socket.inet_aton(ip_addr) - # except socket.error: - # return False - # return ip_addr.count('.') == 3 - except socket.error: # not a valid address - raise CTRexPktBuilder.IPAddressError() - elif ip_type == "ipv6": - try: - return socket.inet_pton(socket.AF_INET6, ip_addr) - except socket.error: # not a valid address - raise CTRexPktBuilder.IPAddressError() - else: - raise CTRexPktBuilder.IPAddressError() - - # ------ private classes ------ # - class CTRexPayloadGen(object): - - def __init__(self, packet_ref, max_pkt_size): - self._pkt_ref = packet_ref - self._max_pkt_size = max_pkt_size - - def gen_random_str(self): - gen_length = self._calc_gen_length() - # return a string of size gen_length bytes, to pad the packet to its max_size - return ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) - for _ in range(gen_length)) - - def gen_repeat_ptrn(self, ptrn_to_repeat): - gen_length = self._calc_gen_length() - if isinstance(ptrn_to_repeat, str): - # generate repeated string - return (ptrn_to_repeat * (gen_length/len(ptrn_to_repeat) + 1))[:gen_length] - elif isinstance(ptrn_to_repeat, int): - ptrn = binascii.unhexlify(hex(ptrn_to_repeat)[2:]) - return (ptrn * (gen_length/len(ptrn) + 1))[:gen_length] - elif isinstance(ptrn_to_repeat, tuple): - if not all((isinstance(x, int) and (x < 255) and (x >= 0)) - for x in ptrn_to_repeat): - raise ValueError("All numbers in tuple must be in range 0 <= number <= 255 ") - # generate repeated sequence - to_pack = (ptrn_to_repeat * (gen_length/len(ptrn_to_repeat) + 1))[:gen_length] - return struct.pack('B'*gen_length, *to_pack) - else: - raise ValueError("Given ptrn_to_repeat argument type ({0}) is illegal.". - format(type(ptrn_to_repeat))) - - def _calc_gen_length(self): - return self._max_pkt_size - len(self._pkt_ref) - - class CTRexVM(object): - """ - This class defines the TRex VM which represents how TRex will regenerate packets. - The packets will be regenerated based on the built packet containing this class. - """ - InstStore = namedtuple('InstStore', ['type', 'inst']) - - def __init__(self): - """ - Instantiate a CTRexVM object - - :parameters: - None - """ - super(CTRexPktBuilder.CTRexVM, self).__init__() - self.vm_variables = {} - self._inst_by_offset = {} # this data structure holds only offset-related instructions, ordered in tuples - self._off_inst_by_name = {} - self.split_by_var = '' - - def set_vm_var_field(self, var_name, field_name, val, offset_inst=False): - """ - Set VM variable field. Only existing variables are allowed to be changed. - - :parameters: - var_name : str - a string representing the name of the VM variable to be changed. - field_name : str - a string representing the field name of the VM variable to be changed. - val : - a value to be applied to field_name field of the var_name VM variable. - - :raises: - + :exc:`KeyError`, in case invalid var_name has been specified. - + :exc:`CTRexPktBuilder.VMVarFieldTypeError`, in case mismatch between `val` and allowed type. - + :exc:`CTRexPktBuilder.VMVarValueError`, in case val isn't one of allowed options of field_name. - - """ - if offset_inst: - return self._off_inst_by_name[var_name].inst.set_field(field_name, val) - else: - return self.vm_variables[var_name].set_field(field_name, val) - - def set_vm_off_inst_field(self, var_name, field_name, val): - return self.set_vm_var_field(var_name, field_name, val, True) - - def add_flow_man_inst(self, name, **kwargs): - """ - Adds a new flow manipulation object to the VM instance. - - :parameters: - name : str - name of the manipulation, must be distinct. - Example: 'source_ip_change' - - **kwargs** : dict - optional, set flow_man fields on initialization (key = field_name, val = field_val). - Must be used with legit fields, see :func:`CTRexPktBuilder.CTRexVM.CTRexVMVariable.set_field`. - - :return: - None - - :raises: - + :exc:`CTRexPktBuilder.VMVarNameExistsError`, in case of desired flow_man name already taken. - + Exceptions from :func:`CTRexPktBuilder.CTRexVM.CTRexVMVariable.set_field` method. - Will rise when VM variables were misconfiguration. - """ - if name not in self.vm_variables: - self.vm_variables[name] = self.CTRexVMFlowVariable(name) - # try configuring VM instruction attributes - for (field, value) in kwargs.items(): - self.vm_variables[name].set_field(field, value) - else: - raise CTRexPktBuilder.VMVarNameExistsError(name) - - def add_fix_checksum_inst(self, linked_ipv4_obj, offset_to_obj=14, name=None): - # check if specified linked_ipv4_obj is indeed an ipv4 object - if not (isinstance(linked_ipv4_obj, dpkt.ip.IP)): - raise ValueError("The provided layer object is not of IPv4.") - if not name: - name = "checksum_{off}".format(off=offset_to_obj) # name will override previous checksum inst, OK - new_checksum_inst = self.CTRexVMChecksumInst(name, offset_to_obj) - # store the checksum inst in the end of the IP header (20 Bytes long) - inst = self.InstStore('checksum', new_checksum_inst) - self._inst_by_offset[offset_to_obj + 20] = inst - self._off_inst_by_name[name] = inst - - def add_write_flow_inst(self, name, pkt_offset, **kwargs): - if name not in self.vm_variables: - raise KeyError("Trying to add write_flow_var instruction to a not-exists VM flow variable ('{0}')". - format(name)) - else: - new_write_inst = self.CTRexVMWrtFlowVarInst(name, pkt_offset) - # try configuring VM instruction attributes - for (field, value) in kwargs.items(): - new_write_inst.set_field(field, value) - # add the instruction to the date-structure - inst = self.InstStore('write', new_write_inst) - self._inst_by_offset[pkt_offset] = inst - self._off_inst_by_name[name] = inst - - def load_flow_man(self, flow_obj): - """ - Loads an outer VM variable (instruction) into current VM. - The outer VM variable must contain different name than existing VM variables currently registered on VM. - - :parameters: - flow_obj : CTRexVMVariable - a CTRexVMVariable to be loaded into VM variable sets. - - :return: - list holds variables data of VM - - """ - assert isinstance(flow_obj, CTRexPktBuilder.CTRexVM.CTRexVMFlowVariable) - if flow_obj.name not in self.vm_variables.keys(): - self.vm_variables[flow_obj.name] = flow_obj - else: - raise CTRexPktBuilder.VMVarNameExistsError(flow_obj.name) - - def set_split_by_var (self, var_name): - if var_name not in self.vm_variables: - raise KeyError("cannot set split by var to an unknown VM var ('{0}')". - format(var_name)) - - self.split_by_var = var_name - - def dump(self): - """ - dumps a VM variables (instructions) and split_by_var into a dict data structure. - - :parameters: - None - - :return: - dict with VM instructions as list and split_by_var as str - - """ - - # at first, dump all CTRexVMFlowVariable instructions - inst_array = [var.dump() if hasattr(var, 'dump') else var - for key, var in self.vm_variables.items()] - # then, dump all the CTRexVMWrtFlowVarInst and CTRexVMChecksumInst instructions - inst_array += [self._inst_by_offset.get(key).inst.dump() - for key in sorted(self._inst_by_offset)] - return {'instructions': inst_array, 'split_by_var': self.split_by_var} - - class CVMAbstractInstruction(object): - __metaclass__ = ABCMeta - - def __init__(self, name): - """ - Instantiate a CTRexVMVariable object - - :parameters: - name : str - a string representing the name of the VM variable. - """ - super(CTRexPktBuilder.CTRexVM.CVMAbstractInstruction, self).__init__() - self.name = name - - def set_field(self, field_name, val): - if not hasattr(self, field_name): - raise CTRexPktBuilder.VMFieldNameError(field_name) - setattr(self, field_name, val) - - @abstractmethod - def dump(self): - pass - - class CTRexVMFlowVariable(CVMAbstractInstruction): - """ - This class defines a single VM variable to be used as part of CTRexVar object. - """ - VALID_SIZE = [1, 2, 4, 8] # size in Bytes - VALID_OPERATION = ["inc", "dec", "random"] - - def __init__(self, name): - """ - Instantiate a CTRexVMVariable object - - :parameters: - name : str - a string representing the name of the VM variable. - """ - super(CTRexPktBuilder.CTRexVM.CTRexVMFlowVariable, self).__init__(name) - # self.name = name - self.size = 4 - self.big_endian = True - self.operation = "inc" - # self.split_by_core = False - self.init_value = 1 - self.min_value = self.init_value - self.max_value = self.init_value - - def set_field(self, field_name, val): - """ - Set VM variable field. Only existing variables are allowed to be changed. - - :parameters: - field_name : str - a string representing the field name of the VM variable to be changed. - val : - a value to be applied to field_name field of the var_name VM variable. - - :return: - None - - :raises: - + :exc:`CTRexPktBuilder.VMVarNameError`, in case of illegal field name. - + :exc:`CTRexPktBuilder.VMVarFieldTypeError`, in case mismatch between `val` and allowed type. - + :exc:`CTRexPktBuilder.VMVarValueError`, in case val isn't one of allowed options of field_name. - - """ - if not hasattr(self, field_name): - raise CTRexPktBuilder.VMFieldNameError(field_name) - elif field_name == "size": - if type(val) != int: - raise CTRexPktBuilder.VMFieldTypeError("size", int) - elif val not in self.VALID_SIZE: - raise CTRexPktBuilder.VMFieldValueError("size", self.VALID_SIZE) - elif field_name in ["init_value", "min_value", "max_value"]: - if type(val) != int: - raise CTRexPktBuilder.VMFieldTypeError(field_name, int) - elif field_name == "operation": - if type(val) != str: - raise CTRexPktBuilder.VMFieldTypeError("operation", str) - elif val not in self.VALID_OPERATION: - raise CTRexPktBuilder.VMFieldValueError("operation", self.VALID_OPERATION) - # elif field_name == "split_by_core": - # val = bool(val) - # update field value on success - setattr(self, field_name, val) - - def dump(self): - """ - dumps a variable fields in a dictionary data structure. - - :parameters: - None - - :return: - dictionary holds variable data of VM variable - - """ - return {"type": "flow_var", # VM variable dump always refers to manipulate instruction. - "name": self.name, - "size": self.size, - "op": self.operation, - # "split_by_core": self.split_by_core, - "init_value": self.init_value, - "min_value": self.min_value, - "max_value": self.max_value} - - class CTRexVMChecksumInst(CVMAbstractInstruction): - - def __init__(self, name, offset): - """ - Instantiate a CTRexVMChecksumInst object - - :parameters: - name : str - a string representing the name of the VM variable. - """ - super(CTRexPktBuilder.CTRexVM.CTRexVMChecksumInst, self).__init__(name) - self.pkt_offset = offset - - def dump(self): - return {"type": "fix_checksum_ipv4", - "pkt_offset": int(self.pkt_offset)} - - class CTRexVMWrtFlowVarInst(CVMAbstractInstruction): - - def __init__(self, name, pkt_offset): - """ - Instantiate a CTRexVMWrtFlowVarInst object - - :parameters: - name : str - a string representing the name of the VM variable. - """ - super(CTRexPktBuilder.CTRexVM.CTRexVMWrtFlowVarInst, self).__init__(name) - self.pkt_offset = int(pkt_offset) - self.add_value = 0 - self.is_big_endian = False - - def set_field(self, field_name, val): - if not hasattr(self, field_name): - raise CTRexPktBuilder.VMFieldNameError(field_name) - elif field_name == 'pkt_offset': - raise ValueError("pkt_offset value cannot be changed") - cur_attr_type = type(getattr(self, field_name)) - if cur_attr_type == type(val): - setattr(self, field_name, val) - else: - CTRexPktBuilder.VMFieldTypeError(field_name, cur_attr_type) - - def dump(self): - return {"type": "write_flow_var", - "name": self.name, - "pkt_offset": self.pkt_offset, - "add_value": int(self.add_value), - "is_big_endian": bool(self.is_big_endian) - } - - class CTRexVMChecksumInst(CVMAbstractInstruction): - - def __init__(self, name, offset): - """ - Instantiate a CTRexVMChecksumInst object - - :parameters: - name : str - a string representing the name of the VM variable. - """ - super(CTRexPktBuilder.CTRexVM.CTRexVMChecksumInst, self).__init__(name) - self.pkt_offset = offset - - def dump(self): - return {"type": "fix_checksum_ipv4", - "pkt_offset": int(self.pkt_offset)} - - class CTRexVMWrtFlowVarInst(CVMAbstractInstruction): - - def __init__(self, name, pkt_offset): - """ - Instantiate a CTRexVMWrtFlowVarInst object - - :parameters: - name : str - a string representing the name of the VM variable. - """ - super(CTRexPktBuilder.CTRexVM.CTRexVMWrtFlowVarInst, self).__init__(name) - self.pkt_offset = int(pkt_offset) - self.add_value = 0 - self.is_big_endian = False - - def set_field(self, field_name, val): - if not hasattr(self, field_name): - raise CTRexPktBuilder.VMFieldNameError(field_name) - elif field_name == 'pkt_offset': - raise ValueError("pkt_offset value cannot be changed") - cur_attr_type = type(getattr(self, field_name)) - if cur_attr_type == type(val): - setattr(self, field_name, val) - else: - CTRexPktBuilder.VMFieldTypeError(field_name, cur_attr_type) - - def dump(self): - return {"type": "write_flow_var", - "name": self.name, - "pkt_offset": self.pkt_offset, - "add_value": int(self.add_value), - "is_big_endian": bool(self.is_big_endian) - } - - class CPacketBuildException(Exception): - """ - This is the general Packet Building error exception class. - """ - def __init__(self, code, message): - self.code = code - self.message = message - - def __str__(self): - return self.__repr__() - - def __repr__(self): - return u"[errcode:%r] %r" % (self.code, self.message) - - class EmptyPacketError(CPacketBuildException): - """ - This exception is used to indicate an error caused by operation performed on an empty packet. - """ - def __init__(self, message=''): - self._default_message = 'Illegal operation on empty packet.' - self.message = message or self._default_message - super(CTRexPktBuilder.EmptyPacketError, self).__init__(-10, self.message) - - class IPAddressError(CPacketBuildException): - """ - This exception is used to indicate an error on the IP addressing part of the packet. - """ - def __init__(self, message=''): - self._default_message = 'Illegal type or value of IP address has been provided.' - self.message = message or self._default_message - super(CTRexPktBuilder.IPAddressError, self).__init__(-11, self.message) - - class MACAddressError(CPacketBuildException): - """ - This exception is used to indicate an error on the MAC addressing part of the packet. - """ - def __init__(self, message=''): - self._default_message = 'Illegal MAC address has been provided.' - self.message = message or self._default_message - super(CTRexPktBuilder.MACAddressError, self).__init__(-12, self.message) - - class PacketLayerError(CPacketBuildException): - """ - This exception is used to indicate an error caused by operation performed on an non-exists layer of the packet. - """ - def __init__(self, name, message=''): - self._default_message = "The given packet layer name ({0}) does not exists.".format(name) - self.message = message or self._default_message - super(CTRexPktBuilder.PacketLayerError, self).__init__(-13, self.message) - - class PacketLayerTypeError(CPacketBuildException): - """ - This exception is used to indicate an error caused by operation performed on an non-exists layer of the packet. - """ - def __init__(self, name, layer_type, ok_type, message=''): - self._default_message = "The type of packet layer {layer_name} is of type {layer_type}, " \ - "and not of the expected {allowed_type}.".format(layer_name=name, - layer_type=layer_type, - allowed_type=ok_type.__name__) - self.message = message or self._default_message - super(CTRexPktBuilder.PacketLayerTypeError, self).__init__(-13, self.message) - - class VMVarNameExistsError(CPacketBuildException): - """ - This exception is used to indicate a duplicate usage of VM variable. - """ - def __init__(self, name, message=''): - self._default_message = 'The given VM name ({0}) already exists as part of the stream.'.format(name) - self.message = message or self._default_message - super(CTRexPktBuilder.VMVarNameExistsError, self).__init__(-21, self.message) - - class VMFieldNameError(CPacketBuildException): - """ - This exception is used to indicate that an undefined VM var field name has been accessed. - """ - def __init__(self, name, message=''): - self._default_message = "The given VM field name ({0}) is not defined and isn't legal.".format(name) - self.message = message or self._default_message - super(CTRexPktBuilder.VMFieldNameError, self).__init__(-22, self.message) - - class VMFieldTypeError(CPacketBuildException): - """ - This exception is used to indicate an illegal value has type has been given to VM variable field. - """ - def __init__(self, name, ok_type, message=''): - self._default_message = "The desired value of field {field_name} is of type {field_type}, " \ - "and not of the allowed {allowed_type}.".format(field_name=name, - field_type=type(name).__name__, - allowed_type=ok_type.__name__) - self.message = message or self._default_message - super(CTRexPktBuilder.VMFieldTypeError, self).__init__(-31, self.message) - - class VMFieldValueError(CPacketBuildException): - """ - This exception is used to indicate an error an illegal value has been assigned to VM variable field. - """ - def __init__(self, name, ok_opts, message=''): - self._default_message = "The desired value of field {field_name} is illegal.\n" \ - "The only allowed options are: {allowed_opts}.".format(field_name=name, - allowed_opts=ok_opts) - self.message = message or self._default_message - super(CTRexPktBuilder.VMFieldValueError, self).__init__(-32, self.message) - - -if __name__ == "__main__": - pass diff --git a/scripts/automation/trex_control_plane/client_utils/packet_builder_interface.py b/scripts/automation/trex_control_plane/client_utils/packet_builder_interface.py deleted file mode 100644 index b6e7c026..00000000 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder_interface.py +++ /dev/null @@ -1,43 +0,0 @@ - -# base object class for a packet builder -class CTrexPktBuilderInterface(object): - - def compile (self): - """ - Compiles the packet and VM - """ - raise Exception("implement me") - - - def dump_pkt(self): - """ - Dumps the packet as a decimal array of bytes (each item x gets value between 0-255) - - :parameters: - None - - :return: - + packet representation as array of bytes - - :raises: - + :exc:`CTRexPktBuilder.EmptyPacketError`, in case packet is empty. - - """ - - raise Exception("implement me") - - - def get_vm_data(self): - """ - Dumps the instructions - - :parameters: - None - - :return: - + json object of instructions - - """ - - raise Exception("implement me") - diff --git a/scripts/automation/trex_control_plane/client_utils/scapy_packet_builder.py b/scripts/automation/trex_control_plane/client_utils/scapy_packet_builder.py deleted file mode 100644 index b1b181c6..00000000 --- a/scripts/automation/trex_control_plane/client_utils/scapy_packet_builder.py +++ /dev/null @@ -1,748 +0,0 @@ -import external_packages -import random -import string -import struct -import socket -import json -import yaml -import binascii -import base64 - -from packet_builder_interface import CTrexPktBuilderInterface - -from scapy.all import * - - - -class CTRexPacketBuildException(Exception): - """ - This is the general Packet Building error exception class. - """ - def __init__(self, code, message): - self.code = code - self.message = message - - def __str__(self): - return self.__repr__() - - def __repr__(self): - return u"[errcode:%r] %r" % (self.code, self.message) - -################################################################################################ - -def ipv4_str_to_num (ipv4_buffer): - - assert type(ipv4_buffer)==str, 'type of ipv4_buffer is not str' - assert len(ipv4_buffer)==4, 'size of ipv4_buffer is not 4' - res=0 - shift=24 - for i in ipv4_buffer: - res = res + (ord(i)< max_value: - raise CTRexPacketBuildException(-12, 'min is greater than max'); - if min_value == max_value: - raise CTRexPacketBuildException(-13, "min value is equal to max value, you can't use this type of range"); - - -class CTRexScIpv4SimpleRange(CTRexScFieldRangeBase): - """ - range of ipv4 ip - """ - def __init__(self, field_name, field_type, min_ip, max_ip): - super(CTRexScIpv4SimpleRange, self).__init__(field_name,field_type) - self.min_ip = min_ip - self.max_ip = max_ip - mmin=ipv4_str_to_num (is_valid_ipv4(min_ip)) - mmax=ipv4_str_to_num (is_valid_ipv4(max_ip)) - if mmin > mmax : - raise CTRexPacketBuildException(-11, 'CTRexScIpv4SimpleRange m_min ip is bigger than max'); - - -class CTRexScIpv4TupleGen(CTRexScriptsBase): - """ - range tuple - """ - FLAGS_ULIMIT_FLOWS =1 - - def __init__(self, min_ipv4, max_ipv4, num_flows=100000, min_port=1025, max_port=65535, flags=0): - super(CTRexScIpv4TupleGen, self).__init__() - self.min_ip = min_ipv4 - self.max_ip = max_ipv4 - mmin=ipv4_str_to_num (is_valid_ipv4(min_ipv4)) - mmax=ipv4_str_to_num (is_valid_ipv4(max_ipv4)) - if mmin > mmax : - raise CTRexPacketBuildException(-11, 'CTRexScIpv4SimpleRange m_min ip is bigger than max'); - - self.num_flows=num_flows; - - self.min_port =min_port - self.max_port =max_port - self.flags = flags - - -class CTRexScTrimPacketSize(CTRexScriptsBase): - """ - trim packet size. field type is CTRexScFieldRangeBase.FILED_TYPES = ["inc","dec","rand"] - """ - def __init__(self,field_type="rand",min_pkt_size=None, max_pkt_size=None): - super(CTRexScTrimPacketSize, self).__init__() - self.field_type = field_type - self.min_pkt_size = min_pkt_size - self.max_pkt_size = max_pkt_size - if max_pkt_size != None and min_pkt_size !=None : - if min_pkt_size == max_pkt_size: - raise CTRexPacketBuildException(-11, 'CTRexScTrimPacketSize min_pkt_size is the same as max_pkt_size '); - - if min_pkt_size > max_pkt_size: - raise CTRexPacketBuildException(-11, 'CTRexScTrimPacketSize min_pkt_size is bigger than max_pkt_size '); - -class CTRexScRaw(CTRexScriptsBase): - """ - raw instructions - """ - def __init__(self,list_of_commands=None): - super(CTRexScRaw, self).__init__() - if list_of_commands==None: - self.commands =[] - else: - self.commands = list_of_commands - - def add_cmd (self,cmd): - self.commands.append(cmd) - - - -################################################################################################ -# VM raw instructions -################################################################################################ - -class CTRexVmInsBase(object): - """ - instruction base - """ - def __init__(self, ins_type): - self.type = ins_type - assert type(ins_type)==str, 'type of ins_type is not str' - -class CTRexVmInsFixIpv4(CTRexVmInsBase): - def __init__(self, offset): - super(CTRexVmInsFixIpv4, self).__init__("fix_checksum_ipv4") - self.pkt_offset = offset - assert type(offset)==int, 'type of offset is not int' - - -class CTRexVmInsFlowVar(CTRexVmInsBase): - #TBD add more validation tests - - OPERATIONS =['inc', 'dec', 'random'] - VALID_SIZES =[1, 2, 4, 8] - - def __init__(self, fv_name, size, op, init_value, min_value, max_value): - super(CTRexVmInsFlowVar, self).__init__("flow_var") - self.name = fv_name; - assert type(fv_name)==str, 'type of fv_name is not str' - self.size = size - self.op = op - self.init_value = init_value - assert type(init_value)==int, 'type of init_value is not int' - self.min_value=min_value - assert type(min_value)==int, 'type of min_value is not int' - self.max_value=max_value - assert type(max_value)==int, 'type of min_value is not int' - -class CTRexVmInsWrFlowVar(CTRexVmInsBase): - def __init__(self, fv_name, pkt_offset, add_value=0, is_big_endian=True): - super(CTRexVmInsWrFlowVar, self).__init__("write_flow_var") - self.name = fv_name - assert type(fv_name)==str, 'type of fv_name is not str' - self.pkt_offset = pkt_offset - assert type(pkt_offset)==int, 'type of pkt_offset is not int' - self.add_value = add_value - assert type(add_value)==int, 'type of add_value is not int' - self.is_big_endian = is_big_endian - assert type(is_big_endian)==bool, 'type of is_big_endian is not bool' - -class CTRexVmInsTrimPktSize(CTRexVmInsBase): - def __init__(self,fv_name): - super(CTRexVmInsTrimPktSize, self).__init__("trim_pkt_size") - self.name = fv_name - assert type(fv_name)==str, 'type of fv_name is not str' - -class CTRexVmInsTupleGen(CTRexVmInsBase): - def __init__(self, fv_name, ip_min, ip_max, port_min, port_max, limit_flows, flags=0): - super(CTRexVmInsTupleGen, self).__init__("tuple_flow_var") - self.name =fv_name - assert type(fv_name)==str, 'type of fv_name is not str' - self.ip_min = ip_min; - self.ip_max = ip_max; - self.port_min = port_min; - self.port_max = port_max; - self.limit_flows = limit_flows; - self.flags =flags; - - -################################################################################################ -# -class CTRexVmEngine(object): - - def __init__(self): - """ - inlcude list of instruction - """ - super(CTRexVmEngine, self).__init__() - self.ins=[] - self.split_by_var = '' - - # return as json - def get_json (self): - inst_array = []; - # dump it as dict - for obj in self.ins: - inst_array.append(obj.__dict__); - - return {'instructions': inst_array, 'split_by_var': self.split_by_var} - - def add_ins (self,ins): - #assert issubclass(ins, CTRexVmInsBase) - self.ins.append(ins); - - def dump (self): - cnt=0; - for obj in self.ins: - print "ins",cnt - cnt = cnt +1 - print obj.__dict__ - - def dump_bjson (self): - print json.dumps(self.get_json(), sort_keys=True, indent=4) - - def dump_as_yaml (self): - print yaml.dump(self.get_json(), default_flow_style=False) - - - -################################################################################################ - -class CTRexScapyPktUtl(object): - - def __init__(self, scapy_pkt): - self.pkt = scapy_pkt - - def pkt_iter (self): - p=self.pkt; - while True: - yield p - p=p.payload - if p ==None or isinstance(p,NoPayload): - break; - - def get_list_iter(self): - l=list(self.pkt_iter()) - return l - - - def get_pkt_layers(self): - """ - return string 'IP:UDP:TCP' - """ - l=self.get_list_iter (); - l1=map(lambda p: p.name,l ); - return ":".join(l1); - - def _layer_offset(self, name, cnt = 0): - """ - return offset of layer e.g 'IP',1 will return offfset of layer ip:1 - """ - save_cnt=cnt - for pkt in self.pkt_iter (): - if pkt.name == name: - if cnt==0: - return (pkt, pkt.offset) - else: - cnt=cnt -1 - - raise CTRexPacketBuildException(-11,("no layer %s-%d" % (name, save_cnt))); - - - def layer_offset(self, name, cnt = 0): - """ - return offset of layer e.g 'IP',1 will return offfset of layer ip:1 - """ - save_cnt=cnt - for pkt in self.pkt_iter (): - if pkt.name == name: - if cnt==0: - return pkt.offset - else: - cnt=cnt -1 - - raise CTRexPacketBuildException(-11,("no layer %s-%d" % (name, save_cnt))); - - def get_field_offet(self, layer, layer_cnt, field_name): - """ - return offset of layer e.g 'IP',1 will return offfset of layer ip:1 - """ - t=self._layer_offset(layer,layer_cnt); - l_offset=t[1]; - layer_pkt=t[0] - - #layer_pkt.dump_fields_offsets () - - for f in layer_pkt.fields_desc: - if f.name == field_name: - return (l_offset+f.offset,f.get_size_bytes ()); - - raise CTRexPacketBuildException(-11, "no layer %s-%d." % (name, save_cnt, field_name)); - - def get_layer_offet_by_str(self, layer_des): - """ - return layer offset by string - - :parameters: - - IP:0 - IP:1 - return offset - - - """ - l1=layer_des.split(":") - layer="" - layer_cnt=0; - - if len(l1)==1: - layer=l1[0]; - else: - layer=l1[0]; - layer_cnt=int(l1[1]); - - return self.layer_offset(layer, layer_cnt) - - - - def get_field_offet_by_str(self, field_des): - """ - return field_des (offset,size) layer:cnt.field - for example - 802|1Q.vlan get 802.1Q->valn replace | with . - IP.src - IP:0.src (first IP.src like IP.src) - for example IP:1.src for internal IP - - return (offset, size) as tuple - - - """ - - s=field_des.split("."); - if len(s)!=2: - raise CTRexPacketBuildException(-11, ("field desription should be layer:cnt.field e.g IP.src or IP:1.src")); - - - layer_ex = s[0].replace("|",".") - field = s[1] - - l1=layer_ex.split(":") - layer="" - layer_cnt=0; - - if len(l1)==1: - layer=l1[0]; - else: - layer=l1[0]; - layer_cnt=int(l1[1]); - - return self.get_field_offet(layer,layer_cnt,field) - - def has_IPv4 (self): - return self.pkt.has_layer("IP"); - - def has_IPv6 (self): - return self.pkt.has_layer("IPv6"); - - def has_UDP (self): - return self.pkt.has_layer("UDP"); - -################################################################################################ - -class CTRexVmDescBase(object): - """ - instruction base - """ - def __init__(self): - pass; - - def get_obj(self): - return self; - - def get_json(self): - return self.get_obj().__dict__ - - def dump_bjson(self): - print json.dumps(self.get_json(), sort_keys=True, indent=4) - - def dump_as_yaml(self): - print yaml.dump(self.get_json(), default_flow_style=False) - - - def get_var_ref (self): - ''' - virtual function return a ref var name - ''' - return None - - def get_var_name(self): - ''' - virtual function return the varible name if exists - ''' - return None - - def compile(self,parent): - ''' - virtual function to take parent than has function name_to_offset - ''' - pass; - - -def valid_fv_size (size): - if not (size in CTRexVmInsFlowVar.VALID_SIZES): - raise CTRexPacketBuildException(-11,("flow var has not valid size %d ") % size ); - -def valid_fv_ops (op): - if not (op in CTRexVmInsFlowVar.OPERATIONS): - raise CTRexPacketBuildException(-11,("flow var does not have a valid op %s ") % op ); - -def convert_val (val): - if type(val) == int: - return val - else: - if type(val) == str: - return ipv4_str_to_num (is_valid_ipv4(val)) - else: - raise CTRexPacketBuildException(-11,("init val not valid %s ") % val ); - -def check_for_int (val): - assert type(val)==int, 'type of vcal is not int' - - -class CTRexVmDescFlowVar(CTRexVmDescBase): - def __init__(self, name, init_value=None, min_value=0, max_value=255, size=4, op="inc"): - super(CTRexVmDescFlowVar, self).__init__() - self.name = name; - assert type(name)==str, 'type of name is not str' - self.size =size - valid_fv_size(size) - self.op =op - valid_fv_ops (op) - - # choose default value for init val - if init_value == None: - init_value = max_value if op == "dec" else min_value - - self.init_value = convert_val (init_value) - self.min_value = convert_val (min_value); - self.max_value = convert_val (max_value) - - if self.min_value > self.max_value : - raise CTRexPacketBuildException(-11,("max %d is lower than min %d ") % (self.max_value,self.min_value) ); - - def get_obj (self): - return CTRexVmInsFlowVar(self.name,self.size,self.op,self.init_value,self.min_value,self.max_value); - - def get_var_name(self): - return [self.name] - - -class CTRexVmDescFixIpv4(CTRexVmDescBase): - def __init__(self, offset): - super(CTRexVmDescFixIpv4, self).__init__() - self.offset = offset; # could be a name of offset - - def get_obj (self): - return CTRexVmInsFixIpv4(self.offset); - - def compile(self,parent): - if type(self.offset)==str: - self.offset = parent._pkt_layer_offset(self.offset); - -class CTRexVmDescWrFlowVar(CTRexVmDescBase): - def __init__(self, fv_name, pkt_offset, offset_fixup=0, add_val=0, is_big=True): - super(CTRexVmDescWrFlowVar, self).__init__() - self.name =fv_name - assert type(fv_name)==str, 'type of fv_name is not str' - self.offset_fixup =offset_fixup - assert type(offset_fixup)==int, 'type of offset_fixup is not int' - self.pkt_offset =pkt_offset - self.add_val =add_val - assert type(add_val)==int,'type of add_val is not int' - self.is_big =is_big; - assert type(is_big)==bool,'type of is_big_endian is not bool' - - def get_var_ref (self): - return self.name - - def get_obj (self): - return CTRexVmInsWrFlowVar(self.name,self.pkt_offset+self.offset_fixup,self.add_val,self.is_big) - - def compile(self,parent): - if type(self.pkt_offset)==str: - t=parent._name_to_offset(self.pkt_offset) - self.pkt_offset = t[0] - - -class CTRexVmDescTrimPktSize(CTRexVmDescBase): - def __init__(self,fv_name): - super(CTRexVmDescTrimPktSize, self).__init__() - self.name = fv_name - assert type(fv_name)==str, 'type of fv_name is not str' - - def get_var_ref (self): - return self.name - - def get_obj (self): - return CTRexVmInsTrimPktSize(self.name) - - - -class CTRexVmDescTupleGen(CTRexVmDescBase): - def __init__(self,name, ip_min="0.0.0.1", ip_max="0.0.0.10", port_min=1025, port_max=65535, limit_flows=100000, flags=0): - super(CTRexVmDescTupleGen, self).__init__() - self.name = name - assert type(name)==str, 'type of fv_name is not str' - self.ip_min = convert_val(ip_min); - self.ip_max = convert_val(ip_max); - self.port_min = port_min; - check_for_int (port_min) - self.port_max = port_max; - check_for_int(port_max) - self.limit_flows = limit_flows; - check_for_int(limit_flows) - self.flags =flags; - check_for_int(flags) - - def get_var_name(self): - return [self.name+".ip",self.name+".port"] - - def get_obj (self): - return CTRexVmInsTupleGen(self.name, self.ip_min, self.ip_max, self.port_min, self.port_max, self.limit_flows, self.flags); - - -################################################################################################ - - -class CScapyTRexPktBuilder(CTrexPktBuilderInterface): - - """ - This class defines the TRex API of building a packet using dpkt package. - Using this class the user can also define how TRex will handle the packet by specifying the VM setting. - """ - def __init__(self, pkt = None, vm = None): - """ - Instantiate a CTRexPktBuilder object - - :parameters: - None - - """ - super(CScapyTRexPktBuilder, self).__init__() - - self.pkt = None - self.vm_scripts = [] # list of high level instructions - self.vm_low_level = None - self.metadata="" - - - # process packet - if pkt != None: - if not isinstance(pkt, Packet): - raise CTRexPacketBuildException(-14, "bad value for variable pkt") - self.set_packet(pkt) - - # process VM - if vm != None: - if not isinstance(vm, (CTRexScRaw, list)): - raise CTRexPacketBuildException(-14, "bad value for variable vm") - - self.add_command(vm if isinstance(vm, CTRexScRaw) else CTRexScRaw(vm)) - - - def dump_vm_data_as_yaml(self): - print yaml.dump(self.get_vm_data(), default_flow_style=False) - - def get_vm_data(self): - """ - Dumps the instructions - - :parameters: - None - - :return: - + json object of instructions - - :raises: - + :exc:`AssertionError`, in case VM is not compiled (is None). - """ - - assert self.vm_low_level is not None, 'vm_low_level is None, please use compile()' - - return self.vm_low_level.get_json() - - def dump_pkt(self, encode = True): - """ - Dumps the packet as a decimal array of bytes (each item x gets value between 0-255) - - :parameters: - encode : bool - Encode using base64. (disable for debug) - - Default: **True** - - :return: - + packet representation as array of bytes - - :raises: - + :exc:`AssertionError`, in case packet is empty. - - """ - - assert self.pkt, 'empty packet' - - return {'binary': base64.b64encode(str(self.pkt)) if encode else str(self.pkt), - 'meta': self.metadata} - - def dump_pkt_to_pcap(self, file_path): - wrpcap(file_path, self.pkt) - - def add_command (self, script): - self.vm_scripts.append(script.clone()); - - def dump_scripts (self): - self.vm_low_level.dump_as_yaml() - - def set_packet (self, pkt): - """ - Scapy packet Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/IP()/"A"*10 - """ - self.pkt = pkt; - - - def compile (self): - self.vm_low_level=CTRexVmEngine() - assert self.pkt, 'empty packet' - self.pkt.build(); - - - for sc in self.vm_scripts: - if isinstance(sc, CTRexScRaw): - self._compile_raw(sc) - - #for obj in self.vm_scripts: - # # tuple gen script - # if isinstance(obj, CTRexScIpv4TupleGen) - # self._add_tuple_gen(tuple_gen) - - #################################################### - # private - - def _compile_raw (self,obj): - - # make sure we have varibles once - vars={}; - - # add it add var to dit - for desc in obj.commands: - var_names = desc.get_var_name() - - if var_names : - for var_name in var_names: - if vars.has_key(var_name): - raise CTRexPacketBuildException(-11,("variable %s define twice ") % (var_name) ); - else: - vars[var_name]=1 - - # check that all write exits - for desc in obj.commands: - var_name = desc.get_var_ref() - if var_name : - if not vars.has_key(var_name): - raise CTRexPacketBuildException(-11,("variable %s does not exists ") % (var_name) ); - desc.compile(self); - - for desc in obj.commands: - self.vm_low_level.add_ins(desc.get_obj()); - - - def _pkt_layer_offset (self,layer_name): - assert self.pkt != None, 'empty packet' - p_utl=CTRexScapyPktUtl(self.pkt); - return p_utl.get_layer_offet_by_str(layer_name) - - def _name_to_offset(self,field_name): - assert self.pkt != None, 'empty packet' - p_utl=CTRexScapyPktUtl(self.pkt); - return p_utl.get_field_offet_by_str(field_name) - - def _add_tuple_gen(self,tuple_gen): - - pass; - - - - diff --git a/scripts/automation/trex_control_plane/client_utils/text_tables.py b/scripts/automation/trex_control_plane/client_utils/text_tables.py index d8928da8..6b52a4a9 100644 --- a/scripts/automation/trex_control_plane/client_utils/text_tables.py +++ b/scripts/automation/trex_control_plane/client_utils/text_tables.py @@ -1,7 +1,5 @@ - -import external_packages from texttable import Texttable -from common.text_opts import format_text +from trex_control_plane.common.text_opts import format_text class TRexTextTable(Texttable): diff --git a/scripts/automation/trex_control_plane/client_utils/yaml_utils.py b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py index 825d6fc9..776a51a7 100755 --- a/scripts/automation/trex_control_plane/client_utils/yaml_utils.py +++ b/scripts/automation/trex_control_plane/client_utils/yaml_utils.py @@ -16,7 +16,6 @@ limitations under the License. """ import traceback import sys -import external_packages import yaml -- cgit 1.2.3-korg