summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorimarom <imarom@cisco.com>2016-11-13 17:17:36 +0200
committerimarom <imarom@cisco.com>2016-11-13 17:17:36 +0200
commit6e1919c3aebabc0977a8ab40b5c60cbd0e7114d0 (patch)
tree502aa78527ee4de1723e4a32c291dfcdb71cea23
parentab28fccc187c6134eeb0400ce0b113a77e498bb2 (diff)
RX features - pre-resolve stage
Signed-off-by: imarom <imarom@cisco.com>
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py132
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py156
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py8
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py5
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py33
-rwxr-xr-xsrc/common/basic_utils.cpp13
-rwxr-xr-xsrc/common/basic_utils.h1
-rw-r--r--src/main_dpdk.cpp39
-rw-r--r--src/rpc-server/commands/trex_rpc_cmd_general.cpp111
-rw-r--r--src/rpc-server/commands/trex_rpc_cmds.h3
-rwxr-xr-xsrc/trex_port_attr.h149
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 &params, 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 &params, 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 &params, 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 &params, 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;