summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py155
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py14
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py358
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py255
-rw-r--r--src/main_dpdk.cpp6
-rw-r--r--src/rpc-server/commands/trex_rpc_cmd_general.cpp6
-rw-r--r--src/trex_port_attr.cpp72
-rwxr-xr-xsrc/trex_port_attr.h104
8 files changed, 473 insertions, 497 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 546298ce..c72244a6 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, is_valid_mac, list_remove_dup
+from .utils.common import *
from .utils.text_opts import *
from functools import wraps
@@ -1793,61 +1793,15 @@ class STLClient(object):
if not rc:
raise STLError(rc)
- def test (self):
- self.resolve()
- return
-
- #rc = self.ports[0].resolve()
- #if not rc:
- # raise STLError(rc)
- #return
-
- self.reset(ports = [0])
-
- 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')
-
- #base_pkt = Ether()/ARP()/('x'*50)
- base_pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(psrc = '1.1.1.2',pdst = '1.1.1.1', hwsrc = 'a0:36:9f:20:e6:ce')
- #base_pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ICMP()
-
- print('Sending ARP request on port 0:\n')
- base_pkt.show2()
-
- # send some traffic
- x = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
-
- self.add_streams(streams = [x], ports = [0])
- self.start(ports = [0], mult = '100%')
- self.wait_on_traffic(ports = [0])
- time.sleep(1)
-
- pkts = self.get_rx_queue_pkts(ports = [0])
-
- print('got back on port 0:\n')
- for pkt in pkts[0]:
- Ether(pkt).show2()
-
- self.remove_rx_queue(ports = [1])
- self.set_port_attr(ports = [1], rxf = 'hw')
- #for pkt in pkts[1]:
- # Ether(pkt).show2()
@__api_check(True)
- def ping(self):
+ def ping_rpc_server(self):
"""
- Pings the server
+ Pings the RPC server
:parameters:
- none
+ None
:raises:
+ :exc:`STLError`
@@ -1865,9 +1819,9 @@ class STLClient(object):
@__api_check(True)
- def ip_ping (self, src_port, dst_ipv4, pkt_size = 64, count = 5):
+ def ping_ip (self, src_port, dst_ipv4, pkt_size = 64, count = 5):
"""
- Pings an IP address
+ Pings an IP address through a port
:parameters:
src_port - on which port_id to send the ICMP PING request
@@ -1888,11 +1842,12 @@ class STLClient(object):
with self.logger.supress(level = LoggerApi.VERBOSE_REGULAR_SYNC):
self.logger.log('')
for i in range(count):
- rc = self.ports[src_port].ping(ping_ipv4 = dst_ipv4, pkt_size = pkt_size, retries = 0)
- if rc:
- self.logger.log(rc.data())
- else:
+ rc = self.ports[src_port].ping(ping_ipv4 = dst_ipv4, pkt_size = pkt_size)
+ if not rc:
raise STLError(rc)
+
+ self.logger.log(rc.data())
+
if i != (count - 1):
time.sleep(1)
@@ -2003,18 +1958,33 @@ class STLClient(object):
ports = ports if ports is not None else self.get_all_ports()
ports = self._validate_port_list(ports)
- # force take the port and ignore any streams on it
- self.acquire(ports, force = True, sync_streams = False)
- self.stop(ports, rx_delay_ms = 0)
- self.remove_all_streams(ports)
- self.clear_stats(ports)
- self.set_port_attr(ports,
- promiscuous = False,
- link_up = True if restart else None,
- rxf = 'hw')
- self.remove_rx_sniffer(ports)
- self.remove_rx_queue(ports)
+
+ if restart:
+ self.logger.pre_cmd("Hard resetting ports {0}:".format(ports))
+ else:
+ self.logger.pre_cmd("Resetting ports {0}:".format(ports))
+
+
+ try:
+ with self.logger.supress():
+ # force take the port and ignore any streams on it
+ self.acquire(ports, force = True, sync_streams = False)
+ self.stop(ports, rx_delay_ms = 0)
+ self.remove_all_streams(ports)
+ self.clear_stats(ports)
+ self.set_port_attr(ports,
+ promiscuous = False,
+ link_up = True if restart else None,
+ rxf = 'hw')
+ self.remove_rx_sniffer(ports)
+ self.remove_rx_queue(ports)
+
+ except STLError as e:
+ self.logger.post_cmd(False)
+ raise e
+
+ self.logger.post_cmd(RC_OK())
@__api_check(True)
@@ -2159,11 +2129,11 @@ class STLClient(object):
# 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:
+ if ports_link_down and not force:
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))
+ active_ports = [port_id for port_id in ports if self.ports[port_id].is_active()]
if active_ports and not force:
raise STLError("Port(s) {0} are active - please stop them or specify 'force'".format(active_ports))
@@ -2228,6 +2198,7 @@ class STLClient(object):
validate_type('core_mask', core_mask, (int, list))
+ # some sanity checks before attempting start
self.__pre_start_check(ports, force)
#########################
@@ -2285,7 +2256,7 @@ class STLClient(object):
return
ports = self._validate_port_list(ports)
-
+
self.logger.pre_cmd("Stopping traffic on port(s) {0}:".format(ports))
rc = self.__stop(ports)
self.logger.post_cmd(rc)
@@ -2844,7 +2815,7 @@ class STLClient(object):
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]
@@ -2908,37 +2879,37 @@ class STLClient(object):
if not ports:
raise STLError('No ports configured with destination as IPv4')
- active_ports = list(set(self.get_active_ports()).intersection(ports))
+ ports = self._validate_port_list(ports)
+
+ active_ports = list_intersect(ports, self.get_active_ports())
if active_ports:
raise STLError('Port(s) {0} are active, please stop them before resolving'.format(active_ports))
- ports = self._validate_port_list(ports)
- self.logger.pre_cmd("Resolving destination on port(s) {0}:".format(ports))
+ self.logger.pre_cmd('Resolving destination on port(s) {0}:'.format(ports))
with self.logger.supress():
rc = self.__resolve(ports, retries)
self.logger.post_cmd(rc)
- if rc:
- self.logger.log(rc)
-
if not rc:
raise STLError(rc)
-
+
+ # print the ARP transaction
+ self.logger.log(rc)
+ self.logger.log('')
@__api_check(True)
- def set_rx_sniffer (self, ports = None, base_filename = 'rx_capture', limit = 1000, rxf = None):
+ def set_rx_sniffer (self, ports = None, base_filename = 'rx.pcap', limit = 1000):
"""
- Sets RX sniffer for port(s) written to a PCAP file
+ Sets a RX sniffer for port(s) written to a PCAP file
:parameters:
ports - for which ports to apply a unique sniffer (each port gets a unique file)
- base_filename - filename will be appended with '-<port_number>'
+ base_filename - filename will be appended with '-<port_number>', e.g. rx.pcap --> rx-0.pcap, rx-1.pcap etc.
limit - limit how many packets will be written
- rxf - RX filter mode to use: 'hw' or 'all'
:raises:
+ :exe:'STLError'
@@ -2952,10 +2923,6 @@ class STLClient(object):
if limit <= 0:
raise STLError("'limit' must be a positive value")
- # change RX filter mode if asked
- if rxf:
- self.set_port_attr(ports, rxf = rxf)
-
self.logger.pre_cmd("Setting RX sniffers on port(s) {0}:".format(ports))
rc = self.__set_rx_sniffer(ports, base_filename, limit)
self.logger.post_cmd(rc)
@@ -2987,7 +2954,7 @@ class STLClient(object):
@__api_check(True)
- def set_rx_queue (self, ports = None, size = 1000, rxf = None):
+ def set_rx_queue (self, ports = None, size = 1000):
"""
Sets RX queue for port(s)
The queue is cyclic and will hold last 'size' packets
@@ -2995,7 +2962,6 @@ class STLClient(object):
:parameters:
ports - for which ports to apply a queue
size - size of the queue
- rxf - which RX filter to use on those ports: 'hw' or 'all'
:raises:
+ :exe:'STLError'
@@ -3008,10 +2974,6 @@ class STLClient(object):
if size <= 0:
raise STLError("'size' must be a positive value")
- # change RX filter mode if asked
- if rxf:
- self.set_port_attr(ports, rxf = rxf)
-
self.logger.pre_cmd("Setting RX queue on port(s) {0}:".format(ports))
rc = self.__set_rx_queue(ports, size)
self.logger.post_cmd(rc)
@@ -3112,7 +3074,7 @@ class STLClient(object):
# no parameters - so ping server
if not line:
- self.ping()
+ self.ping_rpc_server()
return True
parser = parsing_opts.gen_parser(self,
@@ -3128,7 +3090,7 @@ class STLClient(object):
return opts
# IP ping
- self.ip_ping(opts.source_port, opts.ping_ipv4, opts.pkt_size, opts.count)
+ self.ping_ip(opts.source_port, opts.ping_ipv4, opts.pkt_size, opts.count)
@__console
@@ -3722,7 +3684,10 @@ class STLClient(object):
rxf = 'all' if opts.all else None
- self.set_rx_sniffer(opts.ports, opts.output_filename, opts.limit, rxf)
+ if rxf:
+ self.set_port_attr(opts.ports, rxf = rxf)
+
+ self.set_rx_sniffer(opts.ports, opts.output_filename, opts.limit)
@__console
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py
index 1461fcec..4ebfa0be 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py
@@ -172,6 +172,10 @@ class JsonRpcClient(object):
self.disconnect()
return RC_ERR("*** [RPC] - Failed to send message to server")
+ except KeyboardInterrupt as e:
+ # must restore the socket to a sane state
+ self.reconnect()
+ raise e
tries = 0
while True:
@@ -184,6 +188,10 @@ class JsonRpcClient(object):
self.disconnect()
return RC_ERR("*** [RPC] - Failed to get server response from {0}".format(self.transport))
+ except KeyboardInterrupt as e:
+ # must restore the socket to a sane state
+ self.reconnect()
+ raise e
return response
@@ -267,12 +275,6 @@ class JsonRpcClient(object):
# connect using current values
return self.connect()
- if not self.connected:
- return RC_ERR("Not connected to server")
-
- # reconnect
- return self.connect(self.server, self.port)
-
def is_connected(self):
return self.connected
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 d225c31c..d0092533 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
@@ -4,17 +4,14 @@ from collections import namedtuple, OrderedDict
from .trex_stl_packet_builder_scapy import STLPktBuilder
from .trex_stl_streams import STLStream, STLTXSingleBurst
from .trex_stl_types import *
+from .trex_stl_rx_features import ARPResolver, PingResolver
from . import trex_stl_stats
from .utils.constants import FLOW_CTRL_DICT_REVERSED
-from scapy.layers.l2 import Ether, ARP
-from scapy.layers.inet import IP, ICMP
-
import base64
import copy
from datetime import datetime, timedelta
import threading
-import time
StreamOnPort = namedtuple('StreamOnPort', ['compiled_stream', 'metadata'])
@@ -70,7 +67,6 @@ class Port(object):
self.profile = None
self.session_id = session_id
self.status = {}
- self.__attr = {}
self.port_stats = trex_stl_stats.CPortStats(self)
@@ -81,6 +77,7 @@ class Port(object):
self.owner = ''
self.last_factor_type = None
+ self.__attr = {}
self.attr_lock = threading.Lock()
# decorator to verify port is up
@@ -100,9 +97,6 @@ class Port(object):
def func_wrapper(*args, **kwargs):
port = args[0]
- if not port.is_up():
- return port.err("{0} - port is down".format(func.__name__))
-
if not port.is_acquired():
return port.err("{0} - port is not owned".format(func.__name__))
@@ -116,9 +110,6 @@ class Port(object):
def func_wrapper(*args, **kwargs):
port = args[0]
- if not port.is_up():
- return port.err("{0} - port is down".format(func.__name__))
-
if not port.is_acquired():
return port.err("{0} - port is not owned".format(func.__name__))
@@ -511,33 +502,36 @@ class Port(object):
return self.ok()
+
@owned
- def set_arp_resolution (self, ipv4, mac):
-
+ def remove_rx_sniffer (self):
params = {"handler": self.handler,
"port_id": self.port_id,
- "ipv4": ipv4,
- "mac": mac}
+ "type": "capture",
+ "enabled": False}
- rc = self.transmit("set_arp_resolution", params)
+ rc = self.transmit("set_rx_feature", params)
if rc.bad():
return self.err(rc.err())
return self.ok()
-
-
+
+
@owned
- def remove_rx_sniffer (self):
+ def set_arp_resolution (self, ipv4, mac):
+
params = {"handler": self.handler,
"port_id": self.port_id,
- "type": "capture",
- "enabled": False}
+ "ipv4": ipv4,
+ "mac": mac}
- rc = self.transmit("set_rx_feature", params)
+ rc = self.transmit("set_arp_resolution", params)
if rc.bad():
return self.err(rc.err())
return self.ok()
+
+
@owned
@@ -579,7 +573,7 @@ class Port(object):
pkts = rc.data()['pkts']
- # decode the packets
+ # decode the packets from base64 to binary
for i in range(len(pkts)):
pkts[i]['binary'] = base64.b64decode(pkts[i]['binary'])
@@ -735,7 +729,7 @@ class Port(object):
dest = self.__attr['dest']
if dest['type'] != 'mac':
- return self.set_attr(dest = dest['addr'])
+ return self.set_attr(dest = dest['ipv4'])
else:
return self.ok()
@@ -851,42 +845,32 @@ class Port(object):
info['src_ipv4'] = 'Not Configured'
# dest
- dest = attr.get('dest', {})
- info['dest'] = dest.get('addr', 'N/A')
-
+ dest = attr['dest']
if dest['type'] == 'mac':
- info['arp'] = '-'
- else:
- info['arp'] = dest.get('arp', 'N/A')
-
-
- if info['dest'] is None:
- info['dest'] = 'Not Configured'
-
+ info['dest'] = dest['mac']
+ info['arp'] = '-'
- if info['arp'] is None:
- info['arp'] = 'unresolved'
-
+ elif dest['type'] == 'ipv4':
+ info['dest'] = dest['ipv4']
+ info['arp'] = dest['arp']
+
+ elif dest['type'] == 'ipv4_u':
+ info['dest'] = dest['ipv4']
+ info['arp'] = 'unresolved'
# RX info
rx_info = self.status['rx_info']
# RX sniffer
- if 'sniffer' in rx_info:
- sniffer = rx_info['sniffer']
- info['rx_sniffer'] = '{0}\n[{1} / {2}]'.format(sniffer['pcap_filename'], sniffer['count'], sniffer['limit']) if sniffer['is_active'] else 'off'
- else:
- info['rx_sniffer'] = 'N/A'
-
+ sniffer = rx_info['sniffer']
+ info['rx_sniffer'] = '{0}\n[{1} / {2}]'.format(sniffer['pcap_filename'], sniffer['count'], sniffer['limit']) if sniffer['is_active'] else 'off'
+
# RX queue
- if 'queue' in rx_info:
- queue = rx_info['queue']
- info['rx_queue'] = '[{0} / {1}]'.format(queue['count'], queue['size']) if queue['is_active'] else 'off'
- else:
- info['rx_queue'] = 'off'
-
+ queue = rx_info['queue']
+ info['rx_queue'] = '[{0} / {1}]'.format(queue['count'], queue['size']) if queue['is_active'] else 'off'
+
return info
@@ -895,7 +879,7 @@ class Port(object):
return self.STATES_MAP.get(self.state, "Unknown")
def get_src_addr (self):
- src_mac = self.__attr['src_mac']
+ src_mac = self.__attr['src_mac']
src_ipv4 = self.__attr['src_ipv4']
return {'mac': src_mac, 'ipv4': src_ipv4}
@@ -904,36 +888,34 @@ class Port(object):
def get_dst_addr (self):
dest = self.__attr['dest']
- dst_ipv4 = None
- dst_mac = None
-
if dest['type'] == 'mac':
- dst_mac = dest['addr']
+ return {'ipv4': None, 'mac': dest['mac']}
+
elif dest['type'] == 'ipv4':
- dst_ipv4 = dest['addr']
- dst_mac = dest['arp']
+ return {'ipv4': dest['ipv4'], 'mac': dest['arp']}
+
+ elif dest['type'] == 'ipv4_u':
+ return {'ipv4': dest['ipv4'], 'mac': None}
+
else:
assert(0)
-
-
- return {'ipv4': dst_ipv4, 'mac' : dst_mac}
-
- # return True if the port is resolved (either has MAC as dest of ARP resolution)
+
+ # return True if the port is resolved
def is_resolved (self):
- return (self.get_dst_addr()['mac'] != None)
+ return (self.get_dst_addr()['mac'] is not None)
# return True if the port is valid for resolve (has an IPv4 address as dest)
def is_resolvable (self):
- return (self.get_dst_addr()['ipv4'] != None)
+ return (self.get_dst_addr()['ipv4'] is not None)
@writeable
def arp_resolve (self, retries):
return ARPResolver(self).resolve(retries)
@writeable
- def ping (self, ping_ipv4, pkt_size, retries):
- return PingResolver(self, ping_ipv4, pkt_size).resolve(retries)
+ def ping (self, ping_ipv4, pkt_size):
+ return PingResolver(self, ping_ipv4, pkt_size).resolve()
################# stats handler ######################
@@ -1075,248 +1057,4 @@ class Port(object):
def async_event_released (self):
self.owner = ''
-
-# a generic abstract class for resolving using the server
-class Resolver(object):
- def __init__ (self, port, queue_size = 100):
- self.port = port
-
- # code to execute before sending any request - return RC object
- def pre_send (self):
- raise NotImplementedError()
-
- # return a list of streams for request
- def generate_request (self):
- raise NotImplementedError()
-
- # return None for more packets otherwise RC object
- def on_pkt_rx (self, pkt):
- raise NotImplementedError()
-
- # return value in case of timeout
- def on_timeout_err (self, retries):
- raise NotImplementedError()
-
- ##################### API ######################
- def resolve (self, retries = 0):
-
- # first cleanup
- rc = self.port.remove_all_streams()
- if not rc:
- return rc
-
- # call the specific class implementation
- rc = self.pre_send()
- if not rc:
- return rc
-
- # start the iteration
- try:
-
- # add the stream(s)
- self.port.add_streams(self.generate_request())
-
- 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()
- self.port.remove_all_streams()
-
-
- # main resolve function
- def resolve_wrapper (self, retries):
-
- # retry for 'retries'
- index = 0
- while True:
- rc = self.resolve_iteration()
- if rc is not None:
- return rc
-
- if index >= retries:
- return self.on_timeout_err(retries)
-
- index += 1
- time.sleep(0.1)
-
-
-
- def resolve_iteration (self):
-
- mult = {'op': 'abs', 'type' : 'percentage', 'value': 100}
- rc = self.port.start(mul = mult, force = False, duration = -1, mask = 0xffffffff)
- if not rc:
- return rc
-
- # save the start timestamp
- self.start_ts = rc.data()['ts']
-
- # block until traffic finishes
- while self.port.is_active():
- time.sleep(0.01)
-
- return self.wait_for_rx_response()
-
-
- def wait_for_rx_response (self):
-
- # we try to fetch response for 5 times
- polling = 5
-
- while polling > 0:
- # fetch the queue
- rx_pkts = self.port.get_rx_queue_pkts()
-
- # for each packet - examine it
- for pkt in rx_pkts:
- rc = self.on_pkt_rx(pkt)
- if rc is not None:
- return rc
-
- if polling == 0:
- return None
-
- polling -= 1
- time.sleep(0.1)
-
-
-
-
-
-class ARPResolver(Resolver):
- def __init__ (self, port_id):
- super(ARPResolver, self).__init__(port_id)
-
- # before resolve
- def pre_send (self):
- dst = self.port.get_dst_addr()
- src = self.port.get_src_addr()
-
- if dst['ipv4'] is None:
- return self.port.err("Port has a non-IPv4 destination: '{0}'".format(dst['mac']))
-
- if src['ipv4'] is None:
- return self.port.err('Port must have an IPv4 source address configured')
-
- # invalidate the current ARP resolution (if exists)
- return self.port.invalidate_arp()
-
-
- # return a list of streams for request
- def generate_request (self):
-
- dst = self.port.get_dst_addr()
- src = self.port.get_src_addr()
-
- base_pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(psrc = src['ipv4'], pdst = dst['ipv4'], hwsrc = src['mac'])
- s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
-
- return [s1]
-
-
- # return None in case more packets are needed else the status rc
- def on_pkt_rx (self, pkt):
- scapy_pkt = Ether(pkt['binary'])
- if not 'ARP' in scapy_pkt:
- return None
-
- arp = scapy_pkt['ARP']
- dst = self.port.get_dst_addr()
-
- # check this is the right ARP (ARP reply with the address)
- if (arp.op != 2) or (arp.psrc != dst['ipv4']):
- return None
-
-
- rc = self.port.set_arp_resolution(arp.psrc, arp.hwsrc)
- if not rc:
- return rc
-
- return self.port.ok('Port {0} - Recieved ARP reply from: {1}, hw: {2}'.format(self.port.port_id, arp.psrc, arp.hwsrc))
-
- def on_timeout_err (self, retries):
- return self.port.err('failed to receive ARP response ({0} retries)'.format(retries))
-
-
-
-
- #################### ping resolver ####################
-
-class PingResolver(Resolver):
- def __init__ (self, port, ping_ip, pkt_size):
- super(PingResolver, self).__init__(port)
- self.ping_ip = ping_ip
- self.pkt_size = pkt_size
-
- def pre_send (self):
-
- src = self.port.get_src_addr()
- dst = self.port.get_dst_addr()
- if src['ipv4'] is None:
- return self.port.err('Ping - port does not have an IPv4 address configured')
-
- if dst['mac'] is None:
- return self.port.err('Ping - port has an unresolved destination, cannot determine next hop MAC address')
-
- if self.ping_ip == src['ipv4']:
- return self.port.err('Ping - cannot ping own IP')
-
- return self.port.ok()
-
-
- # return a list of streams for request
- def generate_request (self):
-
- src = self.port.get_src_addr()
- dst = self.port.get_dst_addr()
-
- base_pkt = Ether(dst = dst['mac'])/IP(src = src['ipv4'], dst = self.ping_ip)/ICMP(type = 8)
- pad = max(0, self.pkt_size - len(base_pkt))
-
- base_pkt = base_pkt / (pad * 'x')
-
- #base_pkt.show2()
- s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
-
- return [s1]
-
- # return None for more packets otherwise RC object
- def on_pkt_rx (self, pkt):
- scapy_pkt = Ether(pkt['binary'])
- if not 'ICMP' in scapy_pkt:
- return None
-
- #scapy_pkt.show2()
- ip = scapy_pkt['IP']
-
- icmp = scapy_pkt['ICMP']
-
- dt = pkt['ts'] - self.start_ts
-
- if icmp.type == 0:
- # echo reply
- return self.port.ok('Reply from {0}: bytes={1}, time={2:.2f}ms, TTL={3}'.format(ip.src, len(pkt['binary']), dt * 1000, ip.ttl))
-
- # unreachable
- elif icmp.type == 3:
- return self.port.ok('Reply from {0}: Destination host unreachable'.format(icmp.src))
- else:
- scapy_pkt.show2()
- return self.port.err('unknown ICMP reply')
-
-
-
- # return the str of a timeout err
- def on_timeout_err (self, retries):
- return self.port.ok('Request timed out.')
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py
new file mode 100644
index 00000000..f9f6a499
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py
@@ -0,0 +1,255 @@
+
+from .trex_stl_streams import STLStream, STLTXSingleBurst
+from .trex_stl_packet_builder_scapy import STLPktBuilder
+
+from scapy.layers.l2 import Ether, ARP
+from scapy.layers.inet import IP, ICMP
+
+import time
+
+# a generic abstract class for resolving using the server
+class Resolver(object):
+ def __init__ (self, port, queue_size = 100):
+ self.port = port
+
+ # code to execute before sending any request - return RC object
+ def pre_send (self):
+ raise NotImplementedError()
+
+ # return a list of streams for request
+ def generate_request (self):
+ raise NotImplementedError()
+
+ # return None for more packets otherwise RC object
+ def on_pkt_rx (self, pkt):
+ raise NotImplementedError()
+
+ # return value in case of timeout
+ def on_timeout_err (self, retries):
+ raise NotImplementedError()
+
+ ##################### API ######################
+ def resolve (self, retries = 0):
+
+ # first cleanup
+ rc = self.port.remove_all_streams()
+ if not rc:
+ return rc
+
+ # call the specific class implementation
+ rc = self.pre_send()
+ if not rc:
+ return rc
+
+ # start the iteration
+ try:
+
+ # add the stream(s)
+ self.port.add_streams(self.generate_request())
+
+ 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()
+ self.port.remove_all_streams()
+
+
+ # main resolve function
+ def resolve_wrapper (self, retries):
+
+ # retry for 'retries'
+ index = 0
+ while True:
+ rc = self.resolve_iteration()
+ if rc is not None:
+ return rc
+
+ if index >= retries:
+ return self.on_timeout_err(retries)
+
+ index += 1
+ time.sleep(0.1)
+
+
+
+ def resolve_iteration (self):
+
+ mult = {'op': 'abs', 'type' : 'percentage', 'value': 100}
+ rc = self.port.start(mul = mult, force = False, duration = -1, mask = 0xffffffff)
+ if not rc:
+ return rc
+
+ # save the start timestamp
+ self.start_ts = rc.data()['ts']
+
+ # block until traffic finishes
+ while self.port.is_active():
+ time.sleep(0.01)
+
+ return self.wait_for_rx_response()
+
+
+ def wait_for_rx_response (self):
+
+ # we try to fetch response for 5 times
+ polling = 5
+
+ while polling > 0:
+
+ # fetch the queue
+ rx_pkts = self.port.get_rx_queue_pkts()
+
+ # might be an error
+ if not rx_pkts:
+ return rx_pkts
+
+ # for each packet - examine it
+ for pkt in rx_pkts:
+ rc = self.on_pkt_rx(pkt)
+ if rc is not None:
+ return rc
+
+ if polling == 0:
+ return None
+
+ polling -= 1
+ time.sleep(0.1)
+
+
+
+
+
+class ARPResolver(Resolver):
+ def __init__ (self, port_id):
+ super(ARPResolver, self).__init__(port_id)
+
+ # before resolve
+ def pre_send (self):
+ self.dst = self.port.get_dst_addr()
+ self.src = self.port.get_src_addr()
+
+ if self.dst['ipv4'] is None:
+ return self.port.err("Port has a non-IPv4 destination: '{0}'".format(dst['mac']))
+
+ if self.src['ipv4'] is None:
+ return self.port.err('Port must have an IPv4 source address configured')
+
+ # invalidate the current ARP resolution (if exists)
+ return self.port.invalidate_arp()
+
+
+ # return a list of streams for request
+ def generate_request (self):
+
+ base_pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(psrc = self.src['ipv4'], pdst = self.dst['ipv4'], hwsrc = self.src['mac'])
+ s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
+
+ return [s1]
+
+
+ # return None in case more packets are needed else the status rc
+ def on_pkt_rx (self, pkt):
+ scapy_pkt = Ether(pkt['binary'])
+ if not 'ARP' in scapy_pkt:
+ return None
+
+ arp = scapy_pkt['ARP']
+
+ # check this is the right ARP (ARP reply with the address)
+ if (arp.op != 2) or (arp.psrc != self.dst['ipv4']):
+ return None
+
+
+ rc = self.port.set_arp_resolution(arp.psrc, arp.hwsrc)
+ if not rc:
+ return rc
+
+ return self.port.ok('Port {0} - Recieved ARP reply from: {1}, hw: {2}'.format(self.port.port_id, arp.psrc, arp.hwsrc))
+
+
+ def on_timeout_err (self, retries):
+ return self.port.err('failed to receive ARP response ({0} retries)'.format(retries))
+
+
+
+
+ #################### ping resolver ####################
+
+class PingResolver(Resolver):
+ def __init__ (self, port, ping_ip, pkt_size):
+ super(PingResolver, self).__init__(port)
+ self.ping_ip = ping_ip
+ self.pkt_size = pkt_size
+
+ def pre_send (self):
+
+ self.src = self.port.get_src_addr()
+ self.dst = self.port.get_dst_addr()
+
+ if self.src['ipv4'] is None:
+ return self.port.err('Ping - port does not have an IPv4 address configured')
+
+ if self.dst['mac'] is None:
+ return self.port.err('Ping - port has an unresolved destination, cannot determine next hop MAC address')
+
+ if self.ping_ip == self.src['ipv4']:
+ return self.port.err('Ping - cannot ping own IP')
+
+ return self.port.ok()
+
+
+ # return a list of streams for request
+ def generate_request (self):
+
+ src = self.port.get_src_addr()
+ dst = self.port.get_dst_addr()
+
+ base_pkt = Ether(dst = dst['mac'])/IP(src = src['ipv4'], dst = self.ping_ip)/ICMP(type = 8)
+ pad = max(0, self.pkt_size - len(base_pkt))
+
+ base_pkt = base_pkt / (pad * 'x')
+
+ #base_pkt.show2()
+ s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
+
+ return [s1]
+
+ # return None for more packets otherwise RC object
+ def on_pkt_rx (self, pkt):
+ scapy_pkt = Ether(pkt['binary'])
+ if not 'ICMP' in scapy_pkt:
+ return None
+
+ #scapy_pkt.show2()
+ ip = scapy_pkt['IP']
+
+ icmp = scapy_pkt['ICMP']
+
+ dt = pkt['ts'] - self.start_ts
+
+ if icmp.type == 0:
+ # echo reply
+ return self.port.ok('Reply from {0}: bytes={1}, time={2:.2f}ms, TTL={3}'.format(ip.src, len(pkt['binary']), dt * 1000, ip.ttl))
+
+ # unreachable
+ elif icmp.type == 3:
+ return self.port.ok('Reply from {0}: Destination host unreachable'.format(icmp.src))
+ else:
+ scapy_pkt.show2()
+ return self.port.err('unknown ICMP reply')
+
+
+
+ # return the str of a timeout err
+ def on_timeout_err (self, retries):
+ return self.port.ok('Request timed out.')
diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp
index 2aab7f5d..1341def4 100644
--- a/src/main_dpdk.cpp
+++ b/src/main_dpdk.cpp
@@ -3345,9 +3345,9 @@ void CGlobalTRex::pre_test() {
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);
+ m_ports[port_id].get_port_attr()->get_dest().set_dest(dg, dst_mac);
} else {
- m_ports[port_id].get_port_attr()->get_dest().set_dest_mac(dst_mac);
+ m_ports[port_id].get_port_attr()->get_dest().set_dest(dst_mac);
}
}
@@ -4932,7 +4932,7 @@ bool CPhyEthIF::Create(uint8_t portid) {
/* 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);
+ m_port_attr->get_dest().set_dest(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 11dd99bd..ccdcbd8c 100644
--- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp
+++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp
@@ -385,10 +385,10 @@ 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)) {
- port_attr->get_dest().set_dest_ipv4(ipv4_addr);
+ port_attr->get_dest().set_dest(ipv4_addr);
} else if (utl_str_to_macaddr(addr, mac)) {
- port_attr->get_dest().set_dest_mac(mac);
+ port_attr->get_dest().set_dest(mac);
} else {
std::stringstream ss;
@@ -850,7 +850,7 @@ TrexRpcCmdSetARPRes::_run(const Json::Value &params, Json::Value &result) {
generate_parse_err(result, ss.str());
}
- port->getPortAttrObj()->get_dest().set_dest_ipv4(ipv4_addr, mac);
+ port->getPortAttrObj()->get_dest().set_dest(ipv4_addr, mac);
return (TREX_RPC_CMD_OK);
diff --git a/src/trex_port_attr.cpp b/src/trex_port_attr.cpp
index 20b441e2..b215a245 100644
--- a/src/trex_port_attr.cpp
+++ b/src/trex_port_attr.cpp
@@ -17,13 +17,77 @@ limitations under the License.
#include "trex_port_attr.h"
#include "bp_sim.h"
-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;
+ m_type = DEST_TYPE_MAC;
+
+ /* save the default */
+ memcpy(m_default_mac, m_mac, 6);
+}
+
+
+/**
+ * set dest as an IPv4 unresolved
+ */
+void
+DestAttr::set_dest(uint32_t ipv4) {
+ assert(ipv4 != 0);
+
+ m_ipv4 = ipv4;
+ memset(m_mac, 0, 6); // just to be on the safe side
+ m_type = DEST_TYPE_IPV4_UNRESOLVED;
+}
+
+/**
+ * set dest as a resolved IPv4
+ */
+void
+DestAttr::set_dest(uint32_t ipv4, const uint8_t *mac) {
+ assert(ipv4 != 0);
+
+ m_ipv4 = ipv4;
+ memcpy(m_mac, mac, 6);
+ m_type = DEST_TYPE_IPV4;
+}
+
+/**
+ * dest dest as MAC
+ *
+ */
+void
+DestAttr::set_dest(const uint8_t *mac) {
+
+ m_ipv4 = 0;
+ memcpy(m_mac, mac, 6);
+ m_type = DEST_TYPE_MAC;
+}
+
+void
+DestAttr::to_json(Json::Value &output) const {
+ switch (m_type) {
+
+ case DEST_TYPE_IPV4:
+ output["type"] = "ipv4";
+ output["ipv4"] = utl_uint32_to_ipv4(m_ipv4);
+ output["arp"] = utl_macaddr_to_str(m_mac);
+ break;
+
+ case DEST_TYPE_IPV4_UNRESOLVED:
+ output["type"] = "ipv4_u";
+ output["ipv4"] = utl_uint32_to_ipv4(m_ipv4);
+ break;
+
+ case DEST_TYPE_MAC:
+ output["type"] = "mac";
+ output["mac"] = utl_macaddr_to_str(m_mac);
+ break;
+
+ default:
+ assert(0);
+ }
+
}
const uint8_t *
diff --git a/src/trex_port_attr.h b/src/trex_port_attr.h
index 95050d1b..3cb9beff 100755
--- a/src/trex_port_attr.h
+++ b/src/trex_port_attr.h
@@ -31,79 +31,50 @@ limitations under the License.
*
*/
class DestAttr {
-private:
- static const uint8_t g_dummy_mac[6];
+
public:
DestAttr(uint8_t port_id);
+ /**
+ * dest can be either MAC IPv4, or IPv4 unresolved
+ */
enum dest_type_e {
- DEST_TYPE_IPV4 = 1,
- DEST_TYPE_MAC = 2
+ DEST_TYPE_IPV4 = 1,
+ DEST_TYPE_IPV4_UNRESOLVED,
+ DEST_TYPE_MAC,
};
/**
* 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;
- }
+ void set_dest(uint32_t ipv4);
/**
- * set dest as a resolved 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;
-
- }
-
+ void set_dest(uint32_t ipv4, const uint8_t *mac);
+
/**
- * dest dest as MAC
- *
+ * set dest as a plain MAC
*/
- void set_dest_mac(const uint8_t *mac) {
-
- m_src_ipv4 = 0;
- memcpy(m_mac, mac, 6);
- m_type = DEST_TYPE_MAC;
- }
-
+ void set_dest(const uint8_t *mac);
+
+ /**
+ * return true if destination is resolved
+ */
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;
+ return (m_type != DEST_TYPE_IPV4_UNRESOLVED);
}
/**
- * get the dest mac
- * if no MAC is configured and dest was not resolved
- * will return a dummy
+ * get the dest mac
+ * if the dest is not resolved
+ * it will return the default MAC
*/
const uint8_t *get_dest_mac() {
-
- if (is_resolved()) {
- return m_mac;
- } else {
- return g_dummy_mac;
- }
+ return m_mac;
}
/**
@@ -113,39 +84,20 @@ public:
void on_link_down() {
if (m_type == DEST_TYPE_IPV4) {
/* reset the IPv4 dest with no resolution */
- set_dest_ipv4(m_src_ipv4);
+ set_dest(m_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"] = Json::nullValue;
- }
- break;
-
- case DEST_TYPE_MAC:
- output["type"] = "mac";
- output["addr"] = utl_macaddr_to_str(m_mac);
- break;
-
- default:
- assert(0);
- }
-
- }
+ void to_json(Json::Value &output) const;
private:
- uint32_t m_src_ipv4;
+ uint32_t m_ipv4;
uint8_t *m_mac;
dest_type_e m_type;
uint8_t m_port_id;
+
+private:
+ uint8_t m_default_mac[6];
};