From e46e3f598e52112b9db21d6faabde7a5c87341cb Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 16 Nov 2016 17:26:59 +0200 Subject: RX features - ARP resolve Signed-off-by: imarom --- linux/ws_main.py | 2 + .../trex_control_plane/stl/console/trex_console.py | 7 ++ .../stl/trex_stl_lib/trex_stl_client.py | 123 +++++++++++++++------ .../stl/trex_stl_lib/trex_stl_port.py | 45 ++++++-- .../stl/trex_stl_lib/utils/common.py | 2 +- .../stl/trex_stl_lib/utils/parsing_opts.py | 7 ++ src/rpc-server/commands/trex_rpc_cmd_general.cpp | 66 +++-------- src/rpc-server/commands/trex_rpc_cmds.h | 4 +- src/stateless/dp/trex_stateless_dp_core.cpp | 29 ++--- src/trex_port_attr.cpp | 6 + src/trex_port_attr.h | 17 +-- 11 files changed, 187 insertions(+), 121 deletions(-) diff --git a/linux/ws_main.py b/linux/ws_main.py index 9e77485d..b835e4ce 100755 --- a/linux/ws_main.py +++ b/linux/ws_main.py @@ -168,6 +168,8 @@ stateless_src = SrcGroup(dir='src/stateless/', 'cp/trex_dp_port_events.cpp', 'dp/trex_stateless_dp_core.cpp', 'messaging/trex_stateless_messaging.cpp', + 'rx/trex_stateless_rx_core.cpp', + 'rx/trex_stateless_rx_port_mngr.cpp' ]) # RPC code rpc_server_src = SrcGroup(dir='src/rpc-server/', diff --git a/scripts/automation/trex_control_plane/stl/console/trex_console.py b/scripts/automation/trex_control_plane/stl/console/trex_console.py index 92c04a7f..f1635b97 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -334,6 +334,13 @@ class TRexConsole(TRexGeneralCmd): def help_sniffer (self): self.do_set_rx_sniffer("-h") + def do_resolve (self, line): + '''Resolve ARP for ports''' + self.stateless_client.resolve_line(line) + + def help_sniffer (self): + self.do_resolve("-h") + @verify_connected def do_map (self, line): '''Maps ports topology\n''' diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index 3a37524c..9290acbf 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -2110,7 +2110,25 @@ class STLClient(object): raise STLError(rc) - + # common checks for start API + def __pre_start_check (self, ports, force): + + # verify link status + ports_link_down = [port_id for port_id in ports if not self.ports[port_id].is_up()] + if not force and ports_link_down: + raise STLError("Port(s) %s - link DOWN - check the connection or specify 'force'" % ports_link_down) + + # verify ports are stopped or force stop them + active_ports = list(set(self.get_active_ports()).intersection(ports)) + if active_ports and not force: + raise STLError("Port(s) {0} are active - please stop them or specify 'force'".format(active_ports)) + + # warn if ports are not resolved + unresolved_ports = [port_id for port_id in ports if not self.ports[port_id].is_resolved()] + if unresolved_ports and not force: + raise STLError("Port(s) {0} are unresolved - please resolve them or specify 'force'".format(unresolved_ports)) + + @__api_check(True) def start (self, ports = None, @@ -2165,11 +2183,9 @@ class STLClient(object): validate_type('total', total, bool) validate_type('core_mask', core_mask, (int, list)) - # verify link status - ports_link_down = [port_id for port_id in ports if not self.ports[port_id].is_up()] - if not force and ports_link_down: - raise STLError("Port(s) %s - link DOWN - check the connection or specify 'force'" % ports_link_down) - + + self.__pre_start_check(ports, force) + ######################### # decode core mask argument decoded_mask = self.__decode_core_mask(ports, core_mask) @@ -2183,17 +2199,12 @@ class STLClient(object): raise STLArgumentError('mult', mult) - # verify ports are stopped or force stop them + # stop active ports if needed active_ports = list(set(self.get_active_ports()).intersection(ports)) - if active_ports: - if not force: - raise STLError("Port(s) {0} are active - please stop them or specify 'force'".format(active_ports)) - else: - rc = self.stop(active_ports) - if not rc: - raise STLError(rc) - + if active_ports and force: + self.stop(active_ports) + # start traffic self.logger.pre_cmd("Starting traffic on port(s) {0}:".format(ports)) rc = self.__start(mult_obj, duration, ports, force, decoded_mask) @@ -2750,7 +2761,7 @@ class STLClient(object): rxf = None, ipv4 = None, dest = None, - ): + resolve = True): """ Set port attributes @@ -2764,6 +2775,7 @@ class STLClient(object): of IPv4 addresses in the same length of the ports array dest - configure destination address for port(s) in either IPv4 or MAC format. for multiple ports should be a list in the same length of the ports array + resolve - if true, in case a destination address is configured as IPv4 try to resolve it :raises: + :exe:'STLError' @@ -2817,10 +2829,21 @@ class STLClient(object): self.logger.pre_cmd("Applying attributes on port(s) {0}:".format(ports)) rc = self.__set_port_attr(ports, attr_dict) self.logger.post_cmd(rc) - + if not rc: raise STLError(rc) + + # automatic resolve + if resolve: + # find any port with a dest configured as IPv4 + resolve_ports = [port_id for port_id, port_dest in zip(ports, dest) if is_valid_ipv4(port_dest)] + + if resolve_ports: + self.resolve(ports = resolve_ports) + + + @__api_check(True) def resolve (self, ports = None, retries = 0): @@ -2837,10 +2860,15 @@ class STLClient(object): # by default - resolve all the ports that are configured with IPv4 dest if ports is None: ports = [port_id for port_id in self.get_acquired_ports() if self.ports[port_id].get_dest()['type'] == 'ipv4'] + if not ports: + raise STLError('No ports configured with destination as IPv4') + active_ports = list(set(self.get_active_ports()).intersection(ports)) + if active_ports: + raise STLError('Port(s) {0} are active'.format(active_ports)) + ports = self._validate_port_list(ports) - self.logger.pre_cmd("Resolving destination on port(s) {0}:".format(ports)) with self.logger.supress(): rc = self.__resolve(ports, retries) @@ -3205,14 +3233,19 @@ class STLClient(object): # just for sanity - will be checked on the API as well self.__decode_core_mask(opts.ports, core_mask) + # for better use experience - check this first + try: + self.__pre_start_check(opts.ports, opts.force) + except STLError as e: + msg = e.brief() + self.logger.log(format_text(msg, 'bold')) + return RC_ERR(msg) + + + # stop ports if needed active_ports = list_intersect(self.get_active_ports(), opts.ports) - if active_ports: - if not opts.force: - msg = "Port(s) {0} are active - please stop them or add '--force'\n".format(active_ports) - self.logger.log(format_text(msg, 'bold')) - return RC_ERR(msg) - else: - self.stop(active_ports) + if active_ports and opts.force: + self.stop(active_ports) # process tunables @@ -3584,15 +3617,16 @@ class STLClient(object): print(' Flow control: %s' % info['fc_supported']) print('') else: - return self.set_port_attr(opts.ports, - opts.prom, - opts.link, - opts.led, - opts.flow_ctrl, - opts.rx_filter_mode, - opts.ipv4, - opts.dest) - + self.set_port_attr(opts.ports, + opts.prom, + opts.link, + opts.led, + opts.flow_ctrl, + opts.rx_filter_mode, + opts.ipv4, + opts.dest) + + @__console @@ -3616,6 +3650,27 @@ class STLClient(object): self.set_rx_sniffer(opts.ports, opts.output_filename, opts.limit, rxf) + @__console + def resolve_line (self, line): + '''Performs a port ARP resolution''' + + parser = parsing_opts.gen_parser(self, + "resolve", + self.resolve_line.__doc__, + parsing_opts.PORT_LIST_WITH_ALL, + parsing_opts.RETRIES) + + resolvable_ports = [port_id for port_id in self.get_acquired_ports() if self.ports[port_id].get_dest()['type'] == 'ipv4'] + + opts = parser.parse_args(line.split(), default_ports = resolvable_ports, verify_acquired = True) + if not opts: + return opts + + + self.resolve(ports = opts.ports, retries = opts.retries) + + + @__console def show_profile_line (self, line): '''Shows profile information''' diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py index e19eebe1..f658b7fa 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py @@ -752,7 +752,7 @@ class Port(object): base_pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(psrc = ipv4, pdst = dest['addr'], hwsrc = mac) s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) ) - + return self.add_streams([s1]) @@ -924,9 +924,22 @@ class Port(object): return self.__attr['src_mac'] + def is_resolved (self): + dest = self.get_dest() + + if dest['type'] == 'mac': + return True + elif dest['type'] == 'ipv4': + return dest['arp'] != 'none' + else: + # unsupported type + assert(0) + + def resolve (self, retries): return ARPResolver(self).resolve(retries) - + + ################# stats handler ###################### def generate_port_stats(self): @@ -1082,8 +1095,26 @@ class ARPResolver(object): return self.port.ok() - # main resolve function + + # safe call - make sure RX filter mode is restored def resolve (self, retries): + try: + rc = self.port.set_attr(rx_filter_mode = 'all') + if not rc: + return rc + rc = self.port.set_rx_queue(size = 100) + if not rc: + return rc + + return self.resolve_wrapper(retries) + finally: + # best effort restore + self.port.set_attr(rx_filter_mode = 'hw') + self.port.remove_rx_queue() + + + # main resolve function + def resolve_wrapper (self, retries): rc = self.sanity() if not rc: return rc @@ -1092,14 +1123,12 @@ class ARPResolver(object): rc = self.port.invalidate_arp() if not rc: return rc - - rc = self.port.remove_all_streams() - if not rc: - return rc + - rc = self.port.set_rx_queue(size = 100) + rc = self.port.remove_all_streams() if not rc: return rc + rc = self.port.add_arp_request() if not rc: diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py index d4ac973d..02e13fd7 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py @@ -92,7 +92,7 @@ def is_valid_ipv4 (addr): try: socket.inet_pton(socket.AF_INET, addr) return True - except socket.error: + except (socket.error, TypeError): return False def is_valid_mac (mac): diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py index 80260d4a..97c9035a 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py @@ -52,6 +52,7 @@ PORT_RESTART = 34 IPV4 = 35 DEST = 36 +RETRIES = 37 GLOBAL_STATS = 50 PORT_STATS = 51 @@ -344,6 +345,12 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], 'default': None, 'type': check_dest_addr}), + RETRIES: ArgumentPack(['-r', '--retries'], + {'help': 'retries count [default is zero]', + 'dest': 'retries', + 'default': 0, + 'type': int}), + OUTPUT_FILENAME: ArgumentPack(['-o', '--output'], {'help': 'Output PCAP filename', diff --git a/src/rpc-server/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/commands/trex_rpc_cmd_general.cpp index e7f0de7b..14b38165 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp @@ -385,13 +385,11 @@ TrexRpcCmdSetPortAttr::parse_dest(const Json::Value &msg, uint8_t port_id, Json: uint8_t mac[6]; if (utl_ipv4_to_uint32(addr.c_str(), ipv4_addr)) { - if (port_attr->get_src_ipv4() == 0) { - generate_parse_err(result, "unable to configure 'dest' as IPv4 without source IPv4 address configured"); - } port_attr->get_dest().set_dest_ipv4(ipv4_addr); } else if (utl_str_to_macaddr(addr, mac)) { port_attr->get_dest().set_dest_mac(mac); + } else { std::stringstream ss; ss << "'dest' is not an IPv4 address or a MAC address: '" << addr << "'"; @@ -404,33 +402,22 @@ TrexRpcCmdSetPortAttr::parse_dest(const Json::Value &msg, uint8_t port_id, Json: /** - * attributes in the high priority pass must be handled first - * for example, IPv4 configuration should be handled before dest - * + * set port commands + * + * @author imarom (24-Feb-16) + * + * @param params + * @param result + * + * @return trex_rpc_cmd_rc_e */ -void -TrexRpcCmdSetPortAttr::high_priority_pass(const Json::Value &attr, uint8_t port_id, Json::Value &result) { - int ret = 0; - - /* first iteration - high priority attributes */ - for (const std::string &name : attr.getMemberNames()) { - if (name == "ipv4") { - const Json::Value &ipv4 = parse_object(attr, name, result); - ret = parse_ipv4(ipv4, port_id, result); - } - - /* check error code */ - if ( ret == -ENOTSUP ) { - generate_execute_err(result, "Error applying " + name + ": operation is not supported for this NIC."); - } else if (ret) { - generate_execute_err(result, "Error applying " + name + " attribute, return value: " + to_string(ret)); - } - } -} +trex_rpc_cmd_rc_e +TrexRpcCmdSetPortAttr::_run(const Json::Value ¶ms, Json::Value &result) { + uint8_t port_id = parse_port(params, result); + + const Json::Value &attr = parse_object(params, "attr", result); -void -TrexRpcCmdSetPortAttr::regular_priority_pass(const Json::Value &attr, uint8_t port_id, Json::Value &result) { int ret = 0; /* iterate over all attributes in the dict */ @@ -462,7 +449,8 @@ TrexRpcCmdSetPortAttr::regular_priority_pass(const Json::Value &attr, uint8_t po } else if (name == "ipv4") { - /* ignore - was already taken care of in the high priority pass */ + const Json::Value &ipv4 = parse_object(attr, name, result); + ret = parse_ipv4(ipv4, port_id, result); } else if (name == "dest") { @@ -483,28 +471,6 @@ TrexRpcCmdSetPortAttr::regular_priority_pass(const Json::Value &attr, uint8_t po generate_execute_err(result, "Error applying " + name + " attribute, return value: " + to_string(ret)); } } -} - - -/** - * set port commands - * - * @author imarom (24-Feb-16) - * - * @param params - * @param result - * - * @return trex_rpc_cmd_rc_e - */ -trex_rpc_cmd_rc_e -TrexRpcCmdSetPortAttr::_run(const Json::Value ¶ms, Json::Value &result) { - - uint8_t port_id = parse_port(params, result); - - const Json::Value &attr = parse_object(params, "attr", result); - - high_priority_pass(attr, port_id, result); - regular_priority_pass(attr, port_id, result); result["result"] = Json::objectValue; 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 fab81f67..2b2178e2 100644 --- a/src/rpc-server/commands/trex_rpc_cmds.h +++ b/src/rpc-server/commands/trex_rpc_cmds.h @@ -94,9 +94,7 @@ TREX_RPC_CMD_DEFINE(TrexRpcCmdGetPortXStatsValues, "get_port_xstats_values", 1, TREX_RPC_CMD_DEFINE(TrexRpcCmdGetPortXStatsNames, "get_port_xstats_names", 1, false, APIClass::API_CLASS_TYPE_CORE); TREX_RPC_CMD_DEFINE_EXTENDED(TrexRpcCmdSetPortAttr, "set_port_attr", 2, true, APIClass::API_CLASS_TYPE_CORE, - - void high_priority_pass(const Json::Value &attr, uint8 port_id, Json::Value &result); - void regular_priority_pass(const Json::Value &attr, uint8_t port_id, Json::Value &result); + int parse_rx_filter_mode(const Json::Value &msg, uint8_t port_id, Json::Value &result); int parse_ipv4(const Json::Value &msg, uint8_t port_id, Json::Value &result); int parse_dest(const Json::Value &msg, uint8_t port_id, Json::Value &result); diff --git a/src/stateless/dp/trex_stateless_dp_core.cpp b/src/stateless/dp/trex_stateless_dp_core.cpp index 43b77bee..857ac8f9 100644 --- a/src/stateless/dp/trex_stateless_dp_core.cpp +++ b/src/stateless/dp/trex_stateless_dp_core.cpp @@ -26,7 +26,7 @@ limitations under the License. #include "trex_stream_node.h" #include "trex_streams_compiler.h" #include "mbuf.h" -#include "trex_stateless.h" + @@ -495,14 +495,12 @@ bool TrexStatelessDpPerPort::push_pcap(uint8_t port_id, /* main port */ uint8_t mac_addr[12]; - TRexPortAttr *master_port_attr = get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id); - master_port_attr->update_src_dst_mac(mac_addr); - + m_core->m_node_gen.m_v_if->update_mac_addr_from_global_cfg(dir, mac_addr); + /* for dual */ uint8_t slave_mac_addr[12]; - TRexPortAttr *slave_port_attr = get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id ^ 0x1); - slave_port_attr->update_src_dst_mac(slave_mac_addr); - + m_core->m_node_gen.m_v_if->update_mac_addr_from_global_cfg(dir ^ 0x1, slave_mac_addr); + bool rc = pcap_node->create(port_id, dir, socket_id, @@ -825,9 +823,8 @@ void TrexStatelessDpCore::update_mac_addr(TrexStream * stream, CGenNodeStateless *node, pkt_dir_t dir, char *raw_pkt){ - - bool ov_src = stream->get_override_src_mac_by_pkt_data(); - TrexStream::stream_dst_mac_t ov_dst = stream->get_override_dst_mac_mode(); + bool ov_src = stream->get_override_src_mac_by_pkt_data(); + TrexStream::stream_dst_mac_t ov_dst = stream->get_override_dst_mac_mode(); if ( (ov_src == true) && (ov_dst == TrexStream::stPKT) ) { @@ -835,13 +832,11 @@ void TrexStatelessDpCore::update_mac_addr(TrexStream * stream, return; } - TRexPortAttr *port_attr = get_stateless_obj()->get_platform_api()->getPortAttrObj(node->get_port_id()); - - /* take from cfg_file */ + /* take from cfg_file */ if ( (ov_src == false) && (ov_dst == TrexStream::stCFG_FILE) ){ - - port_attr->update_src_dst_mac((uint8_t *)raw_pkt); + + m_core->m_node_gen.m_v_if->update_mac_addr_from_global_cfg(dir,(uint8_t*)raw_pkt); return; } @@ -849,8 +844,8 @@ void TrexStatelessDpCore::update_mac_addr(TrexStream * stream, char tmp_pkt[12]; memcpy(tmp_pkt,raw_pkt,12); - port_attr->update_src_dst_mac((uint8_t *)raw_pkt); - + m_core->m_node_gen.m_v_if->update_mac_addr_from_global_cfg(dir,(uint8_t*)raw_pkt); + if ((ov_src == true) && (ov_dst == TrexStream::stCFG_FILE)) { memcpy(raw_pkt+6,tmp_pkt+6,6); } diff --git a/src/trex_port_attr.cpp b/src/trex_port_attr.cpp index 08e151b6..26199e33 100644 --- a/src/trex_port_attr.cpp +++ b/src/trex_port_attr.cpp @@ -20,6 +20,12 @@ limitations under the License. const uint8_t DestAttr::g_dummy_mac[6] = {0x0,0x0,0x0,0x1,0x0,0x0}; +DestAttr::DestAttr(uint8_t port_id) { + m_port_id = port_id; + + m_mac = CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest; +} + const uint8_t * TRexPortAttr::get_src_mac() const { return CGlobalInfo::m_options.get_src_mac_addr(m_port_id); diff --git a/src/trex_port_attr.h b/src/trex_port_attr.h index 68f9f82e..eb7c85de 100755 --- a/src/trex_port_attr.h +++ b/src/trex_port_attr.h @@ -24,6 +24,7 @@ limitations under the License. #include "common/basic_utils.h" #include #include "trex_stateless_rx_defs.h" +#include /** * destination port attribute @@ -34,10 +35,7 @@ private: static const uint8_t g_dummy_mac[6]; public: - DestAttr() { - /* use a dummy MAC as default */ - set_dest_mac(g_dummy_mac); - } + DestAttr(uint8_t port_id); enum dest_type_e { DEST_TYPE_IPV4 = 1, @@ -64,6 +62,7 @@ public: m_src_ipv4 = ipv4; memcpy(m_mac, mac, 6); m_type = DEST_TYPE_IPV4; + } /** @@ -71,6 +70,7 @@ public: * */ void set_dest_mac(const uint8_t *mac) { + m_src_ipv4 = 0; memcpy(m_mac, mac, 6); m_type = DEST_TYPE_MAC; @@ -143,15 +143,16 @@ public: private: uint32_t m_src_ipv4; - uint8_t m_mac[6]; + uint8_t *m_mac; dest_type_e m_type; + uint8_t m_port_id; }; class TRexPortAttr { public: - TRexPortAttr() { + TRexPortAttr(uint8_t port_id) : m_dest(port_id) { m_src_ipv4 = 0; } @@ -237,7 +238,7 @@ protected: class DpdkTRexPortAttr : public TRexPortAttr { public: - DpdkTRexPortAttr(uint8_t port_id, bool is_virtual, bool fc_change_allowed) { + DpdkTRexPortAttr(uint8_t port_id, bool is_virtual, bool fc_change_allowed) : TRexPortAttr(port_id) { m_port_id = port_id; m_rx_filter_mode = RX_FILTER_MODE_HW; @@ -288,7 +289,7 @@ private: class SimTRexPortAttr : public TRexPortAttr { public: - SimTRexPortAttr() { + SimTRexPortAttr() : TRexPortAttr(0) { m_link.link_speed = 10000; m_link.link_duplex = 1; m_link.link_autoneg = 0; -- cgit 1.2.3-korg