From 6e1919c3aebabc0977a8ab40b5c60cbd0e7114d0 Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 13 Nov 2016 17:17:36 +0200 Subject: RX features - pre-resolve stage Signed-off-by: imarom --- .../stl/trex_stl_lib/trex_stl_client.py | 132 ++++++++--------- .../stl/trex_stl_lib/trex_stl_port.py | 156 +++++++++++++++++---- .../stl/trex_stl_lib/trex_stl_stats.py | 8 +- .../stl/trex_stl_lib/utils/common.py | 5 + .../stl/trex_stl_lib/utils/parsing_opts.py | 33 ++++- src/common/basic_utils.cpp | 13 ++ src/common/basic_utils.h | 1 + src/main_dpdk.cpp | 39 +++++- src/rpc-server/commands/trex_rpc_cmd_general.cpp | 111 ++++++++++++--- src/rpc-server/commands/trex_rpc_cmds.h | 3 + src/trex_port_attr.h | 149 +++++++++++++++++--- 11 files changed, 501 insertions(+), 149 deletions(-) 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 d4d09cd7..2ee5225c 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 @@ -12,7 +12,7 @@ from .trex_stl_types import * from .trex_stl_async_client import CTRexAsyncClient from .utils import parsing_opts, text_tables, common -from .utils.common import list_intersect, list_difference, is_sub_list, PassiveTimer, is_valid_ipv4 +from .utils.common import list_intersect, list_difference, is_sub_list, PassiveTimer, is_valid_ipv4, is_valid_mac from .utils.text_opts import * from functools import wraps @@ -326,24 +326,22 @@ class EventsHandler(object): elif (event_type == 8): port_id = int(data['port_id']) - - if data['attr'] == self.client.ports[port_id].attr: - return # false alarm - - old_info = self.client.ports[port_id].get_formatted_info(sync = False) - self.__async_event_port_attr_changed(port_id, data['attr']) - - new_info = self.client.ports[port_id].get_formatted_info(sync = False) + + diff = self.__async_event_port_attr_changed(port_id, data['attr']) + if not diff: + return + + ev = "port {0} attributes changed".format(port_id) - for key, old_val in old_info.items(): - new_val = new_info.get(key, 'N/A') - if old_val != new_val: - ev += '\n {key}: {old} -> {new}'.format( + for key, (old_val, new_val) in diff.items(): + ev += '\n {key}: {old} -> {new}'.format( key = key, old = old_val.lower() if type(old_val) is str else old_val, new = new_val.lower() if type(new_val) is str else new_val) + show_event = True - + + # server stopped elif (event_type == 100): @@ -399,7 +397,7 @@ class EventsHandler(object): def __async_event_port_attr_changed (self, port_id, attr): if port_id in self.client.ports: - self.client.ports[port_id].async_event_port_attr_changed(attr) + return self.client.ports[port_id].async_event_port_attr_changed(attr) # add event to log def __add_event_log (self, origin, ev_type, msg, show = False): @@ -812,7 +810,7 @@ class STLClient(object): rc = RC() for port_id, port_attr_dict in zip(port_id_list, attr_dict): - rc.add(self.ports[port_id].set_attr(port_attr_dict)) + rc.add(self.ports[port_id].set_attr(**port_attr_dict)) return rc @@ -1772,10 +1770,19 @@ class STLClient(object): raise STLError(rc) def test (self): + #rc = self.ports[0].resolve() + #if not rc: + # raise STLError(rc) + #return - self.reset(ports = [0, 1]) + self.reset(ports = [0]) - self.set_port_attr(ports = [0, 1], ipv4 = ['5.5.5.5', '6.6.6.6']) + attr = self.ports[0].get_ts_attr() + src_ipv4 = attr['src_ipv4'] + src_mac = attr['src_mac'] + dest = attr['dest'] + print(src_ipv4, src_mac, dest) + #self.set_port_attr(ports = [0, 1], ipv4 = ['5.5.5.5', '6.6.6.6']) return self.set_rx_queue(ports = [0], size = 1000, rxf = 'all') @@ -2727,7 +2734,7 @@ class STLClient(object): flow_ctrl = None, rxf = None, ipv4 = None, - default_gateway = None, + dest = None, ): """ Set port attributes @@ -2740,8 +2747,8 @@ class STLClient(object): rxf - 'hw' for hardware rules matching packets only or 'all' all packets ipv4 - configure IPv4 address for port(s). for multiple ports should be a list of IPv4 addresses in the same length of the ports array - default_gateway - configure default gateway for port(s). for multiple ports should be a list - 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 :raises: + :exe:'STLError' @@ -2759,54 +2766,39 @@ class STLClient(object): # common attributes for all ports cmn_attr_dict = {} - if promiscuous is not None: - cmn_attr_dict['promiscuous'] = {'enabled': promiscuous} - - if link_up is not None: - cmn_attr_dict['link_status'] = {'up': link_up} - - if led_on is not None: - cmn_attr_dict['led_status'] = {'on': led_on} - - if flow_ctrl is not None: - cmn_attr_dict['flow_ctrl_mode'] = {'mode': flow_ctrl} - - if rxf is not None: - cmn_attr_dict['rx_filter_mode'] = {'mode': rxf} + cmn_attr_dict['promiscuous'] = promiscuous + cmn_attr_dict['link_status'] = link_up + cmn_attr_dict['led_status'] = led_on + cmn_attr_dict['flow_ctrl_mode'] = flow_ctrl + cmn_attr_dict['rx_filter_mode'] = rxf + # each port starts with a set of the common attributes attr_dict = [dict(cmn_attr_dict) for _ in ports] + + # default value for IPv4 / dest is none for all ports + if ipv4 is None: + ipv4 = [None] * len(ports) + if dest is None: + dest = [None] * len(ports) - # IPv4 - if ipv4 is not None: - ipv4_list = listify(ipv4) - - if len(ipv4_list) != len(ports): - raise STLError("'ipv4' must be a list in the same length of ports - 'ports': {0}, 'ip': {1}".format(ports, ipv4_list)) + ipv4 = listify(ipv4) + if len(ipv4) != len(ports): + raise STLError("'ipv4' must be a list in the same length of ports - 'ports': {0}, 'ip': {1}".format(ports, ipv4)) - for ipv4, port_attr in zip(ipv4_list, attr_dict): - if not is_valid_ipv4(ipv4): - raise STLError("invalid IPv4 address provided: '{0}'".format(ipv4)) - port_attr['ipv4'] = {'addr': ipv4} - + dest = listify(dest) + if len(dest) != len(ports): + raise STLError("'dest' must be a list in the same length of ports - 'ports': {0}, 'dest': {1}".format(ports, dest)) - # default gateway - if default_gateway is not None: - dg_list = listfy(default_gateway) + # update each port attribute with ipv4 + for addr, port_attr in zip(ipv4, attr_dict): + port_attr['ipv4'] = addr + + # update each port attribute with dest + for addr, port_attr in zip(dest, attr_dict): + port_attr['dest'] = addr - if len(dg_list) != len(ports): - raise STLError("'default_gateway' must be a list in the same length of ports - 'ports': {0}, 'default_gateway': {1}".format(ports, dg_list)) - - for dg, port_attr in zip(dg_list, attr_dict): - if not is_valid_ipv4(dg): - raise STLError("invalid IPv4 address provided: '{0}'".format(ipv4)) - port_attr['default_gateway'] = {'addr': dg} - - - # no attributes to set - if not any(attr_dict): - return - + 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) @@ -2815,7 +2807,6 @@ class STLClient(object): raise STLError(rc) - @__api_check(True) def set_rx_sniffer (self, ports = None, base_filename = 'rx_capture', limit = 1000, rxf = None): """ @@ -3515,6 +3506,8 @@ class STLClient(object): parsing_opts.FLOW_CTRL, parsing_opts.SUPPORTED, parsing_opts.RX_FILTER_MODE, + parsing_opts.IPV4, + parsing_opts.DEST ) opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True) @@ -3527,12 +3520,12 @@ class STLClient(object): opts.flow_ctrl = parsing_opts.FLOW_CTRL_DICT.get(opts.flow_ctrl) # if no attributes - fall back to printing the status - if not list(filter(lambda x:x is not None, [opts.prom, opts.link, opts.led, opts.flow_ctrl, opts.supp, opts.rx_filter_mode])): + if not list(filter(lambda x:x is not None, [opts.prom, opts.link, opts.led, opts.flow_ctrl, opts.supp, opts.rx_filter_mode, opts.ipv4, opts.dest])): self.show_stats_line("--ps --port {0}".format(' '.join(str(port) for port in opts.ports))) return if opts.supp: - info = self.ports[0].get_info() # assume for now all ports are same + info = self.ports[0].get_formatted_info() # assume for now all ports are same print('') print('Supported attributes for current NICs:') print(' Promiscuous: yes') @@ -3541,7 +3534,14 @@ 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) + return self.set_port_attr(opts.ports, + opts.prom, + opts.link, + opts.led, + opts.flow_ctrl, + opts.rx_filter_mode, + opts.ipv4, + opts.dest) 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 418ee5a6..3fd00391 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 @@ -10,6 +10,7 @@ from .utils.constants import FLOW_CTRL_DICT_REVERSED import base64 import copy from datetime import datetime, timedelta +import threading StreamOnPort = namedtuple('StreamOnPort', ['compiled_stream', 'metadata']) @@ -50,7 +51,9 @@ class Port(object): def __init__ (self, port_id, user, comm_link, session_id, info): self.port_id = port_id + self.state = self.STATE_IDLE + self.handler = None self.comm_link = comm_link self.transmit = comm_link.transmit @@ -63,7 +66,7 @@ class Port(object): self.profile = None self.session_id = session_id self.status = {} - self.attr = {} + self.__attr = {} self.port_stats = trex_stl_stats.CPortStats(self) @@ -74,6 +77,8 @@ class Port(object): self.owner = '' self.last_factor_type = None + self.attr_lock = threading.Lock() + # decorator to verify port is up def up(func): def func_wrapper(*args, **kwargs): @@ -88,7 +93,7 @@ class Port(object): # owned def owned(func): - def func_wrapper(*args): + def func_wrapper(*args, **kwargs): port = args[0] if not port.is_up(): @@ -97,7 +102,7 @@ class Port(object): if not port.is_acquired(): return port.err("{0} - port is not owned".format(func.__name__)) - return func(*args) + return func(*args, **kwargs) return func_wrapper @@ -129,10 +134,10 @@ class Port(object): return RC_OK(data) def get_speed_bps (self): - return (self.attr['speed'] * 1000 * 1000 * 1000) + return (self.__attr['speed'] * 1000 * 1000 * 1000) def get_formatted_speed (self): - return "%g Gb/s" % (self.attr['speed'] / 1000) + return "%g Gb/s" % (self.__attr['speed'] / 1000) def is_acquired(self): return (self.handler != None) @@ -253,7 +258,8 @@ class Port(object): self.status = rc.data() - self.attr = rc.data()['attr'] + # replace the attributes in a thread safe manner + self.set_ts_attr(rc.data()['attr']) return self.ok() @@ -645,19 +651,44 @@ class Port(object): @owned - def set_attr (self, attr_dict): + def set_attr (self, **kwargs): + + json_attr = {} + + if kwargs.get('promiscuous') is not None: + json_attr['promiscuous'] = {'enabled': kwargs.get('promiscuous')} + + if kwargs.get('link_up') is not None: + json_attr['link_status'] = {'up': kwargs.get('link_up')} + + if kwargs.get('led_on') is not None: + json_attr['led_status'] = {'on': kwargs.get('led_on')} + + if kwargs.get('flow_ctrl_mode') is not None: + json_attr['flow_ctrl_mode'] = {'on': kwargs.get('flow_ctrl_mode')} + + if kwargs.get('rx_filter_mode') is not None: + json_attr['rx_filter_mode'] = {'mode': kwargs.get('rx_filter_mode')} + + if kwargs.get('ipv4') is not None: + json_attr['ipv4'] = {'addr': kwargs.get('ipv4')} + + if kwargs.get('dest') is not None: + json_attr['dest'] = {'addr': kwargs.get('dest')} + params = {"handler": self.handler, "port_id": self.port_id, - "attr": attr_dict} + "attr": json_attr} rc = self.transmit("set_port_attr", params) if rc.bad(): return self.err(rc.err()) - return self.ok() - + # update the dictionary from the server explicitly + return self.sync() + @writeable def push_remote (self, pcap_filename, ipg_usec, speedup, count, duration, is_dual, slave_handler): @@ -730,7 +761,8 @@ class Port(object): if sync: self.sync() - attr = self.attr + # get a copy of the current attribute set (safe against manipulation) + attr = self.get_ts_attr() info = dict(self.info) @@ -781,15 +813,35 @@ class Port(object): if 'rx_filter_mode' in attr: - info['rx_filter_mode'] = 'Hardware Match' if attr['rx_filter_mode'] == 'hw' else 'Fetch All' + info['rx_filter_mode'] = 'hardware match' if attr['rx_filter_mode'] == 'hw' else 'fetch all' else: info['rx_filter_mode'] = 'N/A' + # src MAC and IPv4 + info['src_mac'] = attr.get('src_mac', 'N/A') + + info['src_ipv4'] = attr.get('src_ipv4', 'N/A') + if info['src_ipv4'] == 'none': + info['src_ipv4'] = 'Not Configured' + + # dest + dest = attr.get('dest', {}) + info['dest'] = dest.get('addr', 'N/A') + + if dest['type'] == 'mac': + info['arp'] = '-' + else: + info['arp'] = dest.get('arp', 'N/A') + + + if info['dest'] == 'none': + info['dest'] = 'Not Configured' + + + if info['arp'] == 'none': + info['arp'] = 'unresolved' + - info['mac_addr'] = attr.get('mac_addr', 'N/A') - info['ipv4'] = attr.get('ipv4', 'N/A') - info['default_gateway'] = attr.get('default_gateway', 'N/A') - info['next_hop_mac'] = attr.get('next_hop_mac', 'N/A') # RX info rx_info = self.status['rx_info'] @@ -816,6 +868,17 @@ class Port(object): def get_port_state_name(self): return self.STATES_MAP.get(self.state, "Unknown") + def get_src_ipv4 (self): + src_ipv4 = self.__attr['src_ipv4'] + if src_ipv4 == 'none': + src_ipv4 = None + + return src_ipv4 + + def get_src_mac (self): + return self.__attr['src_mac'] + + ################# stats handler ###################### def generate_port_stats(self): return self.port_stats.generate_stats() @@ -824,14 +887,14 @@ class Port(object): info = self.get_formatted_info() - return {"driver": info['driver'], - "description": info.get('description', 'N/A')[:18], - "MAC addr": info['mac_addr'], - "Next hop MAC": info['next_hop_mac'], - "IPv4": info['ipv4'], - "Default gateway": info['default_gateway'], - "PCI Address": info['pci_addr'], - "NUMA Node": info['numa'], + return {"driver": info['driver'], + "description": info.get('description', 'N/A')[:18], + "src MAC": info['src_mac'], + "src IPv4": info['src_ipv4'], + "Destination": info['dest'], + "ARP Resolution": info['arp'], + "PCI Address": info['pci_addr'], + "NUMA Node": info['numa'], "--": "", "---": "", "----": "", @@ -881,8 +944,20 @@ class Port(object): return {"streams" : OrderedDict(sorted(data.items())) } - - + + ######## attributes are a complex type (dict) that might be manipulated through the async thread ############# + + # get in a thread safe manner a duplication of attributes + def get_ts_attr (self): + with self.attr_lock: + return dict(self.__attr) + + # set in a thread safe manner a new dict of attributes + def set_ts_attr (self, new_attr): + with self.attr_lock: + self.__attr = new_attr + + ################# events handler ###################### def async_event_port_job_done (self): # until thread is locked - order is important @@ -890,8 +965,33 @@ class Port(object): self.state = self.STATE_STREAMS self.last_factor_type = None - def async_event_port_attr_changed (self, attr): - self.attr = attr + def async_event_port_attr_changed (self, new_attr): + + # get a thread safe duplicate + cur_attr = self.get_ts_attr() + + # check if anything changed + if new_attr == cur_attr: + return None + + # generate before + before = self.get_formatted_info(sync = False) + + # update + self.set_ts_attr(new_attr) + + # generate after + after = self.get_formatted_info(sync = False) + + # return diff + diff = {} + for key, new_value in after.items(): + old_value = before.get(key, 'N/A') + if new_value != old_value: + diff[key] = (old_value, new_value) + + return diff + # rest of the events are used for TUI / read only sessions def async_event_port_stopped (self): diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py index 55620689..f0293016 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py @@ -670,11 +670,11 @@ class CTRexInfoGenerator(object): ("promiscuous", []), ("flow ctrl", []), ("--", []), - ("MAC addr", []), - ("Next hop MAC", []), + ("src IPv4", []), + ("src MAC", []), ("---", []), - ("IPv4", []), - ("Default gateway", []), + ("Destination", []), + ("ARP Resolution", []), ("----", []), ("PCI Address", []), ("NUMA Node", []), 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 f0da9a08..d4ac973d 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 @@ -4,6 +4,7 @@ import string import random import time import socket +import re try: import pwd @@ -93,3 +94,7 @@ def is_valid_ipv4 (addr): return True except socket.error: return False + +def is_valid_mac (mac): + return bool(re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", mac.lower())) + 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 b93a797d..80260d4a 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 @@ -1,6 +1,6 @@ import argparse from collections import namedtuple, OrderedDict -from .common import list_intersect, list_difference +from .common import list_intersect, list_difference, is_valid_ipv4, is_valid_mac from .text_opts import format_text from ..trex_stl_types import * from .constants import ON_OFF_DICT, UP_DOWN_DICT, FLOW_CTRL_DICT @@ -45,12 +45,14 @@ FLOW_CTRL = 28 SUPPORTED = 29 RX_FILTER_MODE = 30 - OUTPUT_FILENAME = 31 ALL_FILES = 32 LIMIT = 33 PORT_RESTART = 34 +IPV4 = 35 +DEST = 36 + GLOBAL_STATS = 50 PORT_STATS = 51 PORT_STATUS = 52 @@ -224,8 +226,20 @@ def is_valid_file(filename): return filename +def check_ipv4_addr (ipv4_str): + if not is_valid_ipv4(ipv4_str): + raise argparse.ArgumentTypeError("invalid IPv4 address: '{0}'".format(ipv4_str)) + + return ipv4_str + +def check_dest_addr (addr): + if not (is_valid_ipv4(addr) or is_valid_mac(addr)): + raise argparse.ArgumentTypeError("not a valid IPv4 or MAC address: '{0}'".format(addr)) + + return addr + def decode_tunables (tunable_str): tunables = {} @@ -316,6 +330,21 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], 'choices': ['hw', 'all']}), + IPV4: ArgumentPack(['--ipv4'], + {'help': 'IPv4 address(s) for the port(s)', + 'dest': 'ipv4', + 'nargs': '+', + 'default': None, + 'type': check_ipv4_addr}), + + DEST: ArgumentPack(['--dest'], + {'help': 'Destination address(s) for the port(s) in either IPv4 or MAC format', + 'dest': 'dest', + 'nargs': '+', + 'default': None, + 'type': check_dest_addr}), + + OUTPUT_FILENAME: ArgumentPack(['-o', '--output'], {'help': 'Output PCAP filename', 'dest': 'output_filename', diff --git a/src/common/basic_utils.cpp b/src/common/basic_utils.cpp index 52988131..dfd3b183 100755 --- a/src/common/basic_utils.cpp +++ b/src/common/basic_utils.cpp @@ -201,6 +201,19 @@ std::string utl_macaddr_to_str(const uint8_t *macaddr) { return tmp; } +bool utl_str_to_macaddr(const std::string &s, uint8_t *mac) { + int last = -1; + int rc = sscanf(s.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%n", + mac + 0, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5, + &last); + + if ( (rc != 6) || (s.size() != last) ) { + return false; + } + + return true; +} + /** * generate a random connection handler * diff --git a/src/common/basic_utils.h b/src/common/basic_utils.h index fdbd2f08..ab0ff1ec 100755 --- a/src/common/basic_utils.h +++ b/src/common/basic_utils.h @@ -87,6 +87,7 @@ bool utl_is_file_exists (const std::string& name) ; void utl_macaddr_to_str(const uint8_t *macaddr, std::string &output); std::string utl_macaddr_to_str(const uint8_t *macaddr); +bool utl_str_to_macaddr(const std::string &s, uint8_t *mac); std::string utl_generate_random_str(unsigned int &seed, int len); diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index 406b9c20..8f887006 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -1604,12 +1604,20 @@ bool DpdkTRexPortAttr::update_link_status_nowait(){ rte_eth_link new_link; bool changed = false; rte_eth_link_get_nowait(m_port_id, &new_link); + + /* if the link got down - update the dest atribute to move to unresolved */ + if (new_link.link_status != m_link.link_status) { + get_dest().on_link_down(); + changed = true; + } + + /* other changes */ if (new_link.link_speed != m_link.link_speed || new_link.link_duplex != m_link.link_duplex || - new_link.link_autoneg != m_link.link_autoneg || - new_link.link_status != m_link.link_status) { + new_link.link_autoneg != m_link.link_autoneg) { changed = true; } + m_link = new_link; return changed; } @@ -3101,8 +3109,7 @@ void CGlobalTRex::pre_test() { exit(1); } memcpy(CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, mac, ETHER_ADDR_LEN); - m_ports[port_id].get_port_attr()->set_next_hop_mac(mac); - + // if port is connected in loopback, no need to send gratuitous ARP. It will only confuse our ingress counters. if (pretest.is_loopback(port_id)) CGlobalInfo::m_options.m_ip_cfg[port_id].set_grat_arp_needed(false); @@ -3115,6 +3122,18 @@ void CGlobalTRex::pre_test() { // Configure port back to normal mode. Only relevant packets handled by software. CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(pif, false); + + + /* set resolved IPv4 */ + uint32_t dg = CGlobalInfo::m_options.m_ip_cfg[port_id].get_def_gw(); + const uint8_t *dst_mac = CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest; + if (dg) { + m_ports[port_id].get_port_attr()->get_dest().set_dest_ipv4(dg, dst_mac); + } else { + m_ports[port_id].get_port_attr()->get_dest().set_dest_mac(dst_mac); + } + + } } @@ -4680,8 +4699,16 @@ bool CPhyEthIF::Create(uint8_t portid) { m_port_attr = g_trex.m_drv->create_port_attr(portid); - m_port_attr->set_ipv4(CGlobalInfo::m_options.m_ip_cfg[m_port_id].get_ip()); - m_port_attr->set_default_gateway(CGlobalInfo::m_options.m_ip_cfg[m_port_id].get_def_gw()); + uint32_t src_ipv4 = CGlobalInfo::m_options.m_ip_cfg[m_port_id].get_ip(); + if (src_ipv4) { + m_port_attr->set_src_ipv4(src_ipv4); + } + + /* for now set as unresolved IPv4 destination */ + uint32_t dest_ipv4 = CGlobalInfo::m_options.m_ip_cfg[m_port_id].get_def_gw(); + if (dest_ipv4) { + m_port_attr->get_dest().set_dest_ipv4(dest_ipv4); + } return true; } diff --git a/src/rpc-server/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/commands/trex_rpc_cmd_general.cpp index 5ee853b8..8d61ecba 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp @@ -368,29 +368,71 @@ TrexRpcCmdSetPortAttr::parse_ipv4(const Json::Value &msg, uint8_t port_id, Json: generate_parse_err(result, ss.str()); } - get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id)->set_ipv4(ipv4_addr); + get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id)->set_src_ipv4(ipv4_addr); + return (0); +} + +int +TrexRpcCmdSetPortAttr::parse_dest(const Json::Value &msg, uint8_t port_id, Json::Value &result) { + + /* can be either IPv4 or MAC */ + const std::string addr = parse_string(msg, "addr", result); + + TRexPortAttr *port_attr = get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id); + + /* try IPv4 */ + uint32_t ipv4_addr; + 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 << "'"; + generate_parse_err(result, ss.str()); + } + + return (0); } /** - * set port commands - * - * @author imarom (24-Feb-16) - * - * @param params - * @param result - * - * @return trex_rpc_cmd_rc_e + * attributes in the high priority pass must be handled first + * for example, IPv4 configuration should be handled before dest + * */ -trex_rpc_cmd_rc_e -TrexRpcCmdSetPortAttr::_run(const Json::Value ¶ms, Json::Value &result) { +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)); + } + } +} - 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 */ for (const std::string &name : attr.getMemberNames()) { @@ -407,24 +449,30 @@ TrexRpcCmdSetPortAttr::_run(const Json::Value ¶ms, Json::Value &result) { else if (name == "led_status") { bool on = parse_bool(attr[name], "on", result); ret = get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id)->set_led(on); - } + } else if (name == "flow_ctrl_mode") { int mode = parse_int(attr[name], "mode", result); ret = get_stateless_obj()->get_platform_api()->getPortAttrObj(port_id)->set_flow_ctrl(mode); - } + } else if (name == "rx_filter_mode") { - ret = parse_rx_filter_mode(attr[name], port_id, result); + const Json::Value &rx = parse_object(attr, name, result); + ret = parse_rx_filter_mode(rx, port_id, result); } else if (name == "ipv4") { - ret = parse_ipv4(attr[name], port_id, result); + /* ignore - was already taken care of in the high priority pass */ } - + + else if (name == "dest") { + const Json::Value &dest = parse_object(attr, name, result); + ret = parse_dest(dest, port_id, result); + } + /* unknown attribute */ else { - generate_execute_err(result, "Not recognized attribute: " + name); + generate_execute_err(result, "unknown attribute type: '" + name + "'"); break; } @@ -435,9 +483,32 @@ TrexRpcCmdSetPortAttr::_run(const Json::Value ¶ms, Json::Value &result) { 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 49610eb8..9fb4d551 100644 --- a/src/rpc-server/commands/trex_rpc_cmds.h +++ b/src/rpc-server/commands/trex_rpc_cmds.h @@ -95,8 +95,11 @@ TREX_RPC_CMD_DEFINE(TrexRpcCmdGetPortXStatsNames, "get_port_xstats_names", 1, 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/trex_port_attr.h b/src/trex_port_attr.h index ccf12ae2..cdbb466c 100755 --- a/src/trex_port_attr.h +++ b/src/trex_port_attr.h @@ -23,13 +23,119 @@ limitations under the License. #include "trex_defs.h" #include "common/basic_utils.h" +/** + * destination port attribute + * + */ +class DestAttr { +public: + + DestAttr() { + /* use a dummy MAC as default */ + uint8_t dummy_mac [] = {0xff,0xff,0xff,0xff,0xff,0xff}; + set_dest_mac(dummy_mac); + } + + enum dest_type_e { + DEST_TYPE_IPV4 = 1, + DEST_TYPE_MAC = 2 + }; + + /** + * set dest as an IPv4 unresolved + */ + void set_dest_ipv4(uint32_t ipv4) { + assert(ipv4 != 0); + + m_src_ipv4 = ipv4; + memset(m_mac, 0, 6); + m_type = DEST_TYPE_IPV4; + } + + /** + * set dest as a resolved IPv4 + */ + void set_dest_ipv4(uint32_t ipv4, const uint8_t *mac) { + assert(ipv4 != 0); + + m_src_ipv4 = ipv4; + memcpy(m_mac, mac, 6); + m_type = DEST_TYPE_IPV4; + } + + /** + * dest dest as MAC + * + */ + void set_dest_mac(const uint8_t *mac) { + m_src_ipv4 = 0; + memcpy(m_mac, mac, 6); + m_type = DEST_TYPE_MAC; + } + + + bool is_resolved() const { + if (m_type == DEST_TYPE_MAC) { + return true; + } + + for (int i = 0; i < 6; i++) { + if (m_mac[i] != 0) { + return true; + } + } + + /* all zeroes - non resolved */ + return false; + } + + /** + * when link gets down - this should be called + * + */ + void on_link_down() { + if (m_type == DEST_TYPE_IPV4) { + /* reset the IPv4 dest with no resolution */ + set_dest_ipv4(m_src_ipv4); + } + } + + void to_json(Json::Value &output) { + switch (m_type) { + + case DEST_TYPE_IPV4: + output["type"] = "ipv4"; + output["addr"] = utl_uint32_to_ipv4(m_src_ipv4); + if (is_resolved()) { + output["arp"] = utl_macaddr_to_str(m_mac); + } else { + output["arp"] = "none"; + } + break; + + case DEST_TYPE_MAC: + output["type"] = "mac"; + output["addr"] = utl_macaddr_to_str(m_mac); + break; + + default: + assert(0); + } + + } + +private: + uint32_t m_src_ipv4; + uint8_t m_mac[6]; + dest_type_e m_type; +}; + + class TRexPortAttr { public: TRexPortAttr() { - m_ipv4 = 0; - m_default_gateway = 0; - memset(m_next_hop_mac, 0, sizeof(m_next_hop_mac)); + m_src_ipv4 = 0; } virtual ~TRexPortAttr(){} @@ -57,9 +163,8 @@ public: virtual bool is_link_change_supported() { return flag_is_link_change_supported; } virtual void get_description(std::string &description) { description = intf_info_st.description; } virtual void get_supported_speeds(supp_speeds_t &supp_speeds) = 0; - uint32_t get_ipv4() {return m_ipv4;} - uint32_t get_default_gateway() {return m_default_gateway;} - const uint8_t * get_next_hop_mac() {return m_next_hop_mac;} + uint32_t get_src_ipv4() {return m_src_ipv4;} + DestAttr & get_dest() {return m_dest;} virtual std::string get_rx_filter_mode() { switch (m_rx_filter_mode) { @@ -81,16 +186,8 @@ public: virtual int set_led(bool on) = 0; virtual int set_rx_filter_mode(rx_filter_mode_e mode) = 0; - void set_ipv4(uint32_t addr) { - m_ipv4 = addr; - } - - void set_default_gateway(uint32_t addr) { - m_default_gateway = addr; - } - - void set_next_hop_mac(const uint8_t *next_hop_mac) { - memcpy(m_next_hop_mac, next_hop_mac, sizeof(m_next_hop_mac)); + void set_src_ipv4(uint32_t addr) { + m_src_ipv4 = addr; } /* DUMPS */ @@ -104,28 +201,34 @@ public: uint8_t mac_addr[6]; memcpy(mac_addr, dpdk_mac_addr.addr_bytes, 6); - output["mac_addr"] = utl_macaddr_to_str(mac_addr); - output["next_hop_mac"] = utl_macaddr_to_str(m_next_hop_mac); + output["src_mac"] = utl_macaddr_to_str(mac_addr); output["promiscuous"]["enabled"] = get_promiscuous(); output["link"]["up"] = is_link_up(); output["speed"] = get_link_speed(); output["rx_filter_mode"] = get_rx_filter_mode(); - output["ipv4"] = utl_uint32_to_ipv4(get_ipv4()); - output["default_gateway"] = utl_uint32_to_ipv4(get_default_gateway()); + + if (get_src_ipv4() != 0) { + output["src_ipv4"] = utl_uint32_to_ipv4(get_src_ipv4()); + } else { + output["src_ipv4"] = "none"; + } + int mode; get_flow_ctrl(mode); output["fc"]["mode"] = mode; + m_dest.to_json(output["dest"]); + } protected: uint8_t m_port_id; rte_eth_link m_link; - uint32_t m_ipv4; - uint32_t m_default_gateway; - uint8_t m_next_hop_mac[6]; + uint32_t m_src_ipv4; + DestAttr m_dest; + struct rte_eth_dev_info dev_info; rx_filter_mode_e m_rx_filter_mode; -- cgit 1.2.3-korg