From cdcc62972d42f009f55e6aeb2ca5c60c3acd75eb Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Wed, 26 Aug 2015 14:27:43 +0300 Subject: added dpkt package, initial stateless client implementation --- .../trex_control_plane/client_utils/jsonrpc_client.py | 12 ++++++------ .../trex_control_plane/client_utils/outer_packages.py | 4 +++- .../trex_control_plane/client_utils/packet_builder.py | 11 +++++++++++ 3 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 scripts/automation/trex_control_plane/client_utils/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 index 1631c494..89ac9127 100644 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -34,16 +34,16 @@ class JsonRpcClient(object): print "[verbose] " + msg - def create_jsonrpc_v2 (self, method_name, params = {}, id = None): + def create_jsonrpc_v2 (self, method_name, params = {}): msg = {} msg["jsonrpc"] = "2.0" msg["method"] = method_name msg["params"] = params - msg["id"] = id + msg["id"] = self.id_gen.next() - return json.dumps(msg) + return id, json.dumps(msg) def invoke_rpc_method (self, method_name, params = {}, block = False): rc, msg = self._invoke_rpc_method(method_name, params, block) @@ -56,8 +56,7 @@ class JsonRpcClient(object): if not self.connected: return False, "Not connected to server" - id = self.id_gen.next() - msg = self.create_jsonrpc_v2(method_name, params, id = id) + id, msg = self.create_jsonrpc_v2(method_name, params) self.verbose_msg("Sending Request To Server:\n\n" + self.pretty_json(msg) + "\n") @@ -180,7 +179,8 @@ class JsonRpcClient(object): def __del__(self): print "Shutting down RPC client\n" - self.context.destroy(linger=0) + if hasattr(self, "context"): + self.context.destroy(linger=0) if __name__ == "__main__": pass diff --git a/scripts/automation/trex_control_plane/client_utils/outer_packages.py b/scripts/automation/trex_control_plane/client_utils/outer_packages.py index 53cce991..81672c91 100644 --- a/scripts/automation/trex_control_plane/client_utils/outer_packages.py +++ b/scripts/automation/trex_control_plane/client_utils/outer_packages.py @@ -9,7 +9,9 @@ ROOT_PATH = os.path.abspath(os.path.join(CURRENT_PATH, os.pardir)) PATH_TO_PYTHON_LIB = os.path.abspath(os.path.join(ROOT_PATH, os.pardir, os.pardir, os.pardir, 'external_libs', 'python')) -CLIENT_UTILS_MODULES = ['zmq'] +CLIENT_UTILS_MODULES = ['zmq', + 'dpkt-1.8.6.2' + ] def import_client_utils_modules(): diff --git a/scripts/automation/trex_control_plane/client_utils/packet_builder.py b/scripts/automation/trex_control_plane/client_utils/packet_builder.py new file mode 100644 index 00000000..c33444a7 --- /dev/null +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -0,0 +1,11 @@ +#!/router/bin/python + + +import outer_packages +import dpkt + +class CTRexPktBuilder(object): + """docstring for CTRexPktBuilder""" + def __init__(self, arg): + super(CTRexPktBuilder, self).__init__() + self.arg = arg \ No newline at end of file -- cgit 1.2.3-korg From 68df86e2005dc4693b1270a3e663e2450f81fa93 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 27 Aug 2015 19:18:16 +0300 Subject: progress in packet builder module --- .../client/trex_stateless_client.py | 7 - .../client_utils/outer_packages.py | 2 +- .../client_utils/packet_builder.py | 150 ++++++++++++++++++++- .../trex_control_plane/common/trex_exceptions.py | 14 +- .../examples/interactive_stateless.py | 6 +- .../trex_control_plane/server/trex_server.py | 2 +- 6 files changed, 160 insertions(+), 21 deletions(-) (limited to 'scripts/automation/trex_control_plane/client_utils') diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 670eda1d..5513f420 100644 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -47,12 +47,5 @@ class CTRexStatelessClient(object): - -class CTRexVM(object): - """docstring for CTRexVM""" - def __init__(self, arg): - super(CTRexVM, self).__init__() - self.arg = arg - if __name__ == "__main__": pass diff --git a/scripts/automation/trex_control_plane/client_utils/outer_packages.py b/scripts/automation/trex_control_plane/client_utils/outer_packages.py index 3ba73ec1..a6c9a2eb 100644 --- a/scripts/automation/trex_control_plane/client_utils/outer_packages.py +++ b/scripts/automation/trex_control_plane/client_utils/outer_packages.py @@ -9,7 +9,7 @@ ROOT_PATH = os.path.abspath(os.path.join(CURRENT_PATH, os.pardir)) PATH_TO_PYTHON_LIB = os.path.abspath(os.path.join(ROOT_PATH, os.pardir, os.pardir, 'external_libs')) CLIENT_UTILS_MODULES = ['zmq', - 'dpkt-1.8.6.2' + 'dpkt-1.8.6' ] diff --git a/scripts/automation/trex_control_plane/client_utils/packet_builder.py b/scripts/automation/trex_control_plane/client_utils/packet_builder.py index c33444a7..172dcda2 100644 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -3,9 +3,155 @@ import outer_packages import dpkt +import socket +import binascii + class CTRexPktBuilder(object): """docstring for CTRexPktBuilder""" - def __init__(self, arg): + def __init__(self): super(CTRexPktBuilder, self).__init__() - self.arg = arg \ No newline at end of file + self.packet = None + self.vm = CTRexPktBuilder.CTRexVM(self.packet) + + def add_l2_header(self): + pass + + def add_l3_header(self): + pass + + def add_pkt_payload(self): + pass + + # VM access methods + def set_vm_ip_range(self, ip_start, ip_end, ip_type="ipv4"): + pass + + def set_vm_range_type(self, ip_type): + pass + + def set_vm_core_mask(self, ip_type): + pass + + def get_vm_data(self): + pass + + def dump_pkt(self): + pkt_in_hex = binascii.hexlify(str(self.packet)) + return [pkt_in_hex[i:i+2] for i in range(0, len(pkt_in_hex), 2)] + + # ----- useful shortcut methods ----- # + def gen_dns_packet(self): + pass + + # ----- internal methods ----- # + @staticmethod + def _is_valid_ip_addr(ip_addr, ip_type): + if ip_type == "ipv4": + try: + socket.inet_pton(socket.AF_INET, ip_addr) + except AttributeError: # no inet_pton here, sorry + try: + socket.inet_aton(ip_addr) + except socket.error: + return False + return ip_addr.count('.') == 3 + except socket.error: # not a valid address + return False + return True + elif ip_type == "ipv6": + try: + socket.inet_pton(socket.AF_INET6, ip_addr) + except socket.error: # not a valid address + return False + return True + else: + raise CTRexPktBuilder.IPAddressError() + + # ------ private classes ------ + class CTRexVM(object): + """docstring for CTRexVM""" + def __init__(self, packet): + super(CTRexPktBuilder.CTRexVM, self).__init__() + self.packet = packet + self.vm_variables = {} + + def add_vm_variable(self, name): + if name not in self.vm_variables.keys(): + self.vm_variables[name] = self.CTRexVMVariable(name) + + def fix_checksum_ipv4(self): + pass + + def flow_man_simple(self): + pass + + def write_to_pkt(self): + pass + + def dump(self): + return [var.dump() + for var in self.vm_variables + if var.is_validty()] + + class CTRexVMVariable(object): + VALID_SIZE = [1, 2, 4, 8] + VALID_TYPE = ["inc", "dec", "random"] + VALID_CORE_MASK = ["split", "none"] + + def __init__(self, name): + super(CTRexPktBuilder.CTRexVM.CTRexVMVariable, self).__init__() + self.name = name + self.size = 4 + self.big_endian = True + self.type = "inc" + self.core_mask = "none" + self.init_addr = "10.0.0.1" + self.min_addr = str(self.init_addr) + self.max_addr = str(self.init_addr) + + def is_valid(self): + if self.size not in self.VALID_SIZE: + return False + if self.type not in self.VALID_TYPE: + return False + if self.core_mask not in self.VALID_CORE_MASK: + return False + return True + + def dump(self): + return {"name" : self.name, + "Size" : self.size, + "big_endian" : self.big_endian, + "type" : self.type, + "core_mask" : self.core_mask, + "init_addr" : self.init_addr, + "min_addr" : self.min_addr, + "max_addr" : self.max_addr} + + class CPacketBuildException(Exception): + """ + This is the general Packet 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 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 of IP addressing has been provided.' + self.message = message or self._default_message + super(CTRexPktBuilder.IPAddressError, self).__init__(-11, self.message) + + +if __name__ == "__main__": + pass diff --git a/scripts/automation/trex_control_plane/common/trex_exceptions.py b/scripts/automation/trex_control_plane/common/trex_exceptions.py index 1353fd00..a2a64e19 100755 --- a/scripts/automation/trex_control_plane/common/trex_exceptions.py +++ b/scripts/automation/trex_control_plane/common/trex_exceptions.py @@ -17,13 +17,13 @@ class RPCError(Exception): self.data = remote_data self.args = (code, self.msg, remote_data) - def __str__(self): - return self.__repr__() - def __repr__(self): - if self.args[2] is not None: - return u"[errcode:%r] %r. Extended data: %r" % (self.args[0], self.args[1], self.args[2]) - else: - return u"[errcode:%r] %r" % (self.args[0], self.args[1]) + def __str__(self): + return self.__repr__() + def __repr__(self): + if self.args[2] is not None: + return u"[errcode:%r] %r. Extended data: %r" % (self.args[0], self.args[1], self.args[2]) + else: + return u"[errcode:%r] %r" % (self.args[0], self.args[1]) class TRexException(RPCError): """ diff --git a/scripts/automation/trex_control_plane/examples/interactive_stateless.py b/scripts/automation/trex_control_plane/examples/interactive_stateless.py index 016888d2..7c25b4ef 100644 --- a/scripts/automation/trex_control_plane/examples/interactive_stateless.py +++ b/scripts/automation/trex_control_plane/examples/interactive_stateless.py @@ -93,7 +93,7 @@ class InteractiveStatelessTRex(cmd.Cmd): print termstyle.magenta(inst) if __name__ == "__main__": - parser = ArgumentParser(description=termstyle.cyan('Run T-Rex client API demos and scenarios.'), + parser = ArgumentParser(description=termstyle.cyan('Run TRex client stateless API demos and scenarios.'), usage="client_interactive_example [options]") parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0 \t (C) Cisco Systems Inc.\n') @@ -101,8 +101,8 @@ if __name__ == "__main__": parser.add_argument("-t", "--trex-host", required = True, dest="trex_host", action="store", help="Specify the hostname or ip to connect with T-Rex server.", metavar="HOST" ) - parser.add_argument("-p", "--trex-port", type=int, default = 8090, metavar="PORT", dest="trex_port", - help="Select port on which the T-Rex server listens. Default port is 8090.", action="store") + parser.add_argument("-p", "--trex-port", type=int, default = 5050, metavar="PORT", dest="trex_port", + help="Select port on which the T-Rex server listens. Default port is 5050.", action="store") # parser.add_argument("-m", "--maxhist", type=int, default = 100, metavar="SIZE", dest="hist_size", # help="Specify maximum history size saved at client side. Default size is 100.", action="store") parser.add_argument("--virtual", dest="virtual", diff --git a/scripts/automation/trex_control_plane/server/trex_server.py b/scripts/automation/trex_control_plane/server/trex_server.py index 992a1d5f..35b2669a 100755 --- a/scripts/automation/trex_control_plane/server/trex_server.py +++ b/scripts/automation/trex_control_plane/server/trex_server.py @@ -53,7 +53,7 @@ class CTRexServer(object): the port number on which trex's zmq module will interact with daemon server default value: 4500 - Instatiate a T-Rex client object, and connecting it to listening daemon-server + Instantiate a T-Rex client object, and connecting it to listening daemon-server """ self.TREX_PATH = os.path.abspath(os.path.dirname(trex_path+'/')) self.trex_files_path = os.path.abspath(os.path.dirname(trex_files_path+'/')) -- cgit 1.2.3-korg From 760710869405dbc3b5dadd6ce06015c72ea2ca44 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sun, 30 Aug 2015 13:59:07 +0300 Subject: another stateless progress --- .../client_utils/packet_builder.py | 61 +++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) (limited to 'scripts/automation/trex_control_plane/client_utils') diff --git a/scripts/automation/trex_control_plane/client_utils/packet_builder.py b/scripts/automation/trex_control_plane/client_utils/packet_builder.py index 172dcda2..0d31f28e 100644 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -79,6 +79,13 @@ class CTRexPktBuilder(object): def add_vm_variable(self, name): if name not in self.vm_variables.keys(): self.vm_variables[name] = self.CTRexVMVariable(name) + else: + raise CTRexPktBuilder.VMVarNameExistsError(name) + + def set_vm_var_field(self, var_name, field_name, val): + pass + return self.vm_variables[var_name].set_field(field_name, val) + def fix_checksum_ipv4(self): pass @@ -91,8 +98,7 @@ class CTRexPktBuilder(object): def dump(self): return [var.dump() - for var in self.vm_variables - if var.is_validty()] + for var in self.vm_variables] class CTRexVMVariable(object): VALID_SIZE = [1, 2, 4, 8] @@ -110,6 +116,25 @@ class CTRexPktBuilder(object): self.min_addr = str(self.init_addr) self.max_addr = str(self.init_addr) + def set_field(self, field_name, val): + if field_name == "size": + if type(val) != int: + raise CTRexPktBuilder.VMVarFieldTypeError("size", int) + elif val not in self.VALID_SIZE: + raise CTRexPktBuilder.VMVarValueError("size", self.VALID_SIZE) + elif field_name == "type": + if type(val) != str: + raise CTRexPktBuilder.VMVarFieldTypeError("type", str) + elif val not in self.VALID_TYPE: + raise CTRexPktBuilder.VMVarValueError("type", self.VALID_TYPE) + elif field_name == "core_mask": + if type(val) != str: + raise CTRexPktBuilder.VMVarFieldTypeError("core_mask", str) + elif val not in self.VALID_TYPE: + raise CTRexPktBuilder.VMVarValueError("core_mask", self.VALID_CORE_MASK) + # update field value on success + setattr(self, field_name, val) + def is_valid(self): if self.size not in self.VALID_SIZE: return False @@ -152,6 +177,38 @@ class CTRexPktBuilder(object): self.message = message or self._default_message super(CTRexPktBuilder.IPAddressError, self).__init__(-11, self.message) + class VMVarNameExistsError(CPacketBuildException): + """ + This exception is used to indicate an error on the IP addressing part of the packet. + """ + 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 VMVarFieldTypeError(CPacketBuildException): + """ + This exception is used to indicate an error on the IP addressing part of the packet. + """ + 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.VMVarNameExistsError, self).__init__(-31, self.message) + + class VMVarValueError(CPacketBuildException): + """ + This exception is used to indicate an error on the IP addressing part of the packet. + """ + 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.VMVarValueError, self).__init__(-32, self.message) + if __name__ == "__main__": pass -- cgit 1.2.3-korg From ef520c7e3a18aeefe02ba0d3e6b16fafde3c1b91 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 10 Sep 2015 13:39:18 +0300 Subject: Major progress in Packet building module and VM instruction sets. added dot1q into dpkt lib First abstractino of HLTAPI layer --- .../trex_control_plane/client/trex_client.py | 2 - .../trex_control_plane/client/trex_hltapi.py | 18 + .../trex_control_plane/client/trex_root_path.py | 15 + .../client_utils/packet_builder.py | 566 ++++++++-- scripts/external_libs/dpkt-1.8.6/dpkt/dot1q.py | 1110 ++++++++++++++++++++ 5 files changed, 1635 insertions(+), 76 deletions(-) create mode 100644 scripts/automation/trex_control_plane/client/trex_hltapi.py create mode 100644 scripts/automation/trex_control_plane/client/trex_root_path.py create mode 100644 scripts/external_libs/dpkt-1.8.6/dpkt/dot1q.py (limited to 'scripts/automation/trex_control_plane/client_utils') diff --git a/scripts/automation/trex_control_plane/client/trex_client.py b/scripts/automation/trex_control_plane/client/trex_client.py index 0fbb4719..56775766 100755 --- a/scripts/automation/trex_control_plane/client/trex_client.py +++ b/scripts/automation/trex_control_plane/client/trex_client.py @@ -1062,5 +1062,3 @@ class CTRexResult(object): if __name__ == "__main__": pass - - diff --git a/scripts/automation/trex_control_plane/client/trex_hltapi.py b/scripts/automation/trex_control_plane/client/trex_hltapi.py new file mode 100644 index 00000000..46c283f8 --- /dev/null +++ b/scripts/automation/trex_control_plane/client/trex_hltapi.py @@ -0,0 +1,18 @@ +#!/router/bin/python + +import trex_root_path +from client_utils.packet_builder import CTRexPktBuilder + +print "done!" + +class CTRexHltApi(object): + + def __init__(self): + pass + + def config_traffic(self): + pass + +if __name__ == "__main__": + pass + diff --git a/scripts/automation/trex_control_plane/client/trex_root_path.py b/scripts/automation/trex_control_plane/client/trex_root_path.py new file mode 100644 index 00000000..de4ec03b --- /dev/null +++ b/scripts/automation/trex_control_plane/client/trex_root_path.py @@ -0,0 +1,15 @@ +#!/router/bin/python + +import os +import sys + +def add_root_to_path (): + """adds trex_control_plane root dir to script path, up to `depth` parent dirs""" + root_dirname = 'trex_control_plane' + file_path = os.path.dirname(os.path.realpath(__file__)) + + components = file_path.split(os.sep) + sys.path.append( str.join(os.sep, components[:components.index(root_dirname)+1]) ) + return + +add_root_to_path() diff --git a/scripts/automation/trex_control_plane/client_utils/packet_builder.py b/scripts/automation/trex_control_plane/client_utils/packet_builder.py index 0d31f28e..abe98dbb 100644 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -5,23 +5,207 @@ import outer_packages import dpkt import socket import binascii +import copy +import random +import string +import struct +import re class CTRexPktBuilder(object): - """docstring for CTRexPktBuilder""" - def __init__(self): + """ + 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.vm = CTRexPktBuilder.CTRexVM(self.packet) + self._packet = None + self._pkt_by_hdr = {} + self._pkt_top_layer = None + self._max_pkt_size = max_pkt_size + self.payload_generator = CTRexPktBuilder.CTRexPayloadGen(self._packet, self._max_pkt_size) + self.vm = CTRexPktBuilder.CTRexVM() + + def add_pkt_layer(self, layer_name, pkt_layer): + """ + This method adds additional header to the already existing packet - def add_l2_header(self): - pass + :parameters: + layer_name: str + a string representing the name of the layer. + Example: "l2", "l4_tcp", etc. - def add_l3_header(self): - pass + pkt_layer : dpkt.Packet obj + a dpkt object, generally from higher layer, that will be added on top of existing layer. - def add_pkt_payload(self): - pass + """ + 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"): + 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): + self.set_ip_layer_addr(self, layer_name, attr, ip_addr, ip_type="ipv6") + + def set_eth_layer_addr(self, layer_name, attr, mac_addr): + 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): + """ + 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. + + :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 obejct that is not dpkt.Packet is not allowed using " + "set_layer_attr method.\nUse set_payload method instead.") + if hasattr(layer, attr): + 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_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 get_packet(self, get_ptr=False): + """ + This method enables the user to change a value of a previously defined packet layer. + + :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. + + """ + if get_ptr: + self._pkt_by_hdr = {} + self._pkt_top_layer = None + return self._packet + + else: + return copy.copy(self._packet) # VM access methods def set_vm_ip_range(self, ip_start, ip_end, ip_type="ipv4"): @@ -37,7 +221,7 @@ class CTRexPktBuilder(object): pass def dump_pkt(self): - pkt_in_hex = binascii.hexlify(str(self.packet)) + pkt_in_hex = binascii.hexlify(str(self._packet)) return [pkt_in_hex[i:i+2] for i in range(0, len(pkt_in_hex), 2)] # ----- useful shortcut methods ----- # @@ -45,118 +229,334 @@ class CTRexPktBuilder(object): 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 + + @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 _is_valid_ip_addr(ip_addr, ip_type): + 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: - socket.inet_pton(socket.AF_INET, ip_addr) + return socket.inet_pton(socket.AF_INET, ip_addr) except AttributeError: # no inet_pton here, sorry - try: - socket.inet_aton(ip_addr) - except socket.error: - return False - return ip_addr.count('.') == 3 + # try: + return socket.inet_aton(ip_addr) + # except socket.error: + # return False + # return ip_addr.count('.') == 3 except socket.error: # not a valid address - return False - return True + raise CTRexPktBuilder.IPAddressError() elif ip_type == "ipv6": try: - socket.inet_pton(socket.AF_INET6, ip_addr) + return socket.inet_pton(socket.AF_INET6, ip_addr) except socket.error: # not a valid address - return False - return True + raise CTRexPktBuilder.IPAddressError() else: raise CTRexPktBuilder.IPAddressError() - # ------ private classes ------ + # ------ 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): - """docstring for CTRexVM""" - def __init__(self, packet): + """ + 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. + """ + def __init__(self): + """ + Instantiate a CTRexVM object + + :parameters: + None + """ super(CTRexPktBuilder.CTRexVM, self).__init__() - self.packet = packet self.vm_variables = {} - def add_vm_variable(self, name): + def set_vm_var_field(self, var_name, field_name, val): + """ + 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. + + """ + return self.vm_variables[var_name].set_field(field_name, val) + + def add_flow_man_simple(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.keys(): self.vm_variables[name] = self.CTRexVMVariable(name) + # try configuring VM var attributes + for (field, value) in kwargs.items(): + self.vm_variables[name].set_field(field, value) else: raise CTRexPktBuilder.VMVarNameExistsError(name) - def set_vm_var_field(self, var_name, field_name, val): - pass - return self.vm_variables[var_name].set_field(field_name, val) + 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. - def fix_checksum_ipv4(self): - pass + :return: + list holds variables data of VM - def flow_man_simple(self): - pass - - def write_to_pkt(self): - pass + """ + assert isinstance(flow_obj, CTRexPktBuilder.CTRexVM.CTRexVMVariable) + 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 dump(self): + """ + dumps a VM variables (instructions) into an list data structure. + + :parameters: + None + + :return: + list holds variables data of VM + + """ return [var.dump() - for var in self.vm_variables] + for key, var in self.vm_variables.items()] class CTRexVMVariable(object): + """ + This class defines a single VM variable to be used as part of CTRexVar object. + """ VALID_SIZE = [1, 2, 4, 8] - VALID_TYPE = ["inc", "dec", "random"] - VALID_CORE_MASK = ["split", "none"] + 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.CTRexVMVariable, self).__init__() self.name = name self.size = 4 self.big_endian = True - self.type = "inc" - self.core_mask = "none" - self.init_addr = "10.0.0.1" - self.min_addr = str(self.init_addr) - self.max_addr = str(self.init_addr) + 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): - if field_name == "size": + """ + 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.VMVarNameError(field_name) + elif field_name == "size": if type(val) != int: raise CTRexPktBuilder.VMVarFieldTypeError("size", int) elif val not in self.VALID_SIZE: raise CTRexPktBuilder.VMVarValueError("size", self.VALID_SIZE) - elif field_name == "type": - if type(val) != str: - raise CTRexPktBuilder.VMVarFieldTypeError("type", str) - elif val not in self.VALID_TYPE: - raise CTRexPktBuilder.VMVarValueError("type", self.VALID_TYPE) - elif field_name == "core_mask": + elif field_name == "init_value": + if type(val) != int: + raise CTRexPktBuilder.VMVarFieldTypeError("init_value", int) + elif field_name == "operation": if type(val) != str: - raise CTRexPktBuilder.VMVarFieldTypeError("core_mask", str) - elif val not in self.VALID_TYPE: - raise CTRexPktBuilder.VMVarValueError("core_mask", self.VALID_CORE_MASK) + raise CTRexPktBuilder.VMVarFieldTypeError("operation", str) + elif val not in self.VALID_OPERATION: + raise CTRexPktBuilder.VMVarValueError("operation", self.VALID_OPERATION) + elif field_name == "split_by_core": + val = bool(val) # update field value on success setattr(self, field_name, val) def is_valid(self): if self.size not in self.VALID_SIZE: return False - if self.type not in self.VALID_TYPE: - return False - if self.core_mask not in self.VALID_CORE_MASK: + if self.type not in self.VALID_OPERATION: return False return True def dump(self): - return {"name" : self.name, - "Size" : self.size, - "big_endian" : self.big_endian, - "type" : self.type, - "core_mask" : self.core_mask, - "init_addr" : self.init_addr, - "min_addr" : self.min_addr, - "max_addr" : self.max_addr} + """ + dumps a variable fields in a dictionary data structure. + + :parameters: + None + + :return: + dictionary holds variable data of VM variable + + """ + return {"ins_name": "flow_man_simple", # VM variable dump always refers to manipulate instruction. + "flow_variable_name": self.name, + "object_size": self.size, + # "big_endian": self.big_endian, + "Operation": 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 CPacketBuildException(Exception): """ - This is the general Packet error exception class. + This is the general Packet Building error exception class. """ def __init__(self, code, message): self.code = code @@ -173,22 +573,40 @@ class CTRexPktBuilder(object): 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 of IP addressing has been provided.' + 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__(-11, self.message) + class VMVarNameExistsError(CPacketBuildException): """ - This exception is used to indicate an error on the IP addressing part of the packet. + 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._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 VMVarNameError(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.VMVarNameError, self).__init__(-22, self.message) + class VMVarFieldTypeError(CPacketBuildException): """ - This exception is used to indicate an error on the IP addressing part of the packet. + 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}, \ @@ -196,11 +614,11 @@ class CTRexPktBuilder(object): field_type=type(name).__name__, allowed_type=ok_type.__name__) self.message = message or self._default_message - super(CTRexPktBuilder.VMVarNameExistsError, self).__init__(-31, self.message) + super(CTRexPktBuilder.VMVarFieldTypeError, self).__init__(-31, self.message) class VMVarValueError(CPacketBuildException): """ - This exception is used to indicate an error on the IP addressing part of the packet. + 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 \ diff --git a/scripts/external_libs/dpkt-1.8.6/dpkt/dot1q.py b/scripts/external_libs/dpkt-1.8.6/dpkt/dot1q.py new file mode 100644 index 00000000..ac6eb185 --- /dev/null +++ b/scripts/external_libs/dpkt-1.8.6/dpkt/dot1q.py @@ -0,0 +1,1110 @@ + + + + + + + + + + + + + hexcap/dot1q.py at master · hexcap/hexcap + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content + + + + + + + + + + + + + + +
+ +
+
+ + +
+
+
+ +
+ +
    + +
  • +
    + +
    + + + + Watch + + + + +
    + +
    +
    +
    +
  • + +
  • + +
    + +
    + + +
    +
    + + +
    + +
  • + +
  • + + + Fork + + + + +
  • + +
+ +

+ + /hexcap + + + + + +

+ +
+
+
+ +
+
+
+ + + +
+ +
+

HTTPS clone URL

+
+ + + + +
+
+ + +
+

SSH clone URL

+
+ + + + +
+
+ + +
+

Subversion checkout URL

+
+ + + + +
+
+ + + +
You can clone with +
,
, or
. + + + +
+ + + Clone in Desktop + + + + + Download ZIP + +
+
+
+ + + + + + + +
+ +
+ + Branch: + master + + + +
+ +
+ + + + +
+ + +
+ + +
+
+ @smutt + smutt + + +
+ + + +
+ +
+
+
+ +
+ Raw + Blame + History +
+ + + + + +
+ +
+
+ +
+ +
+ executable file + + 93 lines (79 sloc) + + 3.071 kB +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
"""IEEE 802.1q"""
+
import struct
import dpkt
+
# Ethernet payload types - http://standards.ieee.org/regauth/ethertype
ETH_TYPE_PUP = 0x0200 # PUP protocol
ETH_TYPE_IP = 0x0800 # IP protocol
ETH_TYPE_ARP = 0x0806 # address resolution protocol
ETH_TYPE_CDP = 0x2000 # Cisco Discovery Protocol
ETH_TYPE_DTP = 0x2004 # Cisco Dynamic Trunking Protocol
ETH_TYPE_REVARP = 0x8035 # reverse addr resolution protocol
ETH_TYPE_DOT1Q = 0x8100 # IEEE 802.1Q VLAN tagging
ETH_TYPE_IPX = 0x8137 # Internetwork Packet Exchange
ETH_TYPE_IP6 = 0x86DD # IPv6 protocol
ETH_TYPE_PPP = 0x880B # PPP
ETH_TYPE_MPLS = 0x8847 # MPLS
ETH_TYPE_MPLS_MCAST = 0x8848 # MPLS Multicast
ETH_TYPE_PPPoE_DISC = 0x8863 # PPP Over Ethernet Discovery Stage
ETH_TYPE_PPPoE = 0x8864 # PPP Over Ethernet Session Stage
+
class DOT1Q(dpkt.Packet):
__hdr__ = (
('x2', 'H', 0),
('type', 'H', 0)
)
_typesw = {}
+
# pcp == Priority Code Point(802.1p)
def _get_pcp(self): return self.x2 >> 13
def _set_pcp(self, pcp): self.x2 = (self.x2 & 0x1fff) | (pcp << 13)
pcp = property(_get_pcp, _set_pcp)
+
# dei == Drop Eligible Indicator(almost never actually used)
def _get_dei(self): return (self.x2 >> 12) & 1
def _set_dei(self, dei): self.x2 = (self.x2 & 61439) | (dei << 12)
dei = property(_get_dei, _set_dei)
+
# tag == vlan tag
def _get_tag(self): return self.x2 & (65535 >> 4)
def _set_tag(self, tag): self.x2 = (self.x2 & 0xfff) | tag
tag = property(_get_tag, _set_tag)
+
def set_type(cls, t, pktclass):
cls._typesw[t] = pktclass
set_type = classmethod(set_type)
+
def get_type(cls, t):
return cls._typesw[t]
get_type = classmethod(get_type)
+
def _unpack_data(self, buf):
if self.type == ETH_TYPE_MPLS or \
self.type == ETH_TYPE_MPLS_MCAST:
# XXX - skip labels (max # of labels is undefined, just use 24)
self.labels = []
for i in range(24):
entry = struct.unpack('>I', buf[i*4:i*4+4])[0]
label = ((entry & MPLS_LABEL_MASK) >> MPLS_LABEL_SHIFT, \
(entry & MPLS_QOS_MASK) >> MPLS_QOS_SHIFT, \
(entry & MPLS_TTL_MASK) >> MPLS_TTL_SHIFT)
self.labels.append(label)
if entry & MPLS_STACK_BOTTOM:
break
self.type = ETH_TYPE_IP
buf = buf[(i + 1) * 4:]
try:
self.data = self._typesw[self.type](buf)
setattr(self, self.data.__class__.__name__.lower(), self.data)
except (KeyError, dpkt.UnpackError):
self.data = buf
+
def unpack(self, buf):
dpkt.Packet.unpack(self, buf)
self._unpack_data(self.data)
+
+
# XXX - auto-load Ethernet dispatch table from ETH_TYPE_* definitions
def __load_types():
g = globals()
for k, v in g.iteritems():
if k.startswith('ETH_TYPE_'):
name = k[9:]
modname = name.lower()
try:
mod = __import__(modname, g)
except ImportError:
continue
DOT1Q.set_type(v, getattr(mod, name))
+
if not DOT1Q._typesw:
__load_types()
+ +
+ +
+ +Jump to Line + + +
+
+ +
+
+ + + +
+ +
+ + + + + + +
+ + + Something went wrong with that request. Please try again. +
+ + + + + + + + + + -- cgit 1.2.3-korg From dd99c3890d3bb7b4aab833927e85648cd6e86c85 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 16 Sep 2015 16:22:48 +0300 Subject: added many tests to the RPC server also, few tweaks for handling RPC server errors --- .../client_utils/jsonrpc_client.py | 2 +- src/gtest/rpc_test.cpp | 355 +++++++++++++++------ src/rpc-server/commands/trex_rpc_cmd_general.cpp | 161 +++++++++- src/rpc-server/commands/trex_rpc_cmd_stream.cpp | 40 +-- src/rpc-server/commands/trex_rpc_cmd_test.cpp | 30 -- src/rpc-server/commands/trex_rpc_cmds.h | 21 +- src/rpc-server/trex_rpc_cmds_table.cpp | 5 +- src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp | 8 + src/rpc-server/trex_rpc_jsonrpc_v2_parser.h | 9 + src/rpc-server/trex_rpc_req_resp_server.cpp | 27 ++ src/rpc-server/trex_rpc_req_resp_server.h | 4 +- src/stateless/trex_stateless.cpp | 30 +- src/stateless/trex_stateless_api.h | 51 ++- src/stateless/trex_stream.cpp | 11 + src/stateless/trex_stream_api.h | 12 +- 15 files changed, 565 insertions(+), 201 deletions(-) (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 index b44b1268..aff6b36e 100644 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -145,7 +145,7 @@ class JsonRpcClient(object): return self.invoke_rpc_method("get_status") def query_rpc_server(self): - return self.invoke_rpc_method("get_reg_cmds") + return self.invoke_rpc_method("get_supported_cmds") def set_verbose(self, mode): diff --git a/src/gtest/rpc_test.cpp b/src/gtest/rpc_test.cpp index 767560b0..02d88eae 100644 --- a/src/gtest/rpc_test.cpp +++ b/src/gtest/rpc_test.cpp @@ -30,33 +30,16 @@ using namespace std; class RpcTest : public testing::Test { - void take_ownership(void) { - Json::Value request; - Json::Value response; - - create_request(request, "aquire", 1 , false); - - request["params"]["user"] = "test"; - request["params"]["force"] = true; - - send_request(request, response); - - EXPECT_TRUE(response["result"] != Json::nullValue); - m_ownership_handler = response["result"].asString(); - } - - void release_ownership() { - Json::Value request; - Json::Value response; +protected: - create_request(request, "release", 1 , false); - request["params"]["handler"] = m_ownership_handler; - - send_request(request, response); - EXPECT_TRUE(response["result"] == "ACK"); + void set_verbose(bool verbose) { + m_verbose = verbose; } - + virtual void SetUp() { + + m_verbose = false; + TrexRpcServerConfig cfg = TrexRpcServerConfig(TrexRpcServerConfig::RPC_PROT_TCP, 5050); m_rpc = new TrexRpcServer(cfg); @@ -66,7 +49,6 @@ class RpcTest : public testing::Test { m_socket = zmq_socket (m_context, ZMQ_REQ); zmq_connect (m_socket, "tcp://localhost:5050"); - take_ownership(); } virtual void TearDown() { @@ -79,16 +61,13 @@ class RpcTest : public testing::Test { public: - void create_request(Json::Value &request, const string &method, int id = 1, bool ownership = false) { + void create_request(Json::Value &request, const string &method, int id = 1) { request.clear(); request["jsonrpc"] = "2.0"; request["id"] = id; request["method"] = method; - if (ownership) { - request["params"]["handler"] = m_ownership_handler; - } } void send_request(const Json::Value &request, Json::Value &response) { @@ -98,15 +77,24 @@ public: response.clear(); string request_str = writer.write(request); + + if (m_verbose) { + cout << "\n" << request_str << "\n"; + } + string ret = send_msg(request_str); + if (m_verbose) { + cout << "\n" << ret << "\n"; + } + EXPECT_TRUE(reader.parse(ret, response, false)); EXPECT_EQ(response["jsonrpc"], "2.0"); EXPECT_EQ(response["id"], request["id"]); } string send_msg(const string &msg) { - char buffer[512]; + char buffer[1024 * 20]; zmq_send (m_socket, msg.c_str(), msg.size(), 0); int len = zmq_recv(m_socket, buffer, sizeof(buffer), 0); @@ -117,10 +105,55 @@ public: TrexRpcServer *m_rpc; void *m_context; void *m_socket; + bool m_verbose; +}; + +class RpcTestOwned : public RpcTest { +public: + + void create_request(Json::Value &request, const string &method, int id = 1) { + RpcTest::create_request(request, method, id); + request["params"]["handler"] = m_ownership_handler; + } + +protected: + + virtual void SetUp() { + RpcTest::SetUp(); + take_ownership(); + } + + + void take_ownership(void) { + Json::Value request; + Json::Value response; + + RpcTest::create_request(request, "acquire", 1); + + request["params"]["user"] = "test"; + request["params"]["force"] = true; + + send_request(request, response); + + EXPECT_TRUE(response["result"] != Json::nullValue); + m_ownership_handler = response["result"].asString(); + } + + void release_ownership() { + Json::Value request; + Json::Value response; + + RpcTest::create_request(request, "release", 1); + request["params"]["handler"] = m_ownership_handler; + + send_request(request, response); + EXPECT_TRUE(response["result"] == "ACK"); + } + string m_ownership_handler; }; -TEST_F(RpcTest, basic_rpc_test) { +TEST_F(RpcTest, basic_rpc_negative_cases) { Json::Value request; Json::Value response; Json::Reader reader; @@ -179,53 +212,38 @@ TEST_F(RpcTest, test_add_command) { Json::Value response; Json::Reader reader; - string req_str; - string resp_str; - - /* simple add - missing paramters */ - req_str = "{\"jsonrpc\": \"2.0\", \"method\": \"test_add\", \"id\": 488}"; - resp_str = send_msg(req_str); + /* missing parameters */ + create_request(request, "test_add"); + send_request(request, response); - EXPECT_TRUE(reader.parse(resp_str, response, false)); EXPECT_EQ(response["jsonrpc"], "2.0"); - EXPECT_EQ(response["id"], 488); EXPECT_EQ(response["error"]["code"], -32602); - /* simple add that works */ - req_str = "{\"jsonrpc\": \"2.0\", \"method\": \"test_add\", \"params\": {\"x\": 17, \"y\": -13} , \"id\": \"itay\"}"; - resp_str = send_msg(req_str); - - EXPECT_TRUE(reader.parse(resp_str, response, false)); - EXPECT_EQ(response["jsonrpc"], "2.0"); - EXPECT_EQ(response["id"], "itay"); - EXPECT_EQ(response["result"], 4); - - /* add with bad paratemers types */ - req_str = "{\"jsonrpc\": \"2.0\", \"method\": \"test_add\", \"params\": {\"x\": \"blah\", \"y\": -13} , \"id\": 17}"; - resp_str = send_msg(req_str); + /* bad paramters */ + create_request(request, "test_add"); + request["params"]["x"] = 5; + request["params"]["y"] = "itay"; + send_request(request, response); - EXPECT_TRUE(reader.parse(resp_str, response, false)); EXPECT_EQ(response["jsonrpc"], "2.0"); - EXPECT_EQ(response["id"], 17); EXPECT_EQ(response["error"]["code"], -32602); - /* add with invalid count of parameters */ - req_str = "{\"jsonrpc\": \"2.0\", \"method\": \"test_add\", \"params\": {\"y\": -13} , \"id\": 17}"; - resp_str = send_msg(req_str); + /* simple add that works */ + create_request(request, "test_add"); + request["params"]["x"] = 5; + request["params"]["y"] = -13; + send_request(request, response); - EXPECT_TRUE(reader.parse(resp_str, response, false)); EXPECT_EQ(response["jsonrpc"], "2.0"); - EXPECT_EQ(response["id"], 17); - EXPECT_EQ(response["error"]["code"], -32602); - + EXPECT_EQ(response["result"], -8); /* big numbers */ - req_str = "{\"jsonrpc\": \"2.0\", \"method\": \"test_add\", \"params\": {\"x\": 4827371, \"y\": -39181273} , \"id\": \"itay\"}"; - resp_str = send_msg(req_str); + create_request(request, "test_add"); + request["params"]["x"] = 4827371; + request["params"]["y"] = -39181273; + send_request(request, response); - EXPECT_TRUE(reader.parse(resp_str, response, false)); EXPECT_EQ(response["jsonrpc"], "2.0"); - EXPECT_EQ(response["id"], "itay"); EXPECT_EQ(response["result"], -34353902); } @@ -281,12 +299,174 @@ TEST_F(RpcTest, batch_rpc_test) { return; } -TEST_F(RpcTest, add_stream) { +/* ping command */ +TEST_F(RpcTest, ping) { + Json::Value request; + Json::Value response; + Json::Reader reader; + + create_request(request, "ping"); + send_request(request, response); + EXPECT_TRUE(response["result"] == "ACK"); +} + +static bool +find_member_in_array(const Json::Value &array, const string &member) { + for (auto x : array) { + if (x == member) { + return true; + } + } + + return false; +} + +/* get registered commands */ +TEST_F(RpcTest, get_supported_cmds) { + Json::Value request; + Json::Value response; + Json::Reader reader; + + create_request(request, "get_supported_cmds"); + send_request(request, response); + EXPECT_TRUE(response["result"].size() > 0); + + EXPECT_TRUE(find_member_in_array(response["result"], "ping")); + EXPECT_TRUE(find_member_in_array(response["result"], "get_supported_cmds")); +} + +/* get version */ +TEST_F(RpcTest, get_version) { Json::Value request; Json::Value response; Json::Reader reader; - create_request(request, "get_stream", 1, true); + create_request(request, "get_version"); + send_request(request, response); + + EXPECT_TRUE(response["result"] != Json::nullValue); + EXPECT_TRUE(response["result"]["built_by"] == "MOCK"); + EXPECT_TRUE(response["result"]["version"] == "v0.0"); +} + +/* get system info */ +TEST_F(RpcTest, get_system_info) { + Json::Value request; + Json::Value response; + Json::Reader reader; + + create_request(request, "get_system_info"); + send_request(request, response); + + EXPECT_TRUE(response["result"] != Json::nullValue); + EXPECT_TRUE(response["result"]["core_type"].isString()); + EXPECT_TRUE(response["result"]["hostname"].isString()); + EXPECT_TRUE(response["result"]["uptime"].isString()); + EXPECT_TRUE(response["result"]["dp_core_count"] > 0); + EXPECT_TRUE(response["result"]["port_count"] > 0); + + EXPECT_TRUE(response["result"]["ports"].isArray()); + + const Json::Value &ports = response["result"]["ports"]; + + + for (int i = 0; i < ports.size(); i++) { + EXPECT_TRUE(ports[i]["index"] == i); + EXPECT_TRUE(ports[i]["driver"].isString()); + EXPECT_TRUE(ports[i]["speed"].isString()); + } +} + +/* get owner, acquire and release */ +TEST_F(RpcTest, get_owner_acquire_release) { + Json::Value request; + Json::Value response; + Json::Reader reader; + + /* no user before acquring */ + create_request(request, "get_owner"); + send_request(request, response); + EXPECT_TRUE(response["result"] != Json::nullValue); + + EXPECT_TRUE(response["result"]["owner"] == "none"); + + /* soft acquire */ + create_request(request, "acquire"); + request["params"]["user"] = "itay"; + request["params"]["force"] = false; + + send_request(request, response); + EXPECT_TRUE(response["result"] != Json::nullValue); + + create_request(request, "get_owner"); + send_request(request, response); + EXPECT_TRUE(response["result"] != Json::nullValue); + + EXPECT_TRUE(response["result"]["owner"] == "itay"); + + /* hard acquire */ + create_request(request, "acquire"); + request["params"]["user"] = "moshe"; + request["params"]["force"] = false; + + send_request(request, response); + EXPECT_TRUE(response["result"] == Json::nullValue); + + request["params"]["force"] = true; + + send_request(request, response); + EXPECT_TRUE(response["result"] != Json::nullValue); + + string handler = response["result"].asString(); + + /* make sure */ + create_request(request, "get_owner"); + send_request(request, response); + EXPECT_TRUE(response["result"] != Json::nullValue); + + EXPECT_TRUE(response["result"]["owner"] == "moshe"); + + /* release */ + create_request(request, "release"); + request["params"]["handler"] = handler; + send_request(request, response); + + EXPECT_TRUE(response["result"] == "ACK"); +} + + +static void +create_simple_stream(Json::Value &obj) { + obj["mode"]["type"] = "continuous"; + obj["mode"]["pps"] = (rand() % 1000 + 1) * 0.99; + obj["isg"] = (rand() % 100 + 1) * 0.99;; + obj["enabled"] = true; + obj["self_start"] = true; + obj["next_stream_id"] = -1; + + obj["packet"]["meta"] = "dummy"; + + int packet_size = (rand() % 1500 + 1); + for (int i = 0; i < packet_size; i++) { + obj["packet"]["binary"][i] = (rand() % 0xff); + } + + obj["vm"] = Json::arrayValue; + obj["rx_stats"]["enabled"] = false; +} + +static bool +compare_streams(const Json::Value &s1, const Json::Value &s2) { + return s1 == s2; +} + +TEST_F(RpcTestOwned, add_remove_stream) { + Json::Value request; + Json::Value response; + Json::Reader reader; + + /* verify no such stream */ + create_request(request, "get_stream", 1); request["params"]["port_id"] = 1; request["params"]["stream_id"] = 5; @@ -297,58 +477,31 @@ TEST_F(RpcTest, add_stream) { EXPECT_EQ(response["id"], 1); EXPECT_EQ(response["error"]["code"], -32000); - // add it - create_request(request, "add_stream", 1, true); + /* add it */ + create_request(request, "add_stream", 1); request["params"]["port_id"] = 1; request["params"]["stream_id"] = 5; - request["params"]["stream"]["mode"]["type"] = "continuous"; - request["params"]["stream"]["mode"]["pps"] = 3; - request["params"]["stream"]["isg"] = 4.3; - request["params"]["stream"]["enabled"] = true; - request["params"]["stream"]["self_start"] = true; - request["params"]["stream"]["next_stream_id"] = -1; - request["params"]["stream"]["packet"]["meta"] = "dummy"; - request["params"]["stream"]["packet"]["binary"][0] = 4; - request["params"]["stream"]["packet"]["binary"][1] = 1; - request["params"]["stream"]["packet"]["binary"][2] = 255; - - request["params"]["stream"]["vm"] = Json::arrayValue; - request["params"]["stream"]["rx_stats"]["enabled"] = false; + Json::Value stream; + create_simple_stream(stream); + request["params"]["stream"] = stream; send_request(request, response); EXPECT_EQ(response["result"], "ACK"); /* get it */ - - create_request(request, "get_stream", 1, true); + create_request(request, "get_stream", 1); request["params"]["port_id"] = 1; request["params"]["stream_id"] = 5; send_request(request, response); - const Json::Value &stream = response["result"]["stream"]; - - EXPECT_EQ(stream["enabled"], true); - EXPECT_EQ(stream["self_start"], true); - - EXPECT_EQ(stream["packet"]["binary"][0], 4); - EXPECT_EQ(stream["packet"]["binary"][1], 1); - EXPECT_EQ(stream["packet"]["binary"][2], 255); - - EXPECT_EQ(stream["packet"]["meta"], "dummy"); - EXPECT_EQ(stream["next_stream_id"], -1); - - double delta = stream["isg"].asDouble() - 4.3; - EXPECT_TRUE(delta < 0.0001); - - EXPECT_EQ(stream["mode"]["type"], "continuous"); - EXPECT_EQ(stream["mode"]["pps"], 3); + EXPECT_TRUE(compare_streams(stream, response["result"]["stream"])); // remove it - create_request(request, "remove_stream", 1, true); + create_request(request, "remove_stream", 1); request["params"]["port_id"] = 1; request["params"]["stream_id"] = 5; diff --git a/src/rpc-server/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/commands/trex_rpc_cmd_general.cpp index d5aa3a90..afa15973 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp @@ -18,9 +18,17 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + #include "trex_rpc_cmds.h" #include #include +#include + +#include +#include +#include + +//#include #ifndef TREX_RPC_MOCK_SERVER #include <../linux_dpdk/version.h> @@ -29,36 +37,161 @@ limitations under the License. using namespace std; /** - * get status + * ping command + */ +trex_rpc_cmd_rc_e +TrexRpcCmdPing::_run(const Json::Value ¶ms, Json::Value &result) { + + result["result"] = "ACK"; + return (TREX_RPC_CMD_OK); +} + +/** + * query command + */ +trex_rpc_cmd_rc_e +TrexRpcCmdGetCmds::_run(const Json::Value ¶ms, Json::Value &result) { + vector cmds; + + TrexRpcCommandsTable::get_instance().query(cmds); + + Json::Value test = Json::arrayValue; + for (auto cmd : cmds) { + test.append(cmd); + } + + result["result"] = test; + + return (TREX_RPC_CMD_OK); +} + +/** + * get version * */ trex_rpc_cmd_rc_e -TrexRpcCmdGetStatus::_run(const Json::Value ¶ms, Json::Value &result) { +TrexRpcCmdGetVersion::_run(const Json::Value ¶ms, Json::Value &result) { Json::Value §ion = result["result"]; #ifndef TREX_RPC_MOCK_SERVER - section["general"]["version"] = VERSION_BUILD_NUM; - section["general"]["build_date"] = get_build_date(); - section["general"]["build_time"] = get_build_time(); - section["general"]["built_by"] = VERSION_USER; + section["version"] = VERSION_BUILD_NUM; + section["build_date"] = get_build_date(); + section["build_time"] = get_build_time(); + section["built_by"] = VERSION_USER; #else - section["general"]["version"] = "v0.0"; - section["general"]["build_date"] = __DATE__; - section["general"]["build_time"] = __TIME__; - section["general"]["version_user"] = "MOCK"; + section["version"] = "v0.0"; + section["build_date"] = __DATE__; + section["build_time"] = __TIME__; + section["built_by"] = "MOCK"; #endif - section["general"]["uptime"] = TrexRpcServer::get_server_uptime(); - section["general"]["owner"] = TrexRpcServer::get_owner(); + return (TREX_RPC_CMD_OK); +} + +/** + * get the CPU model + * + */ +std::string +TrexRpcCmdGetSysInfo::get_cpu_model() { + + static const string cpu_prefix = "model name"; + std::ifstream cpuinfo("/proc/cpuinfo"); + + if (cpuinfo.is_open()) { + while (cpuinfo.good()) { + + std::string line; + getline(cpuinfo, line); + + int pos = line.find(cpu_prefix); + if (pos == string::npos) { + continue; + } - // ports + /* trim it */ + int index = cpu_prefix.size() + 1; + while ( (line[index] == ' ') || (line[index] == ':') ) { + index++; + } - section["ports"]["count"] = TrexStateless::get_instance().get_port_count(); + return line.substr(index); + } + } + + return "unknown"; +} + +void +TrexRpcCmdGetSysInfo::get_hostname(string &hostname) { + char buffer[256]; + buffer[0] = 0; + + gethostname(buffer, sizeof(buffer)); + + /* write hostname */ + hostname = buffer; +} + +/** + * get system info + * + */ +trex_rpc_cmd_rc_e +TrexRpcCmdGetSysInfo::_run(const Json::Value ¶ms, Json::Value &result) { + string hostname; + + TrexStateless & instance = TrexStateless::get_instance(); + + Json::Value §ion = result["result"]; + + get_hostname(hostname); + section["hostname"] = hostname; + + section["uptime"] = TrexRpcServer::get_server_uptime(); + + /* FIXME: core count */ + section["dp_core_count"] = 1; + section["core_type"] = get_cpu_model(); + + /* ports */ + + + section["port_count"] = instance.get_port_count(); + + section["ports"] = Json::arrayValue; + + for (int i = 0; i < instance.get_port_count(); i++) { + string driver; + string speed; + + TrexStatelessPort *port = instance.get_port_by_id(i); + port->get_properties(driver, speed); + + section["ports"][i]["index"] = i; + section["ports"][i]["driver"] = driver; + section["ports"][i]["speed"] = speed; + + switch (port->get_state()) { + case TrexStatelessPort::PORT_STATE_DOWN: + section["ports"][i]["status"] = "down"; + break; + + case TrexStatelessPort::PORT_STATE_UP_IDLE: + section["ports"][i]["status"] = "idle"; + break; + + case TrexStatelessPort::PORT_STATE_TRANSMITTING: + section["ports"][i]["status"] = "transmitting"; + break; + } + + } return (TREX_RPC_CMD_OK); } diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp index b62d213b..41567509 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -63,6 +63,9 @@ TrexRpcCmdAddStream::_run(const Json::Value ¶ms, Json::Value &result) { /* allocate a new stream based on the type */ TrexStream *stream = allocate_new_stream(section, port_id, stream_id, result); + /* save this for future queries */ + stream->store_stream_json(section); + /* some fields */ stream->m_enabled = parse_bool(section, "enabled", result); stream->m_self_start = parse_bool(section, "self_start", result); @@ -130,7 +133,7 @@ TrexRpcCmdAddStream::allocate_new_stream(const Json::Value §ion, uint8_t por if (type == "continuous") { - uint32_t pps = parse_int(mode, "pps", result); + double pps = parse_double(mode, "pps", result); stream = new TrexStreamContinuous(port_id, stream_id, pps); } else if (type == "single_burst") { @@ -413,30 +416,11 @@ TrexRpcCmdGetStream::_run(const Json::Value ¶ms, Json::Value &result) { generate_execute_err(result, ss.str()); } - Json::Value stream_json; - - stream_json["enabled"] = stream->m_enabled; - stream_json["self_start"] = stream->m_self_start; - - stream_json["isg"] = stream->m_isg_usec; - stream_json["next_stream_id"] = stream->m_next_stream_id; - - stream_json["packet"]["binary"] = Json::arrayValue; - for (int i = 0; i < stream->m_pkt.len; i++) { - stream_json["packet"]["binary"].append(stream->m_pkt.binary[i]); - } - - stream_json["packet"]["meta"] = stream->m_pkt.meta; - - if (TrexStreamContinuous *cont = dynamic_cast(stream)) { - stream_json["mode"]["type"] = "continuous"; - stream_json["mode"]["pps"] = cont->get_pps(); - - } - - result["result"]["stream"] = stream_json; + /* return the stored stream json (instead of decoding it all over again) */ + result["result"]["stream"] = stream->get_stream_json(); return (TREX_RPC_CMD_OK); + } /*************************** @@ -455,17 +439,17 @@ TrexRpcCmdStartTraffic::_run(const Json::Value ¶ms, Json::Value &result) { TrexStatelessPort *port = TrexStateless::get_instance().get_port_by_id(port_id); - TrexStatelessPort::traffic_rc_e rc = port->start_traffic(); + TrexStatelessPort::rc_e rc = port->start_traffic(); - if (rc == TrexStatelessPort::TRAFFIC_OK) { + if (rc == TrexStatelessPort::RC_OK) { result["result"] = "ACK"; } else { std::stringstream ss; switch (rc) { - case TrexStatelessPort::TRAFFIC_ERR_ALREADY_STARTED: - ss << "traffic has already started on that port"; + case TrexStatelessPort::RC_ERR_BAD_STATE_FOR_OP: + ss << "bad state for operations: port is either transmitting traffic or down"; break; - case TrexStatelessPort::TRAFFIC_ERR_NO_STREAMS: + case TrexStatelessPort::RC_ERR_NO_STREAMS: ss << "no active streams on that port"; break; default: diff --git a/src/rpc-server/commands/trex_rpc_cmd_test.cpp b/src/rpc-server/commands/trex_rpc_cmd_test.cpp index 3153317e..3cdddd31 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_test.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_test.cpp @@ -21,7 +21,6 @@ limitations under the License. #include "trex_rpc_cmds.h" #include #include -#include using namespace std; @@ -50,32 +49,3 @@ TrexRpcCmdTestSub::_run(const Json::Value ¶ms, Json::Value &result) { return (TREX_RPC_CMD_OK); } -/** - * ping command - */ -trex_rpc_cmd_rc_e -TrexRpcCmdPing::_run(const Json::Value ¶ms, Json::Value &result) { - - result["result"] = "ACK"; - return (TREX_RPC_CMD_OK); -} - -/** - * query command - */ -trex_rpc_cmd_rc_e -TrexRpcCmdGetReg::_run(const Json::Value ¶ms, Json::Value &result) { - vector cmds; - - TrexRpcCommandsTable::get_instance().query(cmds); - - Json::Value test = Json::arrayValue; - for (auto cmd : cmds) { - test.append(cmd); - } - - result["result"] = test; - - return (TREX_RPC_CMD_OK); -} - diff --git a/src/rpc-server/commands/trex_rpc_cmds.h b/src/rpc-server/commands/trex_rpc_cmds.h index 32138195..643aa22d 100644 --- a/src/rpc-server/commands/trex_rpc_cmds.h +++ b/src/rpc-server/commands/trex_rpc_cmds.h @@ -35,7 +35,7 @@ class TrexStream; * syntactic sugar for creating a simple command */ -#define TREX_RPC_CMD_DEFINE_EXTENED(class_name, cmd_name, param_count, needs_ownership, ext) \ +#define TREX_RPC_CMD_DEFINE_EXTENDED(class_name, cmd_name, param_count, needs_ownership, ext) \ class class_name : public TrexRpcCommand { \ public: \ class_name () : TrexRpcCommand(cmd_name, param_count, needs_ownership) {} \ @@ -44,7 +44,7 @@ class TrexStream; ext \ } -#define TREX_RPC_CMD_DEFINE(class_name, cmd_name, param_count, needs_ownership) TREX_RPC_CMD_DEFINE_EXTENED(class_name, cmd_name, param_count, needs_ownership, ;) +#define TREX_RPC_CMD_DEFINE(class_name, cmd_name, param_count, needs_ownership) TREX_RPC_CMD_DEFINE_EXTENDED(class_name, cmd_name, param_count, needs_ownership, ;) /** * test cmds @@ -55,9 +55,16 @@ TREX_RPC_CMD_DEFINE(TrexRpcCmdTestSub, "test_sub", 2, false); /** * general cmds */ -TREX_RPC_CMD_DEFINE(TrexRpcCmdPing, "ping", 0, false); -TREX_RPC_CMD_DEFINE(TrexRpcCmdGetReg, "get_reg_cmds", 0, false); -TREX_RPC_CMD_DEFINE(TrexRpcCmdGetStatus, "get_status", 0, false); +TREX_RPC_CMD_DEFINE(TrexRpcCmdPing, "ping", 0, false); +TREX_RPC_CMD_DEFINE(TrexRpcCmdGetCmds, "get_supported_cmds", 0, false); +TREX_RPC_CMD_DEFINE(TrexRpcCmdGetVersion, "get_version", 0, false); + +TREX_RPC_CMD_DEFINE_EXTENDED(TrexRpcCmdGetSysInfo, "get_system_info", 0, false, + +std::string get_cpu_model(); +void get_hostname(std::string &hostname); + +); /** * ownership @@ -73,7 +80,7 @@ TREX_RPC_CMD_DEFINE(TrexRpcCmdRelease, "release", 0, true); TREX_RPC_CMD_DEFINE(TrexRpcCmdRemoveAllStreams, "remove_all_streams", 1, true); TREX_RPC_CMD_DEFINE(TrexRpcCmdRemoveStream, "remove_stream", 2, true); -TREX_RPC_CMD_DEFINE_EXTENED(TrexRpcCmdAddStream, "add_stream", 3, true, +TREX_RPC_CMD_DEFINE_EXTENDED(TrexRpcCmdAddStream, "add_stream", 3, true, /* extended part */ TrexStream * allocate_new_stream(const Json::Value §ion, uint8_t port_id, uint32_t stream_id, Json::Value &result); @@ -92,4 +99,6 @@ TREX_RPC_CMD_DEFINE(TrexRpcCmdGetStream, "get_stream", 2, true); TREX_RPC_CMD_DEFINE(TrexRpcCmdStartTraffic, "start_traffic", 1, true); TREX_RPC_CMD_DEFINE(TrexRpcCmdStopTraffic, "stop_traffic", 1, true); + + #endif /* __TREX_RPC_CMD_H__ */ diff --git a/src/rpc-server/trex_rpc_cmds_table.cpp b/src/rpc-server/trex_rpc_cmds_table.cpp index f968bb74..170f0de1 100644 --- a/src/rpc-server/trex_rpc_cmds_table.cpp +++ b/src/rpc-server/trex_rpc_cmds_table.cpp @@ -34,8 +34,9 @@ TrexRpcCommandsTable::TrexRpcCommandsTable() { /* general */ register_command(new TrexRpcCmdPing()); - register_command(new TrexRpcCmdGetReg()); - register_command(new TrexRpcCmdGetStatus()); + register_command(new TrexRpcCmdGetCmds()); + register_command(new TrexRpcCmdGetVersion()); + register_command(new TrexRpcCmdGetSysInfo()); register_command(new TrexRpcCmdGetOwner()); register_command(new TrexRpcCmdAcquire()); register_command(new TrexRpcCmdRelease()); diff --git a/src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp index 928baca6..9d9de53a 100644 --- a/src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp +++ b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.cpp @@ -225,3 +225,11 @@ std::string TrexJsonRpcV2Parser::pretty_json_str(const std::string &json_str) { return writer.write(value); } +void +TrexJsonRpcV2Parser::generate_common_error(Json::Value &json, const std::string &specific_err) { + JsonRpcError err(Json::Value::null, JSONRPC_V2_ERR_INTERNAL_ERROR, specific_err, true); + + err.execute(json); + +} + diff --git a/src/rpc-server/trex_rpc_jsonrpc_v2_parser.h b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.h index ebffaeb7..0563f21d 100644 --- a/src/rpc-server/trex_rpc_jsonrpc_v2_parser.h +++ b/src/rpc-server/trex_rpc_jsonrpc_v2_parser.h @@ -79,6 +79,15 @@ public: */ void parse(std::vector &commands); + /** + * will generate a valid JSON RPC v2 error message with + * generic error code and message + * + * @author imarom (16-Sep-15) + * + */ + static void generate_common_error(Json::Value &json, const std::string &specific_err); + /** * *tries* to generate a pretty string from JSON * if json_str is not a valid JSON string diff --git a/src/rpc-server/trex_rpc_req_resp_server.cpp b/src/rpc-server/trex_rpc_req_resp_server.cpp index c4d9dfdb..3d52686c 100644 --- a/src/rpc-server/trex_rpc_req_resp_server.cpp +++ b/src/rpc-server/trex_rpc_req_resp_server.cpp @@ -82,6 +82,13 @@ void TrexRpcServerReqRes::_rpc_thread_cb() { } } + if (msg_size >= sizeof(m_msg_buffer)) { + std::stringstream ss; + ss << "RPC request of '" << msg_size << "' exceeds maximum message size which is '" << sizeof(m_msg_buffer) << "'"; + handle_server_error(ss.str()); + continue; + } + /* transform it to a string */ std::string request((const char *)m_msg_buffer, msg_size); @@ -145,3 +152,23 @@ void TrexRpcServerReqRes::handle_request(const std::string &request) { zmq_send(m_socket, response_str.c_str(), response_str.size(), 0); } + +/** + * handles a server error + * + */ +void +TrexRpcServerReqRes::handle_server_error(const std::string &specific_err) { + Json::FastWriter writer; + Json::Value response; + + /* generate error */ + TrexJsonRpcV2Parser::generate_common_error(response, specific_err); + + /* write the JSON to string and sever on ZMQ */ + std::string response_str = writer.write(response); + + verbose_json("Server Replied: ", response_str); + + zmq_send(m_socket, response_str.c_str(), response_str.size(), 0); +} diff --git a/src/rpc-server/trex_rpc_req_resp_server.h b/src/rpc-server/trex_rpc_req_resp_server.h index f12d0540..7c1d66d1 100644 --- a/src/rpc-server/trex_rpc_req_resp_server.h +++ b/src/rpc-server/trex_rpc_req_resp_server.h @@ -39,9 +39,11 @@ protected: void _stop_rpc_thread(); private: + void handle_request(const std::string &request); + void handle_server_error(const std::string &specific_err); - static const int RPC_MAX_MSG_SIZE = 2048; + static const int RPC_MAX_MSG_SIZE = (20 * 1024); void *m_context; void *m_socket; uint8_t m_msg_buffer[RPC_MAX_MSG_SIZE]; diff --git a/src/stateless/trex_stateless.cpp b/src/stateless/trex_stateless.cpp index 2ab0c5d9..b51c4e69 100644 --- a/src/stateless/trex_stateless.cpp +++ b/src/stateless/trex_stateless.cpp @@ -20,6 +20,8 @@ limitations under the License. */ #include +using namespace std; + /*********************************************************** * Trex stateless object * @@ -76,7 +78,7 @@ uint8_t TrexStateless::get_port_count() { * **************************/ TrexStatelessPort::TrexStatelessPort(uint8_t port_id) : m_port_id(port_id) { - m_started = false; + m_port_state = PORT_STATE_DOWN; } @@ -84,25 +86,29 @@ TrexStatelessPort::TrexStatelessPort(uint8_t port_id) : m_port_id(port_id) { * starts the traffic on the port * */ -TrexStatelessPort::traffic_rc_e +TrexStatelessPort::rc_e TrexStatelessPort::start_traffic(void) { - if (m_started) { - return (TRAFFIC_ERR_ALREADY_STARTED); + + if (m_port_state != PORT_STATE_UP_IDLE) { + return (RC_ERR_BAD_STATE_FOR_OP); } if (get_stream_table()->size() == 0) { - return (TRAFFIC_ERR_NO_STREAMS); + return (RC_ERR_NO_STREAMS); } - m_started = true; + m_port_state = PORT_STATE_TRANSMITTING; - return (TRAFFIC_OK); + /* real code goes here */ + return (RC_OK); } void TrexStatelessPort::stop_traffic(void) { - if (m_started) { - m_started = false; + + /* real code goes here */ + if (m_port_state = PORT_STATE_TRANSMITTING) { + m_port_state = PORT_STATE_UP_IDLE; } } @@ -114,4 +120,10 @@ TrexStreamTable * TrexStatelessPort::get_stream_table() { return &m_stream_table; } +void +TrexStatelessPort::get_properties(string &driver, string &speed) { + /* take this from DPDK */ + driver = "Unknown Driver"; + speed = "Unknown Speed"; +} diff --git a/src/stateless/trex_stateless_api.h b/src/stateless/trex_stateless_api.h index 358ab339..60d26878 100644 --- a/src/stateless/trex_stateless_api.h +++ b/src/stateless/trex_stateless_api.h @@ -50,19 +50,36 @@ class TrexStatelessPort { public: /** - * describess error codes for starting traffic + * port state */ - enum traffic_rc_e { - TRAFFIC_OK, - TRAFFIC_ERR_ALREADY_STARTED, - TRAFFIC_ERR_NO_STREAMS, - TRAFFIC_ERR_FAILED_TO_COMPILE_STREAMS + enum port_state_e { + PORT_STATE_DOWN, + PORT_STATE_UP_IDLE, + PORT_STATE_TRANSMITTING + }; + + /** + * describess different error codes for port operations + */ + enum rc_e { + RC_OK, + RC_ERR_BAD_STATE_FOR_OP, + RC_ERR_NO_STREAMS, + RC_ERR_FAILED_TO_COMPILE_STREAMS }; TrexStatelessPort(uint8_t port_id); - traffic_rc_e start_traffic(void); + /** + * start traffic + * + */ + rc_e start_traffic(void); + /** + * stop traffic + * + */ void stop_traffic(void); /** @@ -71,10 +88,28 @@ public: */ TrexStreamTable *get_stream_table(); + /** + * get the port state + * + */ + port_state_e get_state() { + return m_port_state; + } + + /** + * fill up properties of the port + * + * @author imarom (16-Sep-15) + * + * @param driver + * @param speed + */ + void get_properties(std::string &driver, std::string &speed); + private: TrexStreamTable m_stream_table; uint8_t m_port_id; - bool m_started; + port_state_e m_port_state; }; /** diff --git a/src/stateless/trex_stream.cpp b/src/stateless/trex_stream.cpp index 2b5b2424..8bf04748 100644 --- a/src/stateless/trex_stream.cpp +++ b/src/stateless/trex_stream.cpp @@ -45,6 +45,17 @@ TrexStream::~TrexStream() { } } +void +TrexStream::store_stream_json(const Json::Value &stream_json) { + /* deep copy */ + m_stream_json = stream_json; +} + +const Json::Value & +TrexStream::get_stream_json() { + return m_stream_json; +} + /************************************** * stream table *************************************/ diff --git a/src/stateless/trex_stream_api.h b/src/stateless/trex_stream_api.h index 0a955ff7..d3c0fb29 100644 --- a/src/stateless/trex_stream_api.h +++ b/src/stateless/trex_stream_api.h @@ -26,6 +26,8 @@ limitations under the License. #include #include +#include + #include class TrexRpcCmdAddStream; @@ -48,7 +50,13 @@ public: static const uint32_t MIN_PKT_SIZE_BYTES = 1; static const uint32_t MAX_PKT_SIZE_BYTES = 9000; -private: + /* provides storage for the stream json*/ + void store_stream_json(const Json::Value &stream_json); + + /* access the stream json */ + const Json::Value & get_stream_json(); + +protected: /* basic */ uint8_t m_port_id; uint32_t m_stream_id; @@ -82,6 +90,8 @@ private: } m_rx_check; + /* original template provided by requester */ + Json::Value m_stream_json; }; /** -- cgit 1.2.3-korg From 56dbd342eb97fc087611157ce8e965088b7f9bf8 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Thu, 17 Sep 2015 19:50:35 +0300 Subject: Major progress in the packet builder module. Now able to create packets, dump them to JSON of pcap and more features added --- .../client_utils/packet_builder.py | 110 ++++++++++++++++++--- 1 file changed, 99 insertions(+), 11 deletions(-) (limited to 'scripts/automation/trex_control_plane/client_utils') diff --git a/scripts/automation/trex_control_plane/client_utils/packet_builder.py b/scripts/automation/trex_control_plane/client_utils/packet_builder.py index abe98dbb..fc34d931 100644 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -45,6 +45,9 @@ class CTRexPktBuilder(object): 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: @@ -71,7 +74,7 @@ class CTRexPktBuilder(object): raise KeyError("Specified layer '{0}' doesn't exist on packet.".format(layer_name)) def set_ipv6_layer_addr(self, layer_name, attr, ip_addr): - self.set_ip_layer_addr(self, layer_name, attr, ip_addr, ip_type="ipv6") + self.set_ip_layer_addr(layer_name, attr, ip_addr, ip_type="ipv6") def set_eth_layer_addr(self, layer_name, attr, mac_addr): try: @@ -84,7 +87,7 @@ class CTRexPktBuilder(object): except KeyError: raise KeyError("Specified layer '{0}' doesn't exist on packet.".format(layer_name)) - def set_layer_attr(self, layer_name, attr, val): + 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. @@ -101,6 +104,11 @@ class CTRexPktBuilder(object): 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. @@ -110,19 +118,25 @@ class CTRexPktBuilder(object): 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 obejct that is not dpkt.Packet is not allowed using " + 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): - setattr(layer, attr, val) - if attr == 'data': - # re-evaluate packet from the start, possible broken link between layers - self._reevaluate_packet(layer_name.lower()) + 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): + 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. @@ -185,7 +199,7 @@ class CTRexPktBuilder(object): def get_packet(self, get_ptr=False): """ - This method enables the user to change a value of a previously defined packet layer. + This method provides access to the built packet, as an instance or as a pointer to packet itself. :parameters: get_ptr : bool @@ -196,17 +210,33 @@ class CTRexPktBuilder(object): default value : False :return: - the current packet built by CTRexPktBuilder object. + + 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_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_start, ip_end, ip_type="ipv4"): pass @@ -221,8 +251,57 @@ class CTRexPktBuilder(object): pass 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. + + """ + if self._packet is None: + raise CTRexPktBuilder.EmptyPacketError() pkt_in_hex = binascii.hexlify(str(self._packet)) - return [pkt_in_hex[i:i+2] for i in range(0, len(pkt_in_hex), 2)] + return [int(pkt_in_hex[i:i+2], 16) + for i in range(0, len(pkt_in_hex), 2)] + # return [pkt_in_hex[i:i+2] for i in range(0, len(pkt_in_hex), 2)] + + 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") + # ----- useful shortcut methods ----- # def gen_dns_packet(self): @@ -568,6 +647,15 @@ class CTRexPktBuilder(object): 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. -- cgit 1.2.3-korg