summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYaroslav Brustinov <ybrustin@cisco.com>2016-07-04 12:57:23 +0300
committerYaroslav Brustinov <ybrustin@cisco.com>2016-07-04 12:57:23 +0300
commit54da2d81bd54f90d512d35fab4061b0bfec13e05 (patch)
treece92363c19254e775092f511e1e12e4d426c43e4
parent7f58dadbd502f6fe504170c443505c7ad2eb3785 (diff)
parent483dfb7c5021d7dc9e2c7f10c9b76101441c7203 (diff)
Merge branch 'master' into cpu_per_core
-rwxr-xr-xVERSION3
-rwxr-xr-xlinux/ws_main.py1
-rwxr-xr-xlinux_dpdk/ws_main.py1
-rwxr-xr-xscripts/automation/regression/CPlatform.py4
-rw-r--r--scripts/automation/regression/stateless_tests/stl_rx_test.py130
-rw-r--r--scripts/automation/trex_control_plane/stl/examples/stl_flow_latency_stats.py25
-rw-r--r--scripts/automation/trex_control_plane/stl/examples/stl_flow_stats.py45
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py51
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py18
-rw-r--r--scripts/cap2/http_very_long.yaml21
-rw-r--r--scripts/cfg/client_cfg_template.yaml56
-rw-r--r--scripts/cfg/x710_advance_more_flows.yaml41
-rw-r--r--scripts/stl/burst_simple.py36
-rwxr-xr-xsrc/bp_sim.cpp187
-rwxr-xr-xsrc/bp_sim.h81
-rw-r--r--src/dpdk22/drivers/net/i40e/i40e_ethdev.c14
-rw-r--r--src/flow_stat.cpp30
-rw-r--r--src/flow_stat.h6
-rwxr-xr-xsrc/gtest/tuple_gen_test.cpp75
-rw-r--r--src/internal_api/trex_platform_api.h3
-rw-r--r--src/mac_mapping.h60
-rwxr-xr-xsrc/main.cpp39
-rw-r--r--src/main_dpdk.cpp208
-rw-r--r--src/sim/trex_sim_stateful.cpp25
-rw-r--r--src/stateless/cp/trex_streams_compiler.h2
-rw-r--r--src/stateless/dp/trex_stateless_dp_core.h2
-rw-r--r--src/stateless/rx/trex_stateless_rx_core.cpp97
-rw-r--r--src/stateless/rx/trex_stateless_rx_core.h43
-rw-r--r--src/trex_client_config.cpp235
-rw-r--r--src/trex_client_config.h265
-rwxr-xr-xsrc/tuple_gen.cpp185
-rwxr-xr-xsrc/tuple_gen.h194
-rwxr-xr-xsrc/utl_yaml.cpp267
-rwxr-xr-xsrc/utl_yaml.h55
34 files changed, 1778 insertions, 727 deletions
diff --git a/VERSION b/VERSION
index d73be704..b9c74c38 100755
--- a/VERSION
+++ b/VERSION
@@ -1,4 +1,5 @@
-v2.04
+v2.05
+
diff --git a/linux/ws_main.py b/linux/ws_main.py
index 6dccf597..2219522a 100755
--- a/linux/ws_main.py
+++ b/linux/ws_main.py
@@ -122,6 +122,7 @@ main_src = SrcGroup(dir='src',
'flow_stat.cpp',
'flow_stat_parser.cpp',
'trex_watchdog.cpp',
+ 'trex_client_config.cpp',
'pal/linux/pal_utl.cpp',
'pal/linux/mbuf.cpp',
'pal/common/common_mbuf.cpp',
diff --git a/linux_dpdk/ws_main.py b/linux_dpdk/ws_main.py
index dde94dc4..5bc00719 100755
--- a/linux_dpdk/ws_main.py
+++ b/linux_dpdk/ws_main.py
@@ -100,6 +100,7 @@ main_src = SrcGroup(dir='src',
'global_io_mode.cpp',
'main_dpdk.cpp',
'trex_watchdog.cpp',
+ 'trex_client_config.cpp',
'debug.cpp',
'flow_stat.cpp',
'flow_stat_parser.cpp',
diff --git a/scripts/automation/regression/CPlatform.py b/scripts/automation/regression/CPlatform.py
index de1c22ce..256d7411 100755
--- a/scripts/automation/regression/CPlatform.py
+++ b/scripts/automation/regression/CPlatform.py
@@ -20,7 +20,7 @@ class CPlatform(object):
self.tftp_cfg = None
self.config_history = { 'basic_if_config' : False, 'tftp_server_config' : False }
- def configure_basic_interfaces(self, mtu = 4000):
+ def configure_basic_interfaces(self, mtu = 9050):
cache = CCommandCache()
for dual_if in self.if_mngr.get_dual_if_list():
@@ -46,7 +46,7 @@ class CPlatform(object):
- def configure_basic_filtered_interfaces(self, intf_list, mtu = 4000):
+ def configure_basic_filtered_interfaces(self, intf_list, mtu = 9050):
cache = CCommandCache()
for intf in intf_list:
diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py
index 23ebf081..090261ff 100644
--- a/scripts/automation/regression/stateless_tests/stl_rx_test.py
+++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py
@@ -9,11 +9,7 @@ class STLRX_Test(CStlGeneral_Test):
"""Tests for RX feature"""
def setUp(self):
- #if CTRexScenario.setup_name in ('trex08', 'trex09'):
- # self.skip('This test makes trex08 and trex09 sick. Fix those ASAP.')
- if self.is_virt_nics:
- self.skip('Skip this for virtual NICs for now')
- per_driver_params = {"rte_vmxnet3_pmd": [1, 50, 1,False], "rte_ixgbe_pmd": [30, 5000, 1,True,200,400], "rte_i40e_pmd": [80, 5000, 1,True,100,250],
+ per_driver_params = {"rte_vmxnet3_pmd": [1, 50, 1,False], "rte_ixgbe_pmd": [30, 1000, 1,True,300,400], "rte_i40e_pmd": [80, 1000, 1,True,100,250],
"rte_igb_pmd": [80, 500, 1,False], "rte_em_pmd": [1, 50, 1,False], "rte_virtio_pmd": [1, 50, 1,False]}
CStlGeneral_Test.setUp(self)
@@ -32,21 +28,38 @@ class STLRX_Test(CStlGeneral_Test):
self.skip('port {0} does not support RX'.format(self.rx_port))
self.cap = cap
- self.rate_percent = per_driver_params[port_info['driver']][0]
- self.total_pkts = per_driver_params[port_info['driver']][1]
- if len(per_driver_params[port_info['driver']]) > 2:
- self.rate_lat = per_driver_params[port_info['driver']][2]
+ drv_name = port_info['driver']
+ self.rate_percent = per_driver_params[drv_name][0]
+ self.total_pkts = per_driver_params[drv_name][1]
+ if len(per_driver_params[drv_name]) > 2:
+ self.rate_lat = per_driver_params[drv_name][2]
else:
self.rate_lat = self.rate_percent
+ self.lat_pps = 1000
self.drops_expected = False
self.c.reset(ports = [self.tx_port, self.rx_port])
+ vm = STLScVmRaw( [ STLVmFlowVar ( "ip_src", min_value="10.0.0.1",
+ max_value="10.0.0.255", size=4, step=1,op="inc"),
+ STLVmWrFlowVar (fv_name="ip_src", pkt_offset= "IP.src" ), # write ip to packet IP.src
+ STLVmFixIpv4(offset = "IP") # fix checksum
+ ]
+ # Latency is bound to one core. We test that this option is not causing trouble
+ ,split_by_field = "ip_src"
+ ,cache_size =255 # Cache is ignored by latency flows. Need to test it is not crashing.
+ );
+
self.pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('Your_paylaod_comes_here'))
self.large_pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('a'*1000))
self.pkt_9k = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('a'*9000))
+ self.vm_pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")
+ / UDP(dport=12,sport=1025)/('Your_paylaod_comes_here')
+ , vm = vm)
+ self.vm_large_pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('a'*1000)
+ , vm = vm)
+ self.vm_9k_pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('a'*9000)
+ ,vm = vm)
-
- drv_name=port_info['driver']
self.latency_9k_enable=per_driver_params[drv_name][3]
if self.latency_9k_enable:
self.latency_9k_max_average = per_driver_params[drv_name][4]
@@ -124,7 +137,7 @@ class STLRX_Test(CStlGeneral_Test):
tmp = 'TX pkts mismatch - got: {0}, expected: {1}'.format(tx_pkts, total_pkts)
assert False, tmp
- if tx_bytes != (total_pkts * (pkt_len + 4)): # + 4 for ethernet CRC
+ if tx_bytes != (total_pkts * pkt_len):
pprint.pprint(flow_stats)
tmp = 'TX bytes mismatch - got: {0}, expected: {1}'.format(tx_bytes, (total_pkts * pkt_len))
assert False, tmp
@@ -136,7 +149,7 @@ class STLRX_Test(CStlGeneral_Test):
if "rx_bytes" in self.cap:
rx_bytes = flow_stats['rx_bytes'].get(self.rx_port, 0)
- if rx_bytes != (total_pkts * (pkt_len + 4)) and not self.drops_expected: # +4 for ethernet CRC
+ if rx_bytes != (total_pkts * pkt_len) and not self.drops_expected:
pprint.pprint(flow_stats)
tmp = 'RX bytes mismatch - got: {0}, expected: {1}'.format(rx_bytes, (total_pkts * pkt_len))
assert False, tmp
@@ -157,7 +170,7 @@ class STLRX_Test(CStlGeneral_Test):
# one stream on TX --> RX
def test_one_stream(self):
- total_pkts = self.total_pkts * 10
+ total_pkts = self.total_pkts
try:
s1 = STLStream(name = 'rx',
@@ -172,7 +185,7 @@ class STLRX_Test(CStlGeneral_Test):
print("\ninjecting {0} packets on port {1}\n".format(total_pkts, self.tx_port))
- exp = {'pg_id': 5, 'total_pkts': total_pkts, 'pkt_len': self.pkt.get_pkt_len()}
+ exp = {'pg_id': 5, 'total_pkts': total_pkts, 'pkt_len': s1.get_pkt_len()}
self.__rx_iteration( [exp] )
@@ -182,6 +195,9 @@ class STLRX_Test(CStlGeneral_Test):
def test_multiple_streams(self):
+ if self.is_virt_nics:
+ self.skip('Skip this for virtual NICs')
+
num_latency_streams = 128
num_flow_stat_streams = 127
total_pkts = int(self.total_pkts / (num_latency_streams + num_flow_stat_streams))
@@ -200,7 +216,7 @@ class STLRX_Test(CStlGeneral_Test):
flow_stats = STLFlowLatencyStats(pg_id = pg_id),
mode = STLTXSingleBurst(total_pkts = total_pkts+pg_id, percentage = percent)))
- exp.append({'pg_id': pg_id, 'total_pkts': total_pkts+pg_id, 'pkt_len': self.pkt.get_pkt_len()})
+ exp.append({'pg_id': pg_id, 'total_pkts': total_pkts+pg_id, 'pkt_len': streams[-1].get_pkt_len()})
for pg_id in range(num_latency_streams + 1, num_latency_streams + num_flow_stat_streams):
@@ -209,7 +225,7 @@ class STLRX_Test(CStlGeneral_Test):
flow_stats = STLFlowStats(pg_id = pg_id),
mode = STLTXSingleBurst(total_pkts = total_pkts+pg_id, percentage = percent)))
- exp.append({'pg_id': pg_id, 'total_pkts': total_pkts+pg_id, 'pkt_len': self.pkt.get_pkt_len()})
+ exp.append({'pg_id': pg_id, 'total_pkts': total_pkts+pg_id, 'pkt_len': streams[-1].get_pkt_len()})
# add both streams to ports
self.c.add_streams(streams, ports = [self.tx_port])
@@ -224,36 +240,42 @@ class STLRX_Test(CStlGeneral_Test):
total_pkts = self.total_pkts
try:
- s1 = STLStream(name = 'rx',
- packet = self.pkt,
- flow_stats = STLFlowStats(pg_id = 5),
- mode = STLTXSingleBurst(total_pkts = total_pkts,
- percentage = self.rate_percent
- ))
-
- s_lat = STLStream(name = 'rx',
- packet = self.pkt,
- flow_stats = STLFlowLatencyStats(pg_id = 5),
- mode = STLTXSingleBurst(total_pkts = total_pkts,
- percentage = self.rate_lat
- ))
-
- print("\ninjecting {0} packets on port {1}\n".format(total_pkts, self.tx_port))
-
- exp = {'pg_id': 5, 'total_pkts': total_pkts, 'pkt_len': self.pkt.get_pkt_len()}
- exp_lat = {'pg_id': 5, 'total_pkts': total_pkts, 'pkt_len': self.pkt.get_pkt_len()}
-
- self.c.add_streams([s1], ports = [self.tx_port])
- for i in range(0, 10):
- print("starting iteration {0}".format(i))
- self.__rx_iteration( [exp] )
-
- self.c.remove_all_streams(ports = [self.tx_port])
- self.c.add_streams([s_lat], ports = [self.tx_port])
- for i in range(0, 10):
- print("starting iteration {0} latency".format(i))
- self.__rx_iteration( [exp_lat] )
+ streams_data = [
+ {'name': 'Flow stat. No latency', 'pkt': self.pkt, 'lat': False},
+ {'name': 'Latency, no field engine', 'pkt': self.pkt, 'lat': True},
+ {'name': 'Latency, short packet with field engine', 'pkt': self.vm_pkt, 'lat': True},
+ {'name': 'Latency, large packet field engine', 'pkt': self.vm_large_pkt, 'lat': True}
+ ]
+ if self.latency_9k_enable:
+ streams_data.append({'name': 'Latency, 9k packet with field engine', 'pkt': self.vm_9k_pkt, 'lat': True})
+ streams = []
+ for data in streams_data:
+ if data['lat']:
+ flow_stats = STLFlowLatencyStats(pg_id = 5)
+ mode = STLTXSingleBurst(total_pkts = total_pkts, percentage = self.rate_percent)
+ else:
+ flow_stats = STLFlowStats(pg_id = 5)
+ mode = STLTXSingleBurst(total_pkts = total_pkts, pps = self.lat_pps)
+
+ s = STLStream(name = data['name'],
+ packet = data['pkt'],
+ flow_stats = flow_stats,
+ mode = mode
+ )
+ streams.append(s)
+
+ print("\ninjecting {0} packets on port {1}".format(total_pkts, self.tx_port))
+ exp = {'pg_id': 5, 'total_pkts': total_pkts}
+
+ for stream in streams:
+ self.c.add_streams([stream], ports = [self.tx_port])
+ print("Stream: {0}".format(stream.name))
+ exp['pkt_len'] = stream.get_pkt_len()
+ for i in range(0, 10):
+ print("Iteration {0}".format(i))
+ self.__rx_iteration( [exp] )
+ self.c.remove_all_streams(ports = [self.tx_port])
except STLError as e:
@@ -313,8 +335,8 @@ class STLRX_Test(CStlGeneral_Test):
# check low latency when you have stream of 9K stream
def test_9k_stream(self):
-
- #self.skip('Skip due to bug trex-215')
+ if self.is_virt_nics:
+ self.skip('Skip this for virtual NICs')
if self.latency_9k_enable == False:
print("SKIP")
@@ -429,7 +451,7 @@ class STLRX_Test(CStlGeneral_Test):
self.check_stats (stats,ls['tx_pkts'][pid], pkts,"ls['tx_pkts'][pid]")
self.check_stats (stats,ls['tx_bytes']['total'], tbytes,"ls['tx_bytes']['total']")
- self.check_stats (stats,ls['tx_bytes'][pid], pkts+1,"ls['tx_bytes'][pid]")
+ self.check_stats (stats,ls['tx_bytes'][pid], tbytes,"ls['tx_bytes'][pid]")
return 0
@@ -441,17 +463,21 @@ class STLRX_Test(CStlGeneral_Test):
def test_fcs_stream(self):
""" this test send 1 64 byte packet with latency and check that all counters are reported as 64 bytes"""
- #self.skip('Skip due to bug trex-213')
+
+ if self.is_virt_nics:
+ self.skip('Skip this for virtual NICs')
all_ports=list(CTRexScenario.stl_ports_map['map'].keys());
for port in all_ports:
for l in [True,False]:
print(" test port {0} latency : {1} ".format(port,l))
- self.send_1_burst(port,False,100)
+ self.send_1_burst(port,l,100)
# this test adds more and more latency streams and re-test with incremental
def test_incremental_latency_streams (self):
+ if self.is_virt_nics:
+ self.skip('Skip this for virtual NICs')
total_pkts = self.total_pkts
percent = 0.5
@@ -484,7 +510,7 @@ class STLRX_Test(CStlGeneral_Test):
print("port {0} : {1} streams at {2}% of line rate\n".format(self.tx_port, i, total_percent))
- exp.append({'pg_id': i, 'total_pkts': total_pkts, 'pkt_len': my_pkt.get_pkt_len()})
+ exp.append({'pg_id': i, 'total_pkts': total_pkts, 'pkt_len': s1.get_pkt_len()})
self.__rx_iteration( exp )
diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_flow_latency_stats.py b/scripts/automation/trex_control_plane/stl/examples/stl_flow_latency_stats.py
index ac0e212b..d8a99479 100644
--- a/scripts/automation/trex_control_plane/stl/examples/stl_flow_latency_stats.py
+++ b/scripts/automation/trex_control_plane/stl/examples/stl_flow_latency_stats.py
@@ -1,10 +1,12 @@
+# Example showing how to define stream for latency measurement, and how to parse the latency information
+
import stl_path
from trex_stl_lib.api import *
import time
import pprint
-def rx_example (tx_port, rx_port, burst_size, bw):
+def rx_example (tx_port, rx_port, burst_size, pps):
print("\nGoing to inject {0} packets on port {1} - checking RX stats on port {2}\n".format(burst_size, tx_port, rx_port))
@@ -19,7 +21,7 @@ def rx_example (tx_port, rx_port, burst_size, bw):
packet = pkt,
flow_stats = STLFlowLatencyStats(pg_id = 5),
mode = STLTXSingleBurst(total_pkts = total_pkts,
- percentage = bw))
+ pps = pps))
# connect to server
c.connect()
@@ -32,7 +34,7 @@ def rx_example (tx_port, rx_port, burst_size, bw):
print("\nInjecting {0} packets on port {1}\n".format(total_pkts, tx_port))
- rc = rx_iteration(c, tx_port, rx_port, total_pkts, pkt.get_pkt_len(), bw)
+ rc = rx_iteration(c, tx_port, rx_port, total_pkts, pkt.get_pkt_len())
if not rc:
passed = False
@@ -44,12 +46,12 @@ def rx_example (tx_port, rx_port, burst_size, bw):
c.disconnect()
if passed:
- print("\nTest has passed :-)\n")
+ print("\nTest passed :-)\n")
else:
- print("\nTest has failed :-(\n")
+ print("\nTest failed :-(\n")
# RX one iteration
-def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw):
+def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len):
c.clear_stats()
@@ -58,7 +60,8 @@ def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw):
stats = c.get_stats()
flow_stats = stats['flow_stats'].get(5)
- lat_stats = stats['latency'].get(5)
+ global_lat_stats = stats['latency']
+ lat_stats = global_lat_stats.get(5)
if not flow_stats:
print("no flow stats available")
return False
@@ -74,6 +77,8 @@ def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw):
dup = lat_stats['err_cntrs']['dup']
sth = lat_stats['err_cntrs']['seq_too_high']
stl = lat_stats['err_cntrs']['seq_too_low']
+ old_flow = global_lat_stats['global']['old_flow']
+ bad_hdr = global_lat_stats['global']['bad_hdr']
lat = lat_stats['latency']
jitter = lat['jitter']
avg = lat['average']
@@ -89,6 +94,10 @@ def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw):
return False
print('Error counters: dropped:{0}, ooo:{1} dup:{2} seq too high:{3} seq too low:{4}'.format(drops, ooo, dup, sth, stl))
+ if old_flow:
+ print ('Packets arriving too late after flow stopped: {0}'.format(old_flow))
+ if bad_hdr:
+ print ('Latency packets with corrupted info: {0}'.format(bad_hdr))
print('Latency info:')
print(" Maximum latency(usec): {0}".format(tot_max))
print(" Minimum latency(usec): {0}".format(tot_min))
@@ -131,5 +140,5 @@ def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw):
return True
# run the tests
-rx_example(tx_port = 1, rx_port = 0, burst_size = 500000, bw = 50)
+rx_example(tx_port = 0, rx_port = 1, burst_size = 1000, pps = 1000)
diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_flow_stats.py b/scripts/automation/trex_control_plane/stl/examples/stl_flow_stats.py
index ed4902fa..3c630ece 100644
--- a/scripts/automation/trex_control_plane/stl/examples/stl_flow_stats.py
+++ b/scripts/automation/trex_control_plane/stl/examples/stl_flow_stats.py
@@ -1,3 +1,5 @@
+# Example showing how to define stream for getting per flow statistics, and how to parse the received statistics
+
import stl_path
from trex_stl_lib.api import *
@@ -27,18 +29,14 @@ def rx_example (tx_port, rx_port, burst_size, bw):
# prepare our ports
c.reset(ports = [tx_port, rx_port])
- # add both streams to ports
+ # add stream to port
c.add_streams([s1], ports = [tx_port])
- print("\ninjecting {0} packets on port {1}\n".format(total_pkts, tx_port))
-
- for i in range(0, 10):
- print("\nStarting iteration: {0}:".format(i))
- rc = rx_iteration(c, tx_port, rx_port, total_pkts, pkt.get_pkt_len(), bw)
- if not rc:
- passed = False
- break
+ print("\ngoing to inject {0} packets on port {1}\n".format(total_pkts, tx_port))
+ rc = rx_iteration(c, tx_port, rx_port, total_pkts, s1.get_pkt_len())
+ if not rc:
+ passed = False
except STLError as e:
passed = False
@@ -48,19 +46,21 @@ def rx_example (tx_port, rx_port, burst_size, bw):
c.disconnect()
if passed:
- print("\nTest has passed :-)\n")
+ print("\nTest passed :-)\n")
else:
- print("\nTest has failed :-(\n")
+ print("\nTest failed :-(\n")
# RX one iteration
-def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw):
-
+def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len):
+ ret = True
+
c.clear_stats()
c.start(ports = [tx_port])
c.wait_on_traffic(ports = [tx_port])
- flow_stats = c.get_stats()['flow_stats'].get(5)
+ global_flow_stats = c.get_stats()['flow_stats']
+ flow_stats = global_flow_stats.get(5)
if not flow_stats:
print("no flow stats available")
return False
@@ -78,26 +78,33 @@ def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw):
if tx_pkts != total_pkts:
print("TX pkts mismatch - got: {0}, expected: {1}".format(tx_pkts, total_pkts))
pprint.pprint(flow_stats)
- return False
+ ret = False
else:
print("TX pkts match - {0}".format(tx_pkts))
if tx_bytes != (total_pkts * pkt_len):
print("TX bytes mismatch - got: {0}, expected: {1}".format(tx_bytes, (total_pkts * pkt_len)))
pprint.pprint(flow_stats)
- return False
+ ret = False
else:
print("TX bytes match - {0}".format(tx_bytes))
if rx_pkts != total_pkts:
print("RX pkts mismatch - got: {0}, expected: {1}".format(rx_pkts, total_pkts))
pprint.pprint(flow_stats)
- return False
+ ret = False
else:
print("RX pkts match - {0}".format(rx_pkts))
- return True
+
+ for field in ['rx_err', 'tx_err']:
+ for port in global_flow_stats['global'][field].keys():
+ if global_flow_stats['global'][field][port] != 0:
+ print ("\n{0} on port {1}: {2} - You should consider increasing rx_delay_ms value in wait_on_traffic"
+ .format(field, port, global_flow_stats['global'][field][port]))
+
+ return ret
# run the tests
-rx_example(tx_port = 1, rx_port = 2, burst_size = 500000, bw = 50)
+rx_example(tx_port = 0, rx_port = 1, burst_size = 500, bw = 50)
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 dc0035b3..ae7c23f2 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
@@ -1287,7 +1287,7 @@ class STLClient(object):
**total** and per port statistics contain dictionary with following format.
- Most of the bytes counters (unless specified otherwise) are in L2 layer including the FCS. e.g. minimum packet size in 64 bytes
+ Most of the bytes counters (unless specified otherwise) are in L2 layer, including the Ethernet FCS. e.g. minimum packet size is 64 bytes
=============================== ===============
key Meaning
@@ -1306,21 +1306,34 @@ class STLClient(object):
.. _flow_stats:
- **flow_stats** contains dictionaries per packet group id (pg id). Each one with the following structure.
+ **flow_stats** contains :ref:`global dictionary <flow_stats_global>`, and dictionaries per packet group id (pg id). See structures below.
+
+ **per pg_id flow stat** dictionaries have following structure:
================= ===============
key Meaning
================= ===============
- rx_bps Receivd bytes per second rate
- rx_bps_l1 Receivd bytes per second rate, including layer one
+ rx_bps Received bytes per second rate
+ rx_bps_l1 Received bytes per second rate, including layer one
rx_bytes Total number of received bytes
rx_pkts Total number of received packets
rx_pps Received packets per second
- tx_bps Transmitted bytes per second rate
- tx_bps_l1 Transmitted bytes per second rate, including layer one
+ tx_bps Transmit bytes per second rate
+ tx_bps_l1 Transmit bytes per second rate, including layer one
tx_bytes Total number of sent bytes
tx_pkts Total number of sent packets
- tx_pps Transmit packets per second
+ tx_pps Transmit packets per second rate
+ ================= ===============
+
+ .. _flow_stats_global:
+
+ **global flow stats** dictionary has the following structure:
+
+ ================= ===============
+ key Meaning
+ ================= ===============
+ rx_err Number of flow statistics packets received that we could not associate to any pg_id. This can happen if latency on the used setup is large. See :ref:`wait_on_traffic <wait_on_traffic>` rx_delay_ms parameter for details.
+ tx_err Number of flow statistics packets transmitted that we could not associate to any pg_id. This is never expected. If you see this different than 0, please report.
================= ===============
.. _global:
@@ -1343,7 +1356,9 @@ class STLClient(object):
.. _latency:
- **latency** contains dictionary per packet group id (pg id). Each one with the following structure.
+ **latency** contains :ref:`global dictionary <lat_stats_global>`, and dictionaries per packet group id (pg id). Each one with the following structure.
+
+ **per pg_id latency stat** dictionaries have following structure:
=========================== ===============
key Meaning
@@ -1397,7 +1412,16 @@ class STLClient(object):
total_min Minimum latency measured over the stream lifetime (in usec).
================= ===============
+ .. _lat_stats_global:
+ **global latency stats** dictionary has the following structure:
+
+ ================= ===============
+ key Meaning
+ ================= ===============
+ old_flow Number of latency statistics packets received that we could not associate to any pg_id. This can happen if latency on the used setup is large. See :ref:`wait_on_traffic <wait_on_traffic>` rx_delay_ms parameter for details.
+ bad_hdr Number of latency packets received with bad latency data. This can happen becuase of garbage packets in the network, or if the DUT causes packet corruption.
+ ================= ===============
:raises:
None
@@ -2292,6 +2316,8 @@ class STLClient(object):
@__api_check(True)
def wait_on_traffic (self, ports = None, timeout = 60, rx_delay_ms = 10):
"""
+ .. _wait_on_traffic:
+
Block until traffic on specified port(s) has ended
:parameters:
@@ -2302,12 +2328,11 @@ class STLClient(object):
timeout in seconds
rx_delay_ms : int
- time to wait until RX filters are removed
- this value should reflect the time it takes
- packets which were transmitted to arrive
+ Time to wait (in milliseconds) after last packet was sent, until RX filters used for
+ measuring flow statistics and latency are removed.
+ This value should reflect the time it takes packets which were transmitted to arrive
to the destination.
- after this time the RX filters will be removed
-
+ After this time, RX filters will be removed, and packets arriving for per flow statistics feature and latency flows will be counted as errors.
:raises:
+ :exc:`STLTimeoutError` - in case timeout has expired
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 678adb4e..af4d6f69 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
@@ -1093,6 +1093,13 @@ class CLatencyStats(CTRexStats):
snapshot = {}
output = {}
+ output['global'] = {}
+ for field in ['bad_hdr', 'old_flow']:
+ if 'global' in snapshot and field in snapshot['global']:
+ output['global'][field] = snapshot['global'][field]
+ else:
+ output['global'][field] = 0
+
# we care only about the current active keys
pg_ids = list(filter(is_intable, snapshot.keys()))
@@ -1190,6 +1197,14 @@ class CRxStats(CTRexStats):
# copy timestamp field
output['ts'] = current['ts']
+ # global (not per pg_id) error counters
+ output['global'] = {}
+ for field in ['rx_err', 'tx_err']:
+ output['global'][field] = {}
+ if 'global' in current and field in current['global']:
+ for port in current['global'][field]:
+ output['global'][field][int(port)] = current['global'][field][port]
+
# we care only about the current active keys
pg_ids = list(filter(is_intable, current.keys()))
@@ -1337,6 +1352,9 @@ class CRxStats(CTRexStats):
for pg_id, value in self.latest_stats.items():
# skip non ints
if not is_intable(pg_id):
+ # 'global' stats are in the same level of the pg_ids. We do want them to go to the user
+ if pg_id == 'global':
+ stats[pg_id] = value
continue
# bare counters
stats[int(pg_id)] = {}
diff --git a/scripts/cap2/http_very_long.yaml b/scripts/cap2/http_very_long.yaml
new file mode 100644
index 00000000..0e7bb46f
--- /dev/null
+++ b/scripts/cap2/http_very_long.yaml
@@ -0,0 +1,21 @@
+- duration : 0.1
+ generator :
+ distribution : "seq"
+ clients_start : "16.0.0.1"
+ clients_end : "16.0.0.255"
+ servers_start : "48.0.0.1"
+ servers_end : "48.0.255.255"
+ clients_per_gb : 201
+ min_clients : 101
+ dual_port_mask : "1.0.0.0"
+ tcp_aging : 0
+ udp_aging : 0
+ mac : [0x0,0x0,0x0,0x1,0x0,0x00]
+ #cap_ipg : true
+ cap_info :
+ - name: avl/delay_10_http_browsing_0.pcap
+ cps : 2.776
+ ipg : 4000000
+ rtt : 4000000
+ w : 1
+
diff --git a/scripts/cfg/client_cfg_template.yaml b/scripts/cfg/client_cfg_template.yaml
new file mode 100644
index 00000000..8257b981
--- /dev/null
+++ b/scripts/cfg/client_cfg_template.yaml
@@ -0,0 +1,56 @@
+#
+# Client configuration example file
+# The file must contain the following fields
+#
+# 'vlan' - is the entire configuration under VLAN
+# if so, each client group must include vlan
+# configuration
+#
+# 'groups' - each client group must contain a range of IP
+# and initiator and responder maps
+# 'count' represents the number of MAC devices
+# on the group.
+#
+# initiator and responder can contain 'vlan', 'src_mac', 'dst_mac'
+#
+
+vlan: true
+
+groups:
+
+- ip_start : 5.0.0.1
+ ip_end : 5.0.0.4
+ initiator :
+ vlan : 100
+ dst_mac : "00:00:00:01:00:00"
+ responder :
+ vlan : 200
+ dst_mac : "00:00:00:01:00:00"
+
+ count : 2
+
+- ip_start : 5.0.0.5
+ ip_end : 5.0.0.12
+ initiator :
+ vlan : 101
+ dst_mac : "01:00:00:00:01:01"
+
+ responder:
+ vlan : 201
+ dst_mac : "01:00:00:00:02:01"
+
+ count : 4
+
+- ip_start : 5.0.0.13
+ ip_end : 5.0.0.30
+
+ initiator :
+ vlan : 103
+ dst_mac : "02:00:00:00:01:01"
+ responder :
+
+ vlan : 203
+ dst_mac : "02:00:00:00:02:01"
+
+ count : 8
+
diff --git a/scripts/cfg/x710_advance_more_flows.yaml b/scripts/cfg/x710_advance_more_flows.yaml
new file mode 100644
index 00000000..074420ef
--- /dev/null
+++ b/scripts/cfg/x710_advance_more_flows.yaml
@@ -0,0 +1,41 @@
+- port_limit : 4
+ version : 2
+ interfaces : ["02:00.0","02:00.1","84:00.0","84:00.1"] # list of the interfaces to bind run ./dpdk_nic_bind.py --status
+ c : 4
+ #port_bandwidth_gb : 40
+ port_bandwidth_gb : 10
+
+ port_info : # set eh mac addr
+
+ - dest_mac : [0x68, 0x05, 0xca, 0x32, 0x0c, 0x39]
+ src_mac : [0x68, 0x05, 0xca, 0x32, 0x15, 0xb0]
+
+
+ - dest_mac : [0x68, 0x05, 0xca, 0x32, 0x0c, 0x38]
+ src_mac : [0x68, 0x05, 0xca, 0x32, 0x15, 0xb1]
+
+ - dest_mac : [0x68, 0x05, 0xca, 0x32, 0x15, 0xb1]
+ src_mac : [0x68, 0x05, 0xca, 0x32, 0x0c, 0x38]
+
+ - dest_mac : [0x68, 0x05, 0xca, 0x32, 0x15, 0xb0]
+ src_mac : [0x68, 0x05, 0xca, 0x32, 0x0c, 0x39]
+
+
+ platform :
+ master_thread_id : 0
+ latency_thread_id : 8
+ dual_if :
+ - socket : 0
+ threads : [1, 2, 3, 4, 5, 6, 7]
+ - socket : 1
+ threads : [9, 10, 11, 12, 13, 14, 15]
+
+
+
+ memory :
+ dp_flows : 10048576
+
+
+
+
+
diff --git a/scripts/stl/burst_simple.py b/scripts/stl/burst_simple.py
new file mode 100644
index 00000000..87d7a5a1
--- /dev/null
+++ b/scripts/stl/burst_simple.py
@@ -0,0 +1,36 @@
+from trex_stl_lib.api import *
+
+class STLS1(object):
+
+ def __init__ (self):
+ self.fsize =64; # the size of the packet
+
+
+ def create_stream (self):
+
+ # Create base packet and pad it to size
+ size = self.fsize - 4; # HW will add 4 bytes ethernet FCS
+ base_pkt = Ether(dst="00:00:00:00:00:01")/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)
+ pad = max(0, size - len(base_pkt)) * 'x'
+
+
+ return STLProfile( [ STLStream( isg = 1.0, # start in delay in usec
+ packet = STLPktBuilder(pkt = base_pkt/pad),
+ mode = STLTXSingleBurst( pps = 1000),
+ )
+
+ ]).get_streams()
+
+
+ def get_streams (self, direction = 0, **kwargs):
+ # create 1 stream
+ return self.create_stream()
+
+
+# dynamic load - used for trex console or simulator
+def register():
+ return STLS1()
+
+
+
+
diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp
index 6b5acd42..b93c0461 100755
--- a/src/bp_sim.cpp
+++ b/src/bp_sim.cpp
@@ -748,9 +748,8 @@ void CPreviewMode::Dump(FILE *fd){
fprintf(fd," 1g mode : %d\n", (int)get_1g_mode() );
fprintf(fd," zmq_publish : %d\n", (int)get_zmq_publish_enable() );
fprintf(fd," vlan_enable : %d\n", (int)get_vlan_mode_enable() );
+ fprintf(fd," client_cfg : %d\n", (int)get_is_client_cfg_enable() );
fprintf(fd," mbuf_cache_disable : %d\n", (int)isMbufCacheDisabled() );
- fprintf(fd," mac_ip_features : %d\n", (int)get_mac_ip_features_enable()?1:0 );
- fprintf(fd," mac_ip_map : %d\n", (int)get_mac_ip_mapping_enable()?1:0 );
fprintf(fd," vm mode : %d\n", (int)get_vm_one_queue_enable()?1:0 );
}
@@ -2426,26 +2425,6 @@ void CCapFileFlowInfo::Delete(){
RemoveAll();
}
-void operator >> (const YAML::Node& node, mac_mapping_t &fi) {
- utl_yaml_read_ip_addr(node,"ip", fi.ip);
- const YAML::Node& mac_info = node["mac"];
- for(unsigned i=0;i<mac_info.size();i++) {
- const YAML::Node & node_2 =mac_info;
- uint32_t value;
- node_2[i] >> value;
- fi.mac.mac[i] = value;
- }
-}
-
-void operator >> (const YAML::Node& node, std::map<uint32_t, mac_addr_align_t> &mac_info) {
- const YAML::Node& mac_node = node["items"];
- mac_mapping_t mac_mapping;
- for (unsigned i=0;i<mac_node.size();i++) {
- mac_node[i] >> mac_mapping;
- mac_info[mac_mapping.ip] = mac_mapping.mac;
- }
-}
-
void operator >> (const YAML::Node& node, CFlowYamlDpPkt & fi) {
uint32_t val;
node["pkt_id"] >> val;
@@ -3325,7 +3304,7 @@ bool CFlowGenListPerThread::Create(uint32_t thread_id,
/* split the clients to threads */
CTupleGenYamlInfo * tuple_gen = &m_flow_list->m_yaml_info.m_tuple_gen;
- m_smart_gen.Create(0,m_thread_id,m_flow_list->get_is_mac_conf());
+ m_smart_gen.Create(0,m_thread_id);
/* split the clients to threads using the mask */
CIpPortion portion;
@@ -3335,14 +3314,14 @@ bool CFlowGenListPerThread::Create(uint32_t thread_id,
portion);
m_smart_gen.add_client_pool(tuple_gen->m_client_pool[i].m_dist,
- portion.m_ip_start,
- portion.m_ip_end,
- get_longest_flow(i,true),
- get_total_kcps(i,true)*1000,
- &m_flow_list->m_mac_info,
- tuple_gen->m_client_pool[i].m_tcp_aging_sec,
- tuple_gen->m_client_pool[i].m_udp_aging_sec
- );
+ portion.m_ip_start,
+ portion.m_ip_end,
+ get_longest_flow(i,true),
+ get_total_kcps(i,true)*1000,
+ m_flow_list->m_client_config_info,
+ tuple_gen->m_client_pool[i].m_tcp_aging_sec,
+ tuple_gen->m_client_pool[i].m_udp_aging_sec
+ );
}
for (int i=0;i<tuple_gen->m_server_pool.size();i++) {
split_ips(m_thread_id, m_max_threads, getDualPortId(),
@@ -4294,12 +4273,19 @@ void CFlowGenListPerThread::start_generate_stateful(std::string erf_file_name,
fprintf(stderr," nothing to generate no template loaded \n");
return;
}
+
m_preview_mode = preview;
m_node_gen.open_file(erf_file_name,&m_preview_mode);
dsec_t d_time_flow=get_delta_flow_is_sec();
- m_cur_time_sec = 0.01+m_thread_id*m_flow_list->get_delta_flow_is_sec();
+
+ m_cur_time_sec = 0.01 + m_thread_id*m_flow_list->get_delta_flow_is_sec();
+
+
if ( CGlobalInfo::is_realtime() ){
- m_cur_time_sec += now_sec() + 0.5 ;
+ if (m_cur_time_sec > 0.2 ) {
+ m_cur_time_sec = 0.01 + m_thread_id*0.01;
+ }
+ m_cur_time_sec += now_sec() + 0.1 ;
}
dsec_t c_stop_sec = m_cur_time_sec + m_yaml_info.m_duration_sec;
m_stop_time_sec =c_stop_sec;
@@ -4389,32 +4375,11 @@ void CFlowGenList::clean_p_thread_info(void){
m_threads_info.clear();
}
-
-
-int CFlowGenList::load_from_mac_file(std::string file_name) {
- if ( !utl_is_file_exists (file_name) ){
- printf(" ERROR no mac_file is set, file %s does not exist \n",file_name.c_str());
- exit(-1);
- }
- m_mac_info.set_configured(true);
-
- try {
- std::ifstream fin((char *)file_name.c_str());
- YAML::Parser parser(fin);
- YAML::Node doc;
-
- parser.GetNextDocument(doc);
- doc[0] >> m_mac_info.get_mac_info();
- } catch ( const std::exception& e ) {
- std::cout << e.what() << "\n";
- m_mac_info.clear();
- exit(-1);
- }
-
- return (0);
+int CFlowGenList::load_client_config_file(std::string file_name) {
+ m_client_config_info.load_yaml_file(file_name);
+ return (0);
}
-
int CFlowGenList::load_from_yaml(std::string file_name,
uint32_t num_threads){
uint8_t idx;
@@ -4735,21 +4700,20 @@ bool CParserOption::is_valid_opt_val(int val, int min, int max, const std::strin
void CParserOption::dump(FILE *fd){
preview.Dump(fd);
- fprintf(fd," cfg file : %s \n",cfg_file.c_str());
- fprintf(fd," mac file : %s \n",mac_file.c_str());
- fprintf(fd," out file : %s \n",out_file.c_str());
- fprintf(fd," duration : %.0f \n",m_duration);
- fprintf(fd," factor : %.0f \n",m_factor);
- fprintf(fd," mbuf_factor : %.0f \n",m_mbuf_factor);
- fprintf(fd," latency : %d pkt/sec \n",m_latency_rate);
- fprintf(fd," zmq_port : %d \n",m_zmq_port);
- fprintf(fd," telnet_port : %d \n",m_telnet_port);
- fprintf(fd," expected_ports : %d \n",m_expected_portd);
+ fprintf(fd," cfg file : %s \n",cfg_file.c_str());
+ fprintf(fd," mac file : %s \n",client_cfg_file.c_str());
+ fprintf(fd," out file : %s \n",out_file.c_str());
+ fprintf(fd," client cfg file : %s \n",out_file.c_str());
+ fprintf(fd," duration : %.0f \n",m_duration);
+ fprintf(fd," factor : %.0f \n",m_factor);
+ fprintf(fd," mbuf_factor : %.0f \n",m_mbuf_factor);
+ fprintf(fd," latency : %d pkt/sec \n",m_latency_rate);
+ fprintf(fd," zmq_port : %d \n",m_zmq_port);
+ fprintf(fd," telnet_port : %d \n",m_telnet_port);
+ fprintf(fd," expected_ports : %d \n",m_expected_portd);
if (preview.get_vlan_mode_enable() ) {
- fprintf(fd," vlans : [%d,%d] \n",m_vlan_port[0],m_vlan_port[1]);
+ fprintf(fd," vlans : [%d,%d] \n",m_vlan_port[0],m_vlan_port[1]);
}
- fprintf(fd," mac spreading: %d \n",(int)m_mac_splitter);
-
int i;
for (i = 0; i < TREX_MAX_PORTS; i++) {
@@ -4762,6 +4726,15 @@ void CParserOption::dump(FILE *fd){
}
}
+void CParserOption::verify() {
+ /* check for mutual exclusion options */
+ if (preview.get_is_client_cfg_enable()) {
+ if (preview.get_vlan_mode_enable() || preview.get_mac_ip_overide_enable()) {
+ throw std::runtime_error("VLAN / MAC override cannot be combined with client configuration");
+ }
+ }
+}
+
#if 0
void CTupleGlobalGenerator::Dump(FILE *fd){
@@ -4993,6 +4966,7 @@ int CErfIFStl::send_sl_node(CGenNodeStateless *node_sl) {
fsp_head->seq = 0x12345678;
fsp_head->hw_id = hw_id - MAX_FLOW_STATS;
fsp_head->magic = FLOW_STAT_PAYLOAD_MAGIC;
+ fsp_head->flow_seq = FLOW_STAT_PAYLOAD_INITIAL_FLOW_SEQ;
fsp_head->time_stamp = 0x8899aabbccddeeff;
fill_raw_packet(mi, (CGenNode *)node_sl, dir);
rte_pktmbuf_free(mi);
@@ -5047,37 +5021,68 @@ int CErfIFStl::send_node(CGenNode * _no_to_use){
return (0);
}
+void CErfIF::add_vlan(uint16_t vlan_id) {
+ uint8_t *buffer =(uint8_t *)m_raw->raw;
+ uint16_t vlan_protocol = EthernetHeader::Protocol::VLAN;
+ uint32_t vlan_tag = (vlan_protocol << 16) | vlan_id;
+ vlan_tag = PKT_HTONL(vlan_tag);
-int CErfIF::send_node(CGenNode * node){
+ /* insert vlan tag and adjust packet size */
+ memcpy(cbuff+4, buffer + 12, m_raw->pkt_len - 12);
+ memcpy(cbuff, &vlan_tag, 4);
+ memcpy(buffer + 12, cbuff, m_raw->pkt_len - 8);
- if ( m_preview_mode->getFileWrite() ){
+ m_raw->pkt_len += 4;
+}
- CFlowPktInfo * lp=node->m_pkt_info;
- rte_mbuf_t * m=lp->generate_new_mbuf(node);
- pkt_dir_t dir=node->cur_interface_dir();
+void CErfIF::apply_client_config(const ClientCfg *cfg, pkt_dir_t dir) {
+ assert(cfg);
+ uint8_t *p = (uint8_t *)m_raw->raw;
+
+ const ClientCfgDir &cfg_dir = ( (dir == CLIENT_SIDE) ? cfg->m_initiator : cfg->m_responder);
+
+ /* dst mac */
+ if (cfg_dir.has_dst_mac_addr()) {
+ memcpy(p, cfg_dir.get_dst_mac_addr(), 6);
+ }
+
+ /* src mac */
+ if (cfg_dir.has_src_mac_addr()) {
+ memcpy(p + 6, cfg_dir.get_src_mac_addr(), 6);
+ }
+
+ /* VLAN */
+ if (cfg_dir.has_vlan()) {
+ add_vlan(cfg_dir.get_vlan());
+ }
+}
- fill_raw_packet(m,node,dir);
+int CErfIF::send_node(CGenNode *node){
+
+ if (!m_preview_mode->getFileWrite()) {
+ return (0);
+ }
+
+ CFlowPktInfo *lp = node->m_pkt_info;
+ rte_mbuf_t *m = lp->generate_new_mbuf(node);
+ pkt_dir_t dir = node->cur_interface_dir();
+
+ fill_raw_packet(m, node, dir);
/* update mac addr dest/src 12 bytes */
uint8_t *p=(uint8_t *)m_raw->raw;
int p_id=(int)dir;
memcpy(p,CGlobalInfo::m_options.get_dst_src_mac_addr(p_id),12);
- /* If vlan is enabled, add vlan header */
- if ( unlikely( CGlobalInfo::m_options.preview.get_vlan_mode_enable() ) ){
- /* retrieve vlan ID and form vlan tag */
- uint8_t vlan_port = (node->m_src_ip &1);
- uint16_t vlan_protocol = EthernetHeader::Protocol::VLAN;
- uint16_t vlan_id = CGlobalInfo::m_options.m_vlan_port[vlan_port];
- uint32_t vlan_tag = (vlan_protocol << 16) | vlan_id;
- vlan_tag = PKT_HTONL(vlan_tag);
+ /* if a client configuration was provided - apply the config */
+ if (CGlobalInfo::m_options.preview.get_is_client_cfg_enable()) {
+ apply_client_config(node->m_client_cfg, dir);
- /* insert vlan tag and adjust packet size */
- memcpy(cbuff+4, p+12, m_raw->pkt_len-12);
- memcpy(cbuff, &vlan_tag, 4);
- memcpy(p+12, cbuff, m_raw->pkt_len-8);
- m_raw->pkt_len += 4;
+ } else if (CGlobalInfo::m_options.preview.get_vlan_mode_enable()) {
+ uint8_t vlan_port = (node->m_src_ip & 1);
+ uint16_t vlan_id = CGlobalInfo::m_options.m_vlan_port[vlan_port];
+ add_vlan(vlan_id);
}
//utl_DumpBuffer(stdout,p, 12,0);
@@ -5086,8 +5091,8 @@ int CErfIF::send_node(CGenNode * node){
BP_ASSERT(rc == 0);
rte_pktmbuf_free(m);
- }
- return (0);
+
+ return (0);
}
int CErfIF::flush_tx_queue(void){
diff --git a/src/bp_sim.h b/src/bp_sim.h
index 05900351..23e9f8d5 100755
--- a/src/bp_sim.h
+++ b/src/bp_sim.h
@@ -59,6 +59,7 @@ limitations under the License.
#include "platform_cfg.h"
#include "flow_stat.h"
#include "trex_watchdog.h"
+#include "trex_client_config.h"
#include <trex_stateless_dp_core.h>
@@ -182,6 +183,12 @@ inline int ip_to_str(uint32_t ip,char * str){
return(strlen(str));
}
+inline std::string ip_to_str(uint32_t ip) {
+ char tmp[INET_ADDRSTRLEN];
+ ip_to_str(ip, tmp);
+ return tmp;
+}
+
// Routine to create IPv6 address string
inline int ipv6_to_str(ipaddr_t *ip,char * str){
int idx=0;
@@ -259,18 +266,20 @@ class CPreviewMode ;
class CLatencyPktData {
public:
- CLatencyPktData() {m_magic = 0xaa;}
+ CLatencyPktData() {m_flow_seq = FLOW_STAT_PAYLOAD_INITIAL_FLOW_SEQ;}
inline uint32_t get_seq_num() {return m_seq_num;}
inline void inc_seq_num() {m_seq_num++;}
- inline uint32_t get_magic() {return m_magic;}
+ inline uint32_t get_flow_seq() {return m_flow_seq;}
void reset() {
m_seq_num = UINT32_MAX - 1; // catch wrap around issues early
- m_magic++;
+ m_flow_seq++;
+ if (m_flow_seq == FLOW_STAT_PAYLOAD_INITIAL_FLOW_SEQ)
+ m_flow_seq++;
}
private:
- uint32_t m_seq_num; // seq num to put in packet for payload rules
- uint16_t m_magic; // magic to put in packet for payload rules
+ uint32_t m_seq_num; // seq num to put in packet for payload rules. Increased every packet.
+ uint16_t m_flow_seq; // Seq num of flow. Changed when we start new flow on this id.
};
/* represent the virtual interface
@@ -621,7 +630,7 @@ public:
void set_mac_ip_overide_enable(bool enable){
btSetMaskBit32(m_flags,30,30,enable?1:0);
if (enable) {
- set_mac_ip_features_enable(enable);
+ set_slowpath_features_on(enable);
}
}
@@ -633,26 +642,27 @@ public:
btSetMaskBit32(m_flags,31,31,enable?1:0);
}
-
- bool get_mac_ip_features_enable(){
- return (btGetMaskBit32(m_flags1,0,0) ? true:false);
+ bool get_is_slowpath_features_on() {
+ return (btGetMaskBit32(m_flags1, 0, 0) ? true : false);
}
- void set_mac_ip_features_enable(bool enable){
- btSetMaskBit32(m_flags1,0,0,enable?1:0);
+ void set_slowpath_features_on(bool enable) {
+ btSetMaskBit32(m_flags1, 0, 0, enable ? 1 : 0);
}
- bool get_mac_ip_mapping_enable(){
- return (btGetMaskBit32(m_flags1,1,1) ? true:false);
+ bool get_is_client_cfg_enable() {
+ return (btGetMaskBit32(m_flags1, 1, 1) ? true : false);
}
- void set_mac_ip_mapping_enable(bool enable){
- btSetMaskBit32(m_flags1,1,1,enable?1:0);
+ void set_client_cfg_enable(bool enable){
+ btSetMaskBit32(m_flags1, 1, 1, enable ? 1 : 0);
if (enable) {
- set_mac_ip_features_enable(enable);
+ set_slowpath_features_on(enable);
}
}
+
+
bool get_vm_one_queue_enable(){
return (btGetMaskBit32(m_flags1,2,2) ? true:false);
}
@@ -678,16 +688,6 @@ public:
return (btGetMaskBit32(m_flags1,3,3) ? true:false);
}
-
- /* split mac is enabled */
- void setDestMacSplit(bool enable){
- btSetMaskBit32(m_flags1,4,4,enable?1:0);
- }
-
- bool getDestMacSplit(){
- return (btGetMaskBit32(m_flags1,4,4) ? true:false);
- }
-
/* split mac is enabled */
void setWDDisable(bool wd_disable){
btSetMaskBit32(m_flags1,6,6,wd_disable?1:0);
@@ -779,7 +779,6 @@ public:
m_io_mode=1;
m_run_flags=0;
prefix="";
- m_mac_splitter=0;
m_run_mode = RUN_MODE_INVALID;
m_l_pkt_mode = 0;
m_rx_thread_enabled = false;
@@ -805,7 +804,6 @@ public:
uint16_t m_expected_portd;
uint16_t m_io_mode; //0,1,2 0 disable, 1- normal , 2 - short
uint16_t m_run_flags;
- uint8_t m_mac_splitter;
uint8_t m_l_pkt_mode;
uint8_t m_learn_mode;
uint16_t m_debug_pkt_proto;
@@ -815,7 +813,7 @@ public:
std::string cfg_file;
- std::string mac_file;
+ std::string client_cfg_file;
std::string platform_cfg_file;
std::string out_file;
@@ -873,6 +871,8 @@ public:
}
void dump(FILE *fd);
bool is_valid_opt_val(int val, int min, int max, const std::string &opt_name);
+
+ void verify();
};
@@ -1561,7 +1561,7 @@ public:
uint16_t m_nat_external_port;
uint16_t m_nat_pad[3];
- mac_addr_align_t m_src_mac;
+ const ClientCfg *m_client_cfg;
uint32_t m_src_idx;
uint32_t m_dest_idx;
uint32_t m_end_of_cache_line[6];
@@ -1907,7 +1907,8 @@ public:
protected:
-
+ void add_vlan(uint16_t vlan_id);
+ void apply_client_config(const ClientCfg *cfg, pkt_dir_t dir);
virtual void fill_raw_packet(rte_mbuf_t * m,CGenNode * node,pkt_dir_t dir);
CFileWriterBase * m_writer;
@@ -3870,7 +3871,8 @@ public:
public:
int load_from_yaml(std::string csv_file,uint32_t num_threads);
- int load_from_mac_file(std::string csv_file);
+ int load_client_config_file(std::string file_name);
+
public:
void Dump(FILE *fd);
void DumpCsv(FILE *fd);
@@ -3885,12 +3887,12 @@ public:
double get_total_tx_bps();
uint32_t get_total_repeat_flows();
double get_delta_flow_is_sec();
- bool get_is_mac_conf() { return m_mac_info.is_configured();}
+
public:
- std::vector<CFlowGeneratorRec *> m_cap_gen; /* global info */
- CFlowsYamlInfo m_yaml_info; /* global yaml*/
- std::vector<CFlowGenListPerThread *> m_threads_info;
- CFlowGenListMac m_mac_info;
+ std::vector<CFlowGeneratorRec *> m_cap_gen; /* global info */
+ CFlowsYamlInfo m_yaml_info; /* global yaml*/
+ std::vector<CFlowGenListPerThread *> m_threads_info;
+ ClientCfgDB m_client_config_info;
};
@@ -3930,9 +3932,8 @@ inline void CCapFileFlowInfo::generate_flow(CTupleTemplateGeneratorSmart * tup
node->m_src_idx = tuple.getClientId();
node->m_dest_idx = tuple.getServerId();
node->m_src_port = tuple.getClientPort();
- memcpy(&node->m_src_mac,
- tuple.getClientMac(),
- sizeof(mac_addr_align_t));
+ node->m_client_cfg = tuple.getClientCfg();
+
node->m_plugin_info =(void *)0;
if ( unlikely( CGlobalInfo::is_learn_mode() ) ){
diff --git a/src/dpdk22/drivers/net/i40e/i40e_ethdev.c b/src/dpdk22/drivers/net/i40e/i40e_ethdev.c
index 5646eb53..623c071c 100644
--- a/src/dpdk22/drivers/net/i40e/i40e_ethdev.c
+++ b/src/dpdk22/drivers/net/i40e/i40e_ethdev.c
@@ -2145,10 +2145,16 @@ i40e_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
pf->main_vsi->eth_stats.rx_multicast +
pf->main_vsi->eth_stats.rx_broadcast -
pf->main_vsi->eth_stats.rx_discards;
- stats->opackets = pf->main_vsi->eth_stats.tx_unicast +
- pf->main_vsi->eth_stats.tx_multicast +
- pf->main_vsi->eth_stats.tx_broadcast;
- stats->ibytes = ns->eth.rx_bytes;
+
+ stats->opackets = ns->eth.tx_unicast +ns->eth.tx_multicast +ns->eth.tx_broadcast;
+ /*TREX PATCH move to global transmit and not pf->vsi and we have two high and low priorty
+ pf->main_vsi->eth_stats.tx_unicast +
+ pf->main_vsi->eth_stats.tx_multicast +
+ pf->main_vsi->eth_stats.tx_broadcast;
+ */
+
+ stats->ibytes = pf->main_vsi->eth_stats.rx_bytes;
+
stats->obytes = ns->eth.tx_bytes;
stats->oerrors = ns->eth.tx_errors +
pf->main_vsi->eth_stats.tx_errors;
diff --git a/src/flow_stat.cpp b/src/flow_stat.cpp
index cb7a1bf9..8c2f2566 100644
--- a/src/flow_stat.cpp
+++ b/src/flow_stat.cpp
@@ -784,10 +784,6 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) {
m_parser->set_ip_id(IP_ID_RESERVE_BASE + hw_id);
stream->m_rx_check.m_hw_id = hw_id;
} else {
- struct flow_stat_payload_header *fsp_head = (struct flow_stat_payload_header *)
- (stream->m_pkt.binary + stream->m_pkt.len - sizeof(struct flow_stat_payload_header));
- fsp_head->hw_id = hw_id;
- fsp_head->magic = FLOW_STAT_PAYLOAD_MAGIC;
m_parser->set_ip_id(FLOW_STAT_PAYLOAD_IP_ID);
// for payload rules, we use the range right after ip id rules
stream->m_rx_check.m_hw_id = hw_id + MAX_FLOW_STATS;
@@ -800,6 +796,9 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) {
if (m_num_started_streams == 0) {
send_start_stop_msg_to_rx(true); // First transmitting stream. Rx core should start reading packets;
+ //also good time to zero global counters
+ memset(m_rx_cant_count_err, 0, sizeof(m_rx_cant_count_err));
+ memset(m_tx_cant_count_err, 0, sizeof(m_tx_cant_count_err));
// wait to make sure that message is acknowledged. RX core might be in deep sleep mode, and we want to
// start transmitting packets only after it is working, otherwise, packets will get lost.
@@ -914,7 +913,7 @@ int CFlowStatRuleMgr::stop_stream(TrexStream * stream) {
m_num_started_streams--;
assert (m_num_started_streams >= 0);
if (m_num_started_streams == 0) {
- send_start_stop_msg_to_rx(false); // No more transmittig streams. Rx core shoulde get into idle loop.
+ send_start_stop_msg_to_rx(false); // No more transmittig streams. Rx core should get into idle loop.
}
return 0;
}
@@ -951,6 +950,7 @@ bool CFlowStatRuleMgr::dump_json(std::string & s_json, std::string & l_json, boo
tx_per_flow_t tx_stats[MAX_FLOW_STATS];
tx_per_flow_t tx_stats_payload[MAX_FLOW_STATS_PAYLOAD];
rfc2544_info_t rfc2544_info[MAX_FLOW_STATS_PAYLOAD];
+ CRxCoreErrCntrs rx_err_cntrs;
Json::FastWriter writer;
Json::Value s_root;
Json::Value l_root;
@@ -977,6 +977,7 @@ bool CFlowStatRuleMgr::dump_json(std::string & s_json, std::string & l_json, boo
}
m_api->get_rfc2544_info(rfc2544_info, 0, m_max_hw_id_payload, false);
+ m_api->get_rx_err_cntrs(&rx_err_cntrs);
// read hw counters, and update
for (uint8_t port = 0; port < m_num_ports; port++) {
@@ -1050,10 +1051,21 @@ bool CFlowStatRuleMgr::dump_json(std::string & s_json, std::string & l_json, boo
// general per port data
for (uint8_t port = 0; port < m_num_ports; port++) {
std::string str_port = static_cast<std::ostringstream*>( &(std::ostringstream() << int(port) ) )->str();
- if (m_rx_cant_count_err[port] != 0)
- s_data_section["port_data"][str_port]["rx_err"] = m_rx_cant_count_err[port];
- if (m_tx_cant_count_err[port] != 0)
- s_data_section["port_data"][str_port]["tx_err"] = m_tx_cant_count_err[port];
+ if ((m_rx_cant_count_err[port] != 0) || baseline)
+ s_data_section["global"]["rx_err"][str_port] = m_rx_cant_count_err[port];
+ if ((m_tx_cant_count_err[port] != 0) || baseline)
+ s_data_section["global"]["tx_err"][str_port] = m_tx_cant_count_err[port];
+ }
+
+ // payload rules rx errors
+ uint64_t tmp_cnt;
+ tmp_cnt = rx_err_cntrs.get_bad_header();
+ if (tmp_cnt || baseline) {
+ l_data_section["global"]["bad_hdr"] = Json::Value::UInt64(tmp_cnt);
+ }
+ tmp_cnt = rx_err_cntrs.get_old_flow();
+ if (tmp_cnt || baseline) {
+ l_data_section["global"]["old_flow"] = Json::Value::UInt64(tmp_cnt);
}
flow_stat_user_id_map_it_t it;
diff --git a/src/flow_stat.h b/src/flow_stat.h
index a2137198..25d16173 100644
--- a/src/flow_stat.h
+++ b/src/flow_stat.h
@@ -35,7 +35,8 @@
// Do not change this value. In i350 cards, we filter according to first byte of IP ID
// In other places, we identify packets by if (ip_id > IP_ID_RESERVE_BASE)
#define IP_ID_RESERVE_BASE 0xff00
-#define FLOW_STAT_PAYLOAD_MAGIC 0xABCD
+#define FLOW_STAT_PAYLOAD_MAGIC 0xAB
+#define FLOW_STAT_PAYLOAD_INITIAL_FLOW_SEQ 0x01
extern const uint16_t FLOW_STAT_PAYLOAD_IP_ID;
typedef std::map<uint32_t, uint16_t> flow_stat_map_t;
@@ -44,7 +45,8 @@ typedef std::map<uint32_t, uint16_t>::iterator flow_stat_map_it_t;
class CRxCoreStateless;
struct flow_stat_payload_header {
- uint16_t magic;
+ uint8_t magic;
+ uint8_t flow_seq;
uint16_t hw_id;
uint32_t seq;
uint64_t time_stamp;
diff --git a/src/gtest/tuple_gen_test.cpp b/src/gtest/tuple_gen_test.cpp
index f3b9fa1e..fa760c6d 100755
--- a/src/gtest/tuple_gen_test.cpp
+++ b/src/gtest/tuple_gen_test.cpp
@@ -25,6 +25,22 @@ limitations under the License.
/* TEST case for CClientInfo*/
+class CClientInfo : public CSimpleClientInfo<CIpInfo> {
+public:
+ CClientInfo() : CSimpleClientInfo<CIpInfo>(0) {
+
+ }
+};
+
+class CClientInfoL : public CSimpleClientInfo<CIpInfoL> {
+public:
+ CClientInfoL() : CSimpleClientInfo<CIpInfoL>(0) {
+
+ }
+};
+
+static ClientCfgDB g_dummy;
+
class CClientInfoUT {
public:
CClientInfoUT(CClientInfo *a) {
@@ -157,7 +173,7 @@ TEST(CClientInfoLTest, get_new_free_port) {
TEST(tuple_gen,clientPoolL) {
CClientPool gen;
gen.Create(cdSEQ_DIST,
- 0x10000001, 0x10000f01, 64000,1,NULL,false,
+ 0x10000001, 0x10000f01, 64000,1, g_dummy,
0,0);
CTupleBase result;
uint32_t result_src;
@@ -181,7 +197,7 @@ TEST(tuple_gen,clientPoolL) {
TEST(tuple_gen,clientPool) {
CClientPool gen;
gen.Create(cdSEQ_DIST,
- 0x10000001, 0x10000021, 64000,1000,NULL,false,
+ 0x10000001, 0x10000021, 64000,1000, g_dummy,
0,0);
CTupleBase result;
uint32_t result_src;
@@ -273,7 +289,7 @@ TEST(tuple_gen,GenerateTuple2) {
CClientPool c_gen;
CClientPool c_gen_2;
c_gen.Create(cdSEQ_DIST,
- 0x10000001, 0x1000000f, 64000,4,NULL,false,
+ 0x10000001, 0x1000000f, 64000,4, g_dummy,
0,0);
CServerPool s_gen;
CServerPool s_gen_2;
@@ -302,7 +318,7 @@ TEST(tuple_gen,GenerateTuple2) {
c_gen.Delete();
// EXPECT_EQ((size_t)0, gen.m_clients.size());
c_gen.Create(cdSEQ_DIST,
- 0x10000001, 0x1000000f, 64000,400,NULL,false,
+ 0x10000001, 0x1000000f, 64000,400, g_dummy,
0,0);
s_gen.Create(cdSEQ_DIST,
0x30000001, 0x30000001, 64000,10);
@@ -325,39 +341,6 @@ TEST(tuple_gen,GenerateTuple2) {
}
-TEST(tuple_gen,GenerateTupleMac) {
- CFlowGenList fl;
- fl.Create();
- fl.load_from_mac_file("avl/mac_uit.yaml");
-
- CClientPool gen;
- gen.Create(cdSEQ_DIST,
- 0x10000001, 0x1000000f, 64000,2, &fl.m_mac_info,true,0,0);
-
- CTupleBase result;
- uint32_t result_src;
- uint16_t result_port;
- mac_addr_align_t* result_mac;
- for(int i=0;i<10;i++) {
- gen.GenerateTuple(result);
- printf(" C:%x P:%d \n",result.getClient(),result.getClientPort());
-
- result_src = result.getClient();
- result_port = result.getClientPort();
- result_mac = result.getClientMac();
- EXPECT_EQ(result_src, (uint32_t)(0x10000001+i%2));
- EXPECT_EQ(result_port, 1024+i/2);
- printf("i:%d,mac:%d\n",i,result_mac->mac[3]);
- if (i%2==0)
- EXPECT_EQ(result_mac->mac[3], 5);
- else
- EXPECT_EQ(result_mac->mac[3], 1);
- }
-
- gen.Delete();
-// EXPECT_EQ((size_t)0, gen.m_clients.size());
-}
-
TEST(tuple_gen,split1) {
CIpPortion portion;
@@ -421,7 +404,7 @@ TEST(tuple_gen,split2) {
TEST(tuple_gen,template1) {
CTupleGeneratorSmart gen;
gen.Create(1, 1);
- gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,NULL,0,0);
+ gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4, g_dummy, 0, 0);
gen.add_server_pool(cdSEQ_DIST,0x30000001,0x40000001,64000,4,false);
CTupleTemplateGeneratorSmart template_1;
template_1.Create(&gen,0,0);
@@ -446,7 +429,7 @@ TEST(tuple_gen,template1) {
TEST(tuple_gen,template2) {
CTupleGeneratorSmart gen;
gen.Create(1, 1);
- gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,NULL,0,0);
+ gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,g_dummy,0,0);
gen.add_server_pool(cdSEQ_DIST,0x30000001,0x40000001,64000,4,false);
CTupleTemplateGeneratorSmart template_1;
template_1.Create(&gen,0,0);
@@ -475,7 +458,7 @@ TEST(tuple_gen,template2) {
TEST(tuple_gen,no_free) {
CTupleGeneratorSmart gen;
gen.Create(1, 1);
- gen.add_client_pool(cdSEQ_DIST,0x10000001,0x10000001,64000,4,NULL,0,0);
+ gen.add_client_pool(cdSEQ_DIST,0x10000001,0x10000001,64000,4,g_dummy,0,0);
gen.add_server_pool(cdSEQ_DIST,0x30000001,0x400000ff,64000,4,false);
CTupleTemplateGeneratorSmart template_1;
template_1.Create(&gen,0,0);
@@ -497,7 +480,7 @@ TEST(tuple_gen,no_free) {
TEST(tuple_gen,try_to_free) {
CTupleGeneratorSmart gen;
gen.Create(1, 1);
- gen.add_client_pool(cdSEQ_DIST,0x10000001,0x10000001,64000,4,NULL,0,0);
+ gen.add_client_pool(cdSEQ_DIST,0x10000001,0x10000001,64000,4,g_dummy,0,0);
gen.add_server_pool(cdSEQ_DIST,0x30000001,0x400000ff,64000,4,false);
CTupleTemplateGeneratorSmart template_1;
template_1.Create(&gen,0,0);
@@ -524,7 +507,7 @@ TEST(tuple_gen,try_to_free) {
TEST(tuple_gen_2,GenerateTuple) {
CTupleGeneratorSmart gen;
gen.Create(1, 1);
- gen.add_client_pool(cdSEQ_DIST,0x10000001,0x10000f01,64000,4,NULL,0,0);
+ gen.add_client_pool(cdSEQ_DIST,0x10000001,0x10000f01,64000,4,g_dummy,0,0);
gen.add_server_pool(cdSEQ_DIST,0x30000001,0x40000001,64000,4,false);
CTupleTemplateGeneratorSmart template_1;
template_1.Create(&gen,0,0);
@@ -552,7 +535,7 @@ TEST(tuple_gen_2,GenerateTuple) {
TEST(tuple_gen_2,GenerateTuple2) {
CTupleGeneratorSmart gen;
gen.Create(1, 1);
- gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,NULL,0,0);
+ gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,g_dummy,0,0);
gen.add_server_pool(cdSEQ_DIST,0x30000001,0x40000001,64000,4,false);
CTupleTemplateGeneratorSmart template_1;
template_1.Create(&gen,0,0);
@@ -576,7 +559,7 @@ TEST(tuple_gen_2,GenerateTuple2) {
gen.Delete();
// EXPECT_EQ((size_t)0, gen.m_clients.size());
gen.Create(1, 1);
- gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,NULL,0,0);
+ gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,g_dummy,0,0);
gen.add_server_pool(cdSEQ_DIST,0x30000001,0x40000001,64000,4,false);
template_1.Create(&gen,0,0);
for(int i=0;i<200;i++) {
@@ -599,7 +582,7 @@ TEST(tuple_gen_2,GenerateTuple2) {
TEST(tuple_gen_2,template1) {
CTupleGeneratorSmart gen;
gen.Create(1, 1);
- gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,NULL,0,0);
+ gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,g_dummy,0,0);
gen.add_server_pool(cdSEQ_DIST,0x30000001,0x40000001,64000,4,false);
CTupleTemplateGeneratorSmart template_1;
template_1.Create(&gen,0,0);
@@ -626,7 +609,7 @@ TEST(tuple_gen_2,template1) {
TEST(tuple_gen_2,template2) {
CTupleGeneratorSmart gen;
gen.Create(1, 1);
- gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,NULL,0,0);
+ gen.add_client_pool(cdSEQ_DIST,0x10000001,0x1000000f,64000,4,g_dummy,0,0);
gen.add_server_pool(cdSEQ_DIST,0x30000001,0x40000001,64000,4,false);
CTupleTemplateGeneratorSmart template_1;
template_1.Create(&gen,0,0);
diff --git a/src/internal_api/trex_platform_api.h b/src/internal_api/trex_platform_api.h
index b0294883..7037584b 100644
--- a/src/internal_api/trex_platform_api.h
+++ b/src/internal_api/trex_platform_api.h
@@ -149,6 +149,7 @@ public:
virtual int get_flow_stats(uint8_t port_id, void *stats, void *tx_stats, int min, int max, bool reset
, TrexPlatformApi::driver_stat_cap_e type) const = 0;
virtual int get_rfc2544_info(void *rfc2544_info, int min, int max, bool reset) const = 0;
+ virtual int get_rx_err_cntrs(void *rx_err_cntrs) const = 0;
virtual int reset_hw_flow_stats(uint8_t port_id) const = 0;
virtual void get_port_num(uint8_t &port_num) const = 0;
virtual int add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const = 0;
@@ -183,6 +184,7 @@ public:
int get_flow_stats(uint8_t port_id, void *stats, void *tx_stats, int min, int max, bool reset
, TrexPlatformApi::driver_stat_cap_e type) const;
int get_rfc2544_info(void *rfc2544_info, int min, int max, bool reset) const;
+ int get_rx_err_cntrs(void *rx_err_cntrs) const;
int reset_hw_flow_stats(uint8_t port_id) const;
void get_port_num(uint8_t &port_num) const;
int add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const;
@@ -242,6 +244,7 @@ public:
int get_flow_stats(uint8_t port_id, void *stats, void *tx_stats, int min, int max, bool reset
, TrexPlatformApi::driver_stat_cap_e type) const {return 0;};
virtual int get_rfc2544_info(void *rfc2544_info, int min, int max, bool reset) const {return 0;};
+ virtual int get_rx_err_cntrs(void *rx_err_cntrs) const {return 0;};
virtual int reset_hw_flow_stats(uint8_t port_id) const {return 0;};
virtual void get_port_num(uint8_t &port_num) const {port_num = 2;};
virtual int add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const {return 0;}
diff --git a/src/mac_mapping.h b/src/mac_mapping.h
deleted file mode 100644
index 84151e8c..00000000
--- a/src/mac_mapping.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#ifndef MAC_MAPPING_H_
-#define MAC_MAPPING_H_
-
-#define INUSED 0
-#define UNUSED 1
-typedef struct mac_addr_align_ {
-public:
- uint8_t mac[6];
- uint8_t inused;
- uint8_t pad;
-} mac_addr_align_t;
-
-typedef struct mac_mapping_ {
- mac_addr_align_t mac;
- uint32_t ip;
-} mac_mapping_t;
-
-class CFlowGenListMac {
-public:
- CFlowGenListMac() {
- set_configured(false);
- }
-
- std::map<uint32_t, mac_addr_align_t> &
- get_mac_info () {
- return m_mac_info;
- }
-
- bool is_configured() {
- return is_mac_info_configured;
- }
-
- void set_configured(bool is_conf) {
- is_mac_info_configured = is_conf;
- }
-
- void clear() {
- set_configured(false);
- m_mac_info.clear();
- }
-
- uint32_t is_mac_exist(uint32_t ip) {
- if (is_configured()) {
- return m_mac_info.count(ip);
- } else {
- return 0;
- }
- }
- mac_addr_align_t* get_mac_addr_by_ip(uint32_t ip) {
- if (is_mac_exist(ip)!=0) {
- return &(m_mac_info[ip]);
- }
- return NULL;
- }
-private:
- bool is_mac_info_configured;
- std::map<uint32_t, mac_addr_align_t> m_mac_info; /* global mac info loaded form mac_file*/
-};
-
-#endif //MAC_MAPPING_H_
diff --git a/src/main.cpp b/src/main.cpp
index 62eee880..de6cef45 100755
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -22,6 +22,7 @@ limitations under the License.
#include "bp_sim.h"
#include "os_time.h"
+#include "trex_client_config.h"
#include <unordered_map>
#include <string>
@@ -35,7 +36,7 @@ using namespace std;
// An enum for all the option types
enum { OPT_HELP, OPT_CFG, OPT_NODE_DUMP, OP_STATS,
- OPT_FILE_OUT, OPT_UT, OPT_PCAP, OPT_IPV6, OPT_MAC_FILE,
+ OPT_FILE_OUT, OPT_UT, OPT_PCAP, OPT_IPV6, OPT_CLIENT_CFG_FILE,
OPT_SL, OPT_DP_CORE_COUNT, OPT_DP_CORE_INDEX, OPT_LIMIT,
OPT_DRY_RUN};
@@ -61,22 +62,22 @@ typedef enum {
*/
static CSimpleOpt::SOption parser_options[] =
{
- { OPT_HELP, "-?", SO_NONE },
- { OPT_HELP, "-h", SO_NONE },
- { OPT_HELP, "--help", SO_NONE },
- { OPT_UT, "--ut", SO_NONE },
- { OP_STATS, "-s", SO_NONE },
- { OPT_CFG, "-f", SO_REQ_SEP },
- { OPT_MAC_FILE, "--mac", SO_REQ_SEP },
- { OPT_FILE_OUT , "-o", SO_REQ_SEP },
- { OPT_NODE_DUMP , "-v", SO_REQ_SEP },
- { OPT_PCAP, "--pcap", SO_NONE },
- { OPT_IPV6, "--ipv6", SO_NONE },
- { OPT_SL, "--sl", SO_NONE },
- { OPT_DP_CORE_COUNT, "--cores", SO_REQ_SEP },
- { OPT_DP_CORE_INDEX, "--core_index", SO_REQ_SEP },
- { OPT_LIMIT, "--limit", SO_REQ_SEP },
- { OPT_DRY_RUN, "--dry", SO_NONE },
+ { OPT_HELP, "-?", SO_NONE },
+ { OPT_HELP, "-h", SO_NONE },
+ { OPT_HELP, "--help", SO_NONE },
+ { OPT_UT, "--ut", SO_NONE },
+ { OP_STATS, "-s", SO_NONE },
+ { OPT_CFG, "-f", SO_REQ_SEP },
+ { OPT_CLIENT_CFG_FILE, "--client_cfg", SO_REQ_SEP },
+ { OPT_FILE_OUT , "-o", SO_REQ_SEP },
+ { OPT_NODE_DUMP , "-v", SO_REQ_SEP },
+ { OPT_PCAP, "--pcap", SO_NONE },
+ { OPT_IPV6, "--ipv6", SO_NONE },
+ { OPT_SL, "--sl", SO_NONE },
+ { OPT_DP_CORE_COUNT, "--cores", SO_REQ_SEP },
+ { OPT_DP_CORE_INDEX, "--core_index", SO_REQ_SEP },
+ { OPT_LIMIT, "--limit", SO_REQ_SEP },
+ { OPT_DRY_RUN, "--dry", SO_NONE },
SO_END_OF_OPTIONS
@@ -159,8 +160,8 @@ static int parse_options(int argc,
po->cfg_file = args.OptionArg();
break;
- case OPT_MAC_FILE:
- po->mac_file = args.OptionArg();
+ case OPT_CLIENT_CFG_FILE:
+ po->client_cfg_file = args.OptionArg();
break;
case OPT_FILE_OUT:
diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp
index e98c2305..c36c3eae 100644
--- a/src/main_dpdk.cpp
+++ b/src/main_dpdk.cpp
@@ -547,12 +547,10 @@ enum { OPT_HELP,
OPT_L_PKT_MODE,
OPT_NO_FLOW_CONTROL,
OPT_RX_CHECK_HOPS,
- OPT_MAC_FILE,
+ OPT_CLIENT_CFG_FILE,
OPT_NO_KEYBOARD_INPUT,
- OPT_VLAN,
OPT_VIRT_ONE_TX_RX_QUEUE,
OPT_PREFIX,
- OPT_MAC_SPLIT,
OPT_SEND_DEBUG_PKT,
OPT_NO_WATCHDOG,
OPT_ALLOW_COREDUMP
@@ -610,12 +608,10 @@ static CSimpleOpt::SOption parser_options[] =
{ OPT_LEARN_VERIFY, "--learn-verify", SO_NONE },
{ OPT_L_PKT_MODE, "--l-pkt-mode", SO_REQ_SEP },
{ OPT_NO_FLOW_CONTROL, "--no-flow-control-change", SO_NONE },
- { OPT_VLAN, "--vlan", SO_NONE },
- { OPT_MAC_FILE, "--mac", SO_REQ_SEP },
+ { OPT_CLIENT_CFG_FILE, "--client_cfg", SO_REQ_SEP },
{ OPT_NO_KEYBOARD_INPUT ,"--no-key", SO_NONE },
{ OPT_VIRT_ONE_TX_RX_QUEUE, "--vm-sim", SO_NONE },
{ OPT_PREFIX, "--prefix", SO_REQ_SEP },
- { OPT_MAC_SPLIT, "--mac-spread", SO_REQ_SEP },
{ OPT_SEND_DEBUG_PKT, "--send-debug-pkt", SO_REQ_SEP },
{ OPT_MBUF_FACTOR , "--mbuf-factor", SO_REQ_SEP },
{ OPT_NO_WATCHDOG , "--no-watchdog", SO_NONE },
@@ -641,7 +637,7 @@ static int usage(){
printf(" options \n\n");
- printf(" --mac [file] : YAML file with <client ip, mac addr> configuration \n");
+ printf(" --client_cfg [file] : YAML file which describes clients configuration\n");
printf(" \n\n");
printf(" -c [number of threads] : default is 1. number of threads to allocate for each dual ports. \n");
printf(" \n");
@@ -716,8 +712,6 @@ static int usage(){
printf(" --no-key : daemon mode, don't get input from keyboard \n");
printf(" --no-flow-control-change : By default TRex disables flow-control. If this option is given, it does not touch it\n");
printf(" --prefix : for multi trex, each instance should have a different name \n");
- printf(" --mac-spread : Spread the destination mac-order by this factor. e.g 2 will generate the traffic to 2 devices DEST-MAC ,DEST-MAC+1 \n");
- printf(" maximum is up to 128 devices \n");
printf(" --mbuf-factor : factor for packet memory \n");
printf(" \n");
printf(" --no-watchdog : disable watchdog \n");
@@ -725,6 +719,7 @@ static int usage(){
printf(" --allow-coredump : allow a creation of core dump \n");
printf(" \n");
printf(" --vm-sim : simulate vm with driver of one input queue and one output queue \n");
+
printf(" \n");
printf(" Examples: ");
printf(" basic trex run for 10 sec and multiplier of x10 \n");
@@ -740,7 +735,7 @@ static int usage(){
printf("\n");
printf("\n");
- printf(" Copyright (c) 2015-2015 Cisco Systems, Inc. \n");
+ printf(" Copyright (c) 2015-2016 Cisco Systems, Inc. \n");
printf(" \n");
printf(" Licensed under the Apache License, Version 2.0 (the 'License') \n");
printf(" you may not use this file except in compliance with the License. \n");
@@ -825,8 +820,8 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t
po->preview.set_no_keyboard(true);
break;
- case OPT_MAC_FILE :
- po->mac_file = args.OptionArg();
+ case OPT_CLIENT_CFG_FILE :
+ po->client_cfg_file = args.OptionArg();
break;
case OPT_PLAT_CFG_FILE :
@@ -841,9 +836,6 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t
po->preview.set_ipv6_mode_enable(true);
break;
- case OPT_VLAN:
- po->preview.set_vlan_mode_enable(true);
- break;
case OPT_LEARN :
po->m_learn_mode = CParserOption::LEARN_MODE_IP_OPTION;
@@ -975,13 +967,6 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t
po->prefix = args.OptionArg();
break;
- case OPT_MAC_SPLIT:
- sscanf(args.OptionArg(),"%d", &tmp_data);
- po->m_mac_splitter = (uint8_t)tmp_data;
- po->preview.set_mac_ip_features_enable(true);
- po->preview.setDestMacSplit(true);
- break;
-
case OPT_SEND_DEBUG_PKT:
sscanf(args.OptionArg(),"%d", &tmp_data);
po->m_debug_pkt_proto = (uint8_t)tmp_data;
@@ -1005,17 +990,9 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t
parse_err("Please provide single run mode (e.g. batch or interactive)");
}
- if ( po->m_mac_splitter > 128 ){
- std::stringstream ss;
- ss << "maximum mac spreading is 128 you set it to: " << po->m_mac_splitter;
- parse_err(ss.str());
- }
-
- if ( CGlobalInfo::is_learn_mode() ){
- if ( po->preview.get_ipv6_mode_enable() ){
- parse_err("--learn mode is not supported with --ipv6, beacuse there is not such thing NAT66 ( ipv6-ipv6) \n" \
- "if you think it is important,open a defect \n");
- }
+ if (CGlobalInfo::is_learn_mode() && po->preview.get_ipv6_mode_enable()) {
+ parse_err("--learn mode is not supported with --ipv6, beacuse there is not such thing NAT66 ( ipv6-ipv6) \n" \
+ "if you think it is important,open a defect \n");
}
if (po->preview.get_is_rx_check_enable() || po->is_latency_enabled() || CGlobalInfo::is_learn_mode()) {
@@ -1755,7 +1732,9 @@ public:
virtual void flush_dp_rx_queue(void);
virtual int flush_tx_queue(void);
__attribute__ ((noinline)) void flush_rx_queue();
- __attribute__ ((noinline)) void update_mac_addr(CGenNode * node,uint8_t *p);
+ __attribute__ ((noinline)) void handle_slowpath_features(CGenNode *node, rte_mbuf_t *m, uint8_t *p, pkt_dir_t dir);
+
+ void apply_client_cfg(const ClientCfg *cfg, rte_mbuf_t *m, pkt_dir_t dir, uint8_t *p);
bool process_rx_pkt(pkt_dir_t dir,rte_mbuf_t * m);
@@ -1788,6 +1767,8 @@ protected:
rte_mbuf_t *m,
CVirtualIFPerSideStats * lp_stats);
+ void add_vlan(rte_mbuf_t *m, uint16_t vlan_id);
+
protected:
uint8_t m_core_id;
uint16_t m_mbuf_cache;
@@ -2012,27 +1993,6 @@ void CCoreEthIF::send_one_pkt(pkt_dir_t dir,
lp_port->m_len = 0;
}
-
-void CCoreEthIF::update_mac_addr(CGenNode * node,uint8_t *p){
-
- if ( CGlobalInfo::m_options.preview.getDestMacSplit() ) {
- p[5]+= (node->m_src_ip % CGlobalInfo::m_options.m_mac_splitter);
- }
-
- if ( unlikely( CGlobalInfo::m_options.preview.get_mac_ip_mapping_enable() ) ) {
- /* mac mapping file is configured
- */
- if ( node->is_initiator_pkt() && (node->m_src_mac.inused==INUSED)) {
- memcpy(p+6, &node->m_src_mac.mac, 6);
- }
- } else if ( unlikely( CGlobalInfo::m_options.preview.get_mac_ip_overide_enable() ) ){
- /* client side */
- if ( node->is_initiator_pkt() ){
- *((uint32_t*)(p+6))=PKT_NTOHL(node->m_src_ip);
- }
- }
-}
-
int CCoreEthIFStateless::send_node_flow_stat(rte_mbuf *m, CGenNodeStateless * node_sl, CCorePerPort * lp_port
, CVirtualIFPerSideStats * lp_stats, bool is_const) {
// Defining this makes 10% percent packet loss. 1% packet reorder.
@@ -2055,7 +2015,8 @@ int CCoreEthIFStateless::send_node_flow_stat(rte_mbuf *m, CGenNodeStateless * no
mi = node_sl->alloc_flow_stat_mbuf(m, fsp_head, is_const);
fsp_head->seq = lp_stats->m_lat_data[hw_id_payload].get_seq_num();
fsp_head->hw_id = hw_id_payload;
- fsp_head->magic = lp_stats->m_lat_data[hw_id_payload].get_magic();
+ fsp_head->flow_seq = lp_stats->m_lat_data[hw_id_payload].get_flow_seq();
+ fsp_head->magic = FLOW_STAT_PAYLOAD_MAGIC;
lp_stats->m_lat_data[hw_id_payload].inc_seq_num();
#ifdef ERR_CNTRS_TEST
@@ -2157,8 +2118,59 @@ int CCoreEthIFStateless::handle_slow_path_node(CGenNode * no) {
return (-1);
}
+void CCoreEthIF::apply_client_cfg(const ClientCfg *cfg, rte_mbuf_t *m, pkt_dir_t dir, uint8_t *p) {
+
+ assert(cfg);
+
+ /* take the right direction config */
+ const ClientCfgDir &cfg_dir = ( (dir == CLIENT_SIDE) ? cfg->m_initiator : cfg->m_responder);
+
+ /* dst mac */
+ if (cfg_dir.has_dst_mac_addr()) {
+ memcpy(p, cfg_dir.get_dst_mac_addr(), 6);
+ }
+
+ /* src mac */
+ if (cfg_dir.has_src_mac_addr()) {
+ memcpy(p + 6, cfg_dir.get_src_mac_addr(), 6);
+ }
+
+ /* VLAN */
+ if (cfg_dir.has_vlan()) {
+ add_vlan(m, cfg_dir.get_vlan());
+ }
+}
+
+
+void CCoreEthIF::add_vlan(rte_mbuf_t *m, uint16_t vlan_id) {
+ m->ol_flags = PKT_TX_VLAN_PKT;
+ m->l2_len = 14;
+ m->vlan_tci = vlan_id;
+}
+
+/**
+ * slow path features goes here (avoid multiple IFs)
+ *
+ */
+void CCoreEthIF::handle_slowpath_features(CGenNode *node, rte_mbuf_t *m, uint8_t *p, pkt_dir_t dir) {
+
+
+ /* MAC ovverride */
+ if ( unlikely( CGlobalInfo::m_options.preview.get_mac_ip_overide_enable() ) ) {
+ /* client side */
+ if ( node->is_initiator_pkt() ) {
+ *((uint32_t*)(p+6)) = PKT_NTOHL(node->m_src_ip);
+ }
+ }
+
+ /* flag is faster than checking the node pointer (another cacheline) */
+ if ( unlikely(CGlobalInfo::m_options.preview.get_is_client_cfg_enable() ) ) {
+ apply_client_cfg(node->m_client_cfg, m, dir, p);
+ }
-int CCoreEthIF::send_node(CGenNode * node){
+}
+
+int CCoreEthIF::send_node(CGenNode * node) {
if ( unlikely( node->get_cache_mbuf() !=NULL ) ) {
pkt_dir_t dir;
@@ -2176,33 +2188,30 @@ int CCoreEthIF::send_node(CGenNode * node){
rte_mbuf_t * m=lp->generate_new_mbuf(node);
pkt_dir_t dir;
- bool single_port;
+ bool single_port;
- dir = node->cur_interface_dir();
+ dir = node->cur_interface_dir();
single_port = node->get_is_all_flow_from_same_dir() ;
+
if ( unlikely( CGlobalInfo::m_options.preview.get_vlan_mode_enable() ) ){
/* which vlan to choose 0 or 1*/
uint8_t vlan_port = (node->m_src_ip &1);
-
- /* set the vlan */
- m->ol_flags = PKT_TX_VLAN_PKT;
- m->l2_len =14;
- uint16_t vlan_id = CGlobalInfo::m_options.m_vlan_port[vlan_port];
-
+ uint16_t vlan_id = CGlobalInfo::m_options.m_vlan_port[vlan_port];
if (likely( vlan_id >0 ) ) {
- m->vlan_tci = vlan_id;
dir = dir ^ vlan_port;
}else{
/* both from the same dir but with VLAN0 */
- m->vlan_tci = CGlobalInfo::m_options.m_vlan_port[0];
+ vlan_id = CGlobalInfo::m_options.m_vlan_port[0];
dir = dir ^ 0;
}
+
+ add_vlan(m, vlan_id);
}
- CCorePerPort * lp_port=&m_ports[dir];
- CVirtualIFPerSideStats * lp_stats = &m_stats[dir];
+ CCorePerPort *lp_port = &m_ports[dir];
+ CVirtualIFPerSideStats *lp_stats = &m_stats[dir];
if (unlikely(m==0)) {
lp_stats->m_tx_alloc_error++;
@@ -2210,19 +2219,17 @@ int CCoreEthIF::send_node(CGenNode * node){
}
/* update mac addr dest/src 12 bytes */
- uint8_t *p=rte_pktmbuf_mtod(m, uint8_t*);
- uint8_t p_id=lp_port->m_port->get_port_id();
-
-
+ uint8_t *p = rte_pktmbuf_mtod(m, uint8_t*);
+ uint8_t p_id = lp_port->m_port->get_port_id();
+
memcpy(p,CGlobalInfo::m_options.get_dst_src_mac_addr(p_id),12);
- /* if customer enables both mac_file and get_mac_ip_overide,
- * we will apply mac_file.
- */
- if ( unlikely(CGlobalInfo::m_options.preview.get_mac_ip_features_enable() ) ) {
- update_mac_addr(node,p);
+ /* when slowpath features are on */
+ if ( unlikely( CGlobalInfo::m_options.preview.get_is_slowpath_features_on() ) ) {
+ handle_slowpath_features(node, m, p, dir);
}
+
if ( unlikely( node->is_rx_check_enabled() ) ) {
lp_stats->m_tx_rx_check_pkt++;
lp->do_generate_new_mbuf_rxcheck(m, node, single_port);
@@ -4197,12 +4204,24 @@ int CGlobalTRex::start_master_statefull() {
m_fl.Create();
m_fl.load_from_yaml(CGlobalInfo::m_options.cfg_file,get_cores_tx());
- if (CGlobalInfo::m_options.mac_file != "") {
- CGlobalInfo::m_options.preview.set_mac_ip_mapping_enable(true);
- m_fl.load_from_mac_file(CGlobalInfo::m_options.mac_file);
- m_fl.m_mac_info.set_configured(true);
- } else {
- m_fl.m_mac_info.set_configured(false);
+
+ /* client config */
+ if (CGlobalInfo::m_options.client_cfg_file != "") {
+ try {
+ m_fl.load_client_config_file(CGlobalInfo::m_options.client_cfg_file);
+ } catch (const std::runtime_error &e) {
+ std::cout << "\n*** " << e.what() << "\n\n";
+ exit(-1);
+ }
+ CGlobalInfo::m_options.preview.set_client_cfg_enable(true);
+ }
+
+ /* verify options */
+ try {
+ CGlobalInfo::m_options.verify();
+ } catch (const std::runtime_error &e) {
+ std::cout << "\n*** " << e.what() << "\n\n";
+ exit(-1);
}
m_expected_pps = m_fl.get_total_pps();
@@ -4339,20 +4358,15 @@ bool CCoreEthIF::process_rx_pkt(pkt_dir_t dir,
}
bool send=false;
+ // e1000 on ESXI hands us the packet with the ethernet FCS
+ if (parser.getPktSize() < m->pkt_len) {
+ rte_pktmbuf_trim(m, m->pkt_len - parser.getPktSize());
+ }
+
if ( get_is_stateless() ) {
// In stateless RX, we only care about flow stat packets
if ((parser.getIpId() & 0xff00) == IP_ID_RESERVE_BASE) {
send = true;
- if (parser.getIpId() == FLOW_STAT_PAYLOAD_IP_ID) {
- // e1000 on ESXI appends 4 bytes to the packet.
- // This is a best effort hack to get our latency info which we put at the end of the packet
- uint8_t *p = rte_pktmbuf_mtod(m, uint8_t*);
- struct flow_stat_payload_header *fsp_head = (struct flow_stat_payload_header *)
- (p + m->pkt_len - sizeof(struct flow_stat_payload_header));
- if (fsp_head->magic != FLOW_STAT_PAYLOAD_MAGIC) {
- rte_pktmbuf_trim(m, 4);
- }
- }
}
} else {
CLatencyPktMode *c_l_pkt_mode = g_trex.m_mg.c_l_pkt_mode;
@@ -5503,7 +5517,7 @@ void CTRexExtendedDriverBase40G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFSta
stats->ipackets = stats1.ipackets;
- stats->ibytes = stats1.ibytes + (stats1.ipackets<<2);
+ stats->ibytes = stats1.ibytes ;
stats->opackets = stats1.opackets;
stats->obytes = stats1.obytes + (stats1.opackets<<2);
@@ -5763,6 +5777,10 @@ int TrexDpdkPlatformApi::get_rfc2544_info(void *rfc2544_info, int min, int max,
return g_trex.m_rx_sl.get_rfc2544_info((rfc2544_info_t *)rfc2544_info, min, max, reset);
}
+int TrexDpdkPlatformApi::get_rx_err_cntrs(void *rx_err_cntrs) const {
+ return g_trex.m_rx_sl.get_rx_err_cntrs((CRxCoreErrCntrs *)rx_err_cntrs);
+}
+
int TrexDpdkPlatformApi::reset_hw_flow_stats(uint8_t port_id) const {
return g_trex.m_ports[port_id].reset_hw_flow_stats();
}
diff --git a/src/sim/trex_sim_stateful.cpp b/src/sim/trex_sim_stateful.cpp
index 88698cd1..7546644d 100644
--- a/src/sim/trex_sim_stateful.cpp
+++ b/src/sim/trex_sim_stateful.cpp
@@ -165,6 +165,24 @@ int load_list_of_cap_files(CParserOption * op){
CFlowGenList fl;
fl.Create();
fl.load_from_yaml(op->cfg_file,1);
+
+ if (op->client_cfg_file != "") {
+ try {
+ fl.load_client_config_file(op->client_cfg_file);
+ } catch (const std::runtime_error &e) {
+ std::cout << "\n*** " << e.what() << "\n\n";
+ exit(-1);
+ }
+ CGlobalInfo::m_options.preview.set_client_cfg_enable(true);
+ }
+
+ try {
+ CGlobalInfo::m_options.verify();
+ } catch (const std::runtime_error &e) {
+ std::cout << "\n*** " << e.what() << "\n\n";
+ exit(-1);
+ }
+
if ( op->preview.getVMode() >0 ) {
fl.DumpCsv(stdout);
}
@@ -596,5 +614,10 @@ int merge_2_cap_files_sip() {
int
SimStateful::run() {
assert( CMsgIns::Ins()->Create(4) );
- return load_list_of_cap_files(&CGlobalInfo::m_options);
+ try {
+ return load_list_of_cap_files(&CGlobalInfo::m_options);
+ } catch (const std::runtime_error &e) {
+ std::cout << "\n*** " << e.what() << "\n\n";
+ exit(-1);
+ }
}
diff --git a/src/stateless/cp/trex_streams_compiler.h b/src/stateless/cp/trex_streams_compiler.h
index 7e674364..171e3aff 100644
--- a/src/stateless/cp/trex_streams_compiler.h
+++ b/src/stateless/cp/trex_streams_compiler.h
@@ -244,7 +244,7 @@ public:
}
double get_factor_pps(double req_pps) const {
- if ( (req_pps - m_fixed.m_pps) < 0 ) {
+ if ( (req_pps - m_fixed.m_pps) <= 0 ) {
std::stringstream ss;
ss << "current stream configuration enforces a minimum rate of '" << m_fixed.m_pps << "' pps";
throw TrexException(ss.str());
diff --git a/src/stateless/dp/trex_stateless_dp_core.h b/src/stateless/dp/trex_stateless_dp_core.h
index af2187ae..31cb0be3 100644
--- a/src/stateless/dp/trex_stateless_dp_core.h
+++ b/src/stateless/dp/trex_stateless_dp_core.h
@@ -114,7 +114,7 @@ class TrexStatelessDpCore {
public:
- #define SCHD_OFFSET_DTIME (10.0/1000000.0)
+ #define SCHD_OFFSET_DTIME (100.0/1000000.0)
/* states */
enum state_e {
diff --git a/src/stateless/rx/trex_stateless_rx_core.cpp b/src/stateless/rx/trex_stateless_rx_core.cpp
index e5831129..853fc868 100644
--- a/src/stateless/rx/trex_stateless_rx_core.cpp
+++ b/src/stateless/rx/trex_stateless_rx_core.cpp
@@ -29,15 +29,15 @@
void CRFC2544Info::create() {
m_latency.Create();
- m_exp_magic = 0;
- m_prev_magic = 0;
+ m_exp_flow_seq = 0;
+ m_prev_flow_seq = 0;
reset();
}
// after calling stop, packets still arriving will be considered error
void CRFC2544Info::stop() {
- m_prev_magic = m_exp_magic;
- m_exp_magic = FLOW_STAT_PAYLOAD_MAGIC_NONE;
+ m_prev_flow_seq = m_exp_flow_seq;
+ m_exp_flow_seq = FLOW_STAT_PAYLOAD_INITIAL_FLOW_SEQ;
}
void CRFC2544Info::reset() {
@@ -195,82 +195,96 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t *
if (parser.get_ip_id(ip_id) == 0) {
if (is_flow_stat_id(ip_id)) {
uint16_t hw_id;
- bool good_packet = true;
+
if (is_flow_stat_payload_id(ip_id)) {
+ bool good_packet = true;
uint8_t *p = rte_pktmbuf_mtod(m, uint8_t*);
struct flow_stat_payload_header *fsp_head = (struct flow_stat_payload_header *)
(p + m->pkt_len - sizeof(struct flow_stat_payload_header));
hw_id = fsp_head->hw_id;
- CRFC2544Info &curr_rfc2544 = m_rfc2544[hw_id];
- if (unlikely(fsp_head->magic != curr_rfc2544.get_exp_magic())) {
- // bad magic.
- // Might be the first packet of a new flow, packet from an old flow or just garbage.
- if (fsp_head->magic == curr_rfc2544.get_prev_magic()) {
- // packet from previous flow using this hw_id that arrived late
- good_packet = false;
- } else {
- if (curr_rfc2544.no_magic()) {
- // first packet we see from this flow
- good_packet = true;
- curr_rfc2544.set_exp_magic(fsp_head->magic);
- } else {
- // garbage packet
+ CRFC2544Info *curr_rfc2544;
+
+ if (unlikely(fsp_head->magic != FLOW_STAT_PAYLOAD_MAGIC) || hw_id >= MAX_FLOW_STATS_PAYLOAD) {
+ good_packet = false;
+ m_err_cntrs.m_bad_header++;
+ } else {
+ curr_rfc2544 = &m_rfc2544[hw_id];
+
+ if (fsp_head->flow_seq != curr_rfc2544->get_exp_flow_seq()) {
+ // bad flow seq num
+ // Might be the first packet of a new flow, packet from an old flow, or garbage.
+
+ if (fsp_head->flow_seq == curr_rfc2544->get_prev_flow_seq()) {
+ // packet from previous flow using this hw_id that arrived late
good_packet = false;
+ m_err_cntrs.m_old_flow++;
+ } else {
+ if (curr_rfc2544->no_flow_seq()) {
+ // first packet we see from this flow
+ good_packet = true;
+ curr_rfc2544->set_exp_flow_seq(fsp_head->flow_seq);
+ } else {
+ // garbage packet
+ good_packet = false;
+ m_err_cntrs.m_bad_header++;
+ }
}
}
}
if (good_packet) {
uint32_t pkt_seq = fsp_head->seq;
- uint32_t exp_seq = curr_rfc2544.get_seq();
+ uint32_t exp_seq = curr_rfc2544->get_seq();
if (unlikely(pkt_seq != exp_seq)) {
if (pkt_seq < exp_seq) {
if (exp_seq - pkt_seq > 100000) {
// packet loss while we had wrap around
- curr_rfc2544.inc_seq_err(pkt_seq - exp_seq);
- curr_rfc2544.inc_seq_err_too_big();
- curr_rfc2544.set_seq(pkt_seq + 1);
+ curr_rfc2544->inc_seq_err(pkt_seq - exp_seq);
+ curr_rfc2544->inc_seq_err_too_big();
+ curr_rfc2544->set_seq(pkt_seq + 1);
} else {
if (pkt_seq == (exp_seq - 1)) {
- curr_rfc2544.inc_dup();
+ curr_rfc2544->inc_dup();
} else {
- curr_rfc2544.inc_ooo();
+ curr_rfc2544->inc_ooo();
// We thought it was lost, but it was just out of order
- curr_rfc2544.dec_seq_err();
+ curr_rfc2544->dec_seq_err();
}
- curr_rfc2544.inc_seq_err_too_low();
+ curr_rfc2544->inc_seq_err_too_low();
}
} else {
if (unlikely (pkt_seq - exp_seq > 100000)) {
// packet reorder while we had wrap around
if (pkt_seq == (exp_seq - 1)) {
- curr_rfc2544.inc_dup();
+ curr_rfc2544->inc_dup();
} else {
- curr_rfc2544.inc_ooo();
+ curr_rfc2544->inc_ooo();
// We thought it was lost, but it was just out of order
- curr_rfc2544.dec_seq_err();
+ curr_rfc2544->dec_seq_err();
}
- curr_rfc2544.inc_seq_err_too_low();
+ curr_rfc2544->inc_seq_err_too_low();
} else {
- // seq > curr_rfc2544.seq. Assuming lost packets
- curr_rfc2544.inc_seq_err(pkt_seq - exp_seq);
- curr_rfc2544.inc_seq_err_too_big();
- curr_rfc2544.set_seq(pkt_seq + 1);
+ // seq > curr_rfc2544->seq. Assuming lost packets
+ curr_rfc2544->inc_seq_err(pkt_seq - exp_seq);
+ curr_rfc2544->inc_seq_err_too_big();
+ curr_rfc2544->set_seq(pkt_seq + 1);
}
}
} else {
- curr_rfc2544.set_seq(pkt_seq + 1);
+ curr_rfc2544->set_seq(pkt_seq + 1);
}
lp->m_port.m_rx_pg_stat_payload[hw_id].add_pkts(1);
lp->m_port.m_rx_pg_stat_payload[hw_id].add_bytes(m->pkt_len + 4); // +4 for ethernet CRC
uint64_t d = (os_get_hr_tick_64() - fsp_head->time_stamp );
dsec_t ctime = ptime_convert_hr_dsec(d);
- curr_rfc2544.add_sample(ctime);
+ curr_rfc2544->add_sample(ctime);
}
} else {
hw_id = get_hw_id(ip_id);
- lp->m_port.m_rx_pg_stat[hw_id].add_pkts(1);
- lp->m_port.m_rx_pg_stat[hw_id].add_bytes(m->pkt_len + 4); // +4 for ethernet CRC
+ if (hw_id < MAX_FLOW_STATS) {
+ lp->m_port.m_rx_pg_stat[hw_id].add_pkts(1);
+ lp->m_port.m_rx_pg_stat[hw_id].add_bytes(m->pkt_len + 4); // +4 for ethernet CRC
+ }
}
}
}
@@ -431,6 +445,11 @@ int CRxCoreStateless::get_rfc2544_info(rfc2544_info_t *rfc2544_info, int min, in
return 0;
}
+int CRxCoreStateless::get_rx_err_cntrs(CRxCoreErrCntrs *rx_err) {
+ *rx_err = m_err_cntrs;
+ return 0;
+}
+
void CRxCoreStateless::set_working_msg_ack(bool val) {
sanb_smp_memory_barrier();
m_ack_start_work_msg = val;
diff --git a/src/stateless/rx/trex_stateless_rx_core.h b/src/stateless/rx/trex_stateless_rx_core.h
index 140fedf4..fc66704e 100644
--- a/src/stateless/rx/trex_stateless_rx_core.h
+++ b/src/stateless/rx/trex_stateless_rx_core.h
@@ -77,14 +77,11 @@ class CRFC2544Info {
inline void inc_seq_err_too_low() {m_seq_err_events_too_low++;}
inline void inc_dup() {m_dup++;}
inline void inc_ooo() {m_ooo++;}
- inline uint16_t get_exp_magic() {return m_exp_magic;}
- inline void set_exp_magic(uint16_t magic) {m_exp_magic = magic;}
- inline uint16_t get_prev_magic() {return m_prev_magic;}
- inline bool no_magic() {return (m_exp_magic == FLOW_STAT_PAYLOAD_MAGIC_NONE) ? true : false;}
+ inline uint16_t get_exp_flow_seq() {return m_exp_flow_seq;}
+ inline void set_exp_flow_seq(uint16_t flow_seq) {m_exp_flow_seq = flow_seq;}
+ inline uint16_t get_prev_flow_seq() {return m_prev_flow_seq;}
+ inline bool no_flow_seq() {return (m_exp_flow_seq == FLOW_STAT_PAYLOAD_INITIAL_FLOW_SEQ) ? true : false;}
private:
- enum payload_e {
- FLOW_STAT_PAYLOAD_MAGIC_NONE = 0
- };
uint32_t m_seq; // expected next seq num
CTimeHistogram m_latency; // latency info
CJitter m_jitter;
@@ -93,9 +90,28 @@ class CRFC2544Info {
uint64_t m_seq_err_events_too_low; // How many packet seq num lower than expected events we had
uint64_t m_ooo; // Packets we got with seq num lower than expected (We guess they are out of order)
uint64_t m_dup; // Packets we got with same seq num
- uint16_t m_exp_magic; // magic number we should see in latency header
- // magic number previously used with this id. We use this to catch packets arriving late from old flow
- uint16_t m_prev_magic;
+ uint16_t m_exp_flow_seq; // flow sequence number we should see in latency header
+ // flow sequence number previously used with this id. We use this to catch packets arriving late from an old flow
+ uint16_t m_prev_flow_seq;
+};
+
+class CRxCoreErrCntrs {
+ friend CRxCoreStateless;
+
+ public:
+ uint64_t get_bad_header() {return m_bad_header;}
+ uint64_t get_old_flow() {return m_old_flow;}
+ CRxCoreErrCntrs() {
+ reset();
+ }
+ void reset() {
+ m_bad_header = 0;
+ m_old_flow = 0;
+ }
+
+ private:
+ uint64_t m_bad_header;
+ uint64_t m_old_flow;
};
class CRxCoreStateless {
@@ -112,7 +128,11 @@ class CRxCoreStateless {
int get_rx_stats(uint8_t port_id, rx_per_flow_t *rx_stats, int min, int max, bool reset
, TrexPlatformApi::driver_stat_cap_e type);
int get_rfc2544_info(rfc2544_info_t *rfc2544_info, int min, int max, bool reset);
- void work() {m_state = STATE_WORKING;}
+ int get_rx_err_cntrs(CRxCoreErrCntrs *rx_err);
+ void work() {
+ m_state = STATE_WORKING;
+ m_err_cntrs.reset(); // When starting to work, reset global counters
+ }
void idle() {m_state = STATE_IDLE;}
void quit() {m_state = STATE_QUIT;}
bool is_working() const {return (m_ack_start_work_msg == true);}
@@ -149,6 +169,7 @@ class CRxCoreStateless {
CCpuUtlCp m_cpu_cp_u;
// Used for acking "work" (go out of idle) messages from cp
volatile bool m_ack_start_work_msg __rte_cache_aligned;
+ CRxCoreErrCntrs m_err_cntrs;
CRFC2544Info m_rfc2544[MAX_FLOW_STATS_PAYLOAD];
};
#endif
diff --git a/src/trex_client_config.cpp b/src/trex_client_config.cpp
new file mode 100644
index 00000000..b56037ea
--- /dev/null
+++ b/src/trex_client_config.cpp
@@ -0,0 +1,235 @@
+/*
+ Itay Marom
+ Cisco Systems, Inc.
+*/
+
+/*
+Copyright (c) 2016 Cisco Systems, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include <stdexcept>
+#include <sstream>
+#include <fstream>
+
+#include <yaml-cpp/yaml.h>
+
+#include "utl_yaml.h"
+#include "trex_client_config.h"
+#include "common/basic_utils.h"
+#include "bp_sim.h"
+
+void
+ClientCfgEntry::dump() const {
+
+ std::cout << "IP start: " << ip_to_str(m_ip_start) << "\n";
+ std::cout << "IP end: " << ip_to_str(m_ip_end) << "\n";
+
+ //m_cfg.dump();
+
+ #if 0
+ std::cout << "Init. MAC addr: ";
+ for (int i = 0; i < 6; i++) {
+ printf("%lx:", ( (m_initiator.m_dst_mac >> ( (6-i) * 8)) & 0xFF ) );
+ }
+ std::cout << "\n";
+
+ std::cout << "Init. VLAN: " << m_initiator.m_vlan << "\n";
+
+ std::cout << "Res. MAC addr: ";
+ for (int i = 0; i < 6; i++) {
+ printf("%lx:", ( (m_responder.m_dst_mac >> ( (6-i) * 8)) & 0xFF ) );
+ }
+ std::cout << "\n";
+
+ std::cout << "Res. VLAN: " << m_responder.m_vlan << "\n";
+ #endif
+}
+
+
+/**
+ * loads a YAML file containing
+ * the client groups configuration
+ *
+ */
+void
+ClientCfgDB::load_yaml_file(const std::string &filename) {
+ std::stringstream ss;
+
+ m_groups.clear();
+ m_cache_group = NULL;
+
+ /* wrapper parser */
+ YAML::Node root;
+ YAMLParserWrapper parser(filename);
+ parser.load(root);
+
+ /* parse globals */
+ m_under_vlan = parser.parse_bool(root, "vlan");
+
+ const YAML::Node &groups = parser.parse_list(root, "groups");
+
+ /* parse each group */
+ for (int i = 0; i < groups.size(); i++) {
+ parse_single_group(parser, groups[i]);
+ }
+
+ verify(parser);
+
+ m_is_empty = false;
+
+}
+
+/**
+ * reads a single group of clients from YAML
+ *
+ */
+void
+ClientCfgDB::parse_single_group(YAMLParserWrapper &parser, const YAML::Node &node) {
+ ClientCfgEntry group;
+
+ /* ip_start */
+ group.m_ip_start = parser.parse_ip(node, "ip_start");
+
+ /* ip_end */
+ group.m_ip_end = parser.parse_ip(node, "ip_end");
+
+ /* sanity check */
+ if (group.m_ip_end < group.m_ip_start) {
+ parser.parse_err("ip_end must be >= ip_start", node);
+ }
+
+ const YAML::Node &init = parser.parse_map(node, "initiator");
+ const YAML::Node &resp = parser.parse_map(node, "responder");
+
+ parse_dir(parser, init, group.m_cfg.m_initiator);
+ parse_dir(parser, resp, group.m_cfg.m_responder);
+
+
+ group.m_count = parser.parse_uint(node, "count", 0, UINT64_MAX, 1);
+
+ /* add to map with copying */
+ m_groups[group.m_ip_start] = group;
+
+}
+
+void
+ClientCfgDB::parse_dir(YAMLParserWrapper &parser, const YAML::Node &node, ClientCfgDir &dir) {
+ if (node.FindValue("src_mac")) {
+ dir.set_dst_mac_addr(parser.parse_mac_addr(node, "src_mac"));
+ }
+
+ if (node.FindValue("dst_mac")) {
+ dir.set_dst_mac_addr(parser.parse_mac_addr(node, "dst_mac"));
+ }
+
+ if (m_under_vlan) {
+ dir.set_vlan(parser.parse_uint(node, "vlan", 0, 0xfff));
+ } else {
+ if (node.FindValue("vlan")) {
+ parser.parse_err("VLAN config was disabled", node["vlan"]);
+ }
+ }
+}
+
+/**
+ * sanity checks
+ *
+ * @author imarom (28-Jun-16)
+ */
+void
+ClientCfgDB::verify(const YAMLParserWrapper &parser) const {
+ std::stringstream ss;
+ uint32_t monotonic = 0;
+
+ /* check that no interval overlaps */
+
+ /* all intervals do not overloap iff when sorted each start/end dots are strong monotonic */
+ for (const auto &p : m_groups) {
+ const ClientCfgEntry &group = p.second;
+
+ if ( (monotonic > 0 ) && (group.m_ip_start <= monotonic) ) {
+ ss << "IP '" << ip_to_str(group.m_ip_start) << "' - '" << ip_to_str(group.m_ip_end) << "' overlaps with other groups";
+ parser.parse_err(ss.str());
+ }
+
+ monotonic = group.m_ip_end;
+ }
+}
+
+/**
+ * lookup function
+ * should be fast
+ *
+ */
+ClientCfgEntry *
+ClientCfgDB::lookup(uint32_t ip) {
+
+ /* a cache to avoid constant search (usually its a range of IPs) */
+ if ( (m_cache_group) && (m_cache_group->contains(ip)) ) {
+ return m_cache_group;
+ }
+
+ /* clear the cache pointer */
+ m_cache_group = NULL;
+
+ std::map<uint32_t ,ClientCfgEntry>::iterator it;
+
+ /* upper bound fetchs the first greater element */
+ it = m_groups.upper_bound(ip);
+
+ /* if the first element in the map is bigger - its not in the map */
+ if (it == m_groups.begin()) {
+ return NULL;
+ }
+
+ /* go one back - we know it's not on begin so we have at least one back */
+ it--;
+
+ ClientCfgEntry &group = (*it).second;
+
+ /* because this is a non overlapping intervals
+ if IP exists it must be in this group
+ because if it exists in some other previous group
+ its end_range >= ip so start_range > ip
+ and so start_range is the upper_bound
+ */
+ if (group.contains(ip)) {
+ /* save as cache */
+ m_cache_group = &group;
+ return &group;
+ } else {
+ return NULL;
+ }
+
+}
+
+/**
+ * for convenience - search by IP as string
+ *
+ * @author imarom (28-Jun-16)
+ *
+ * @param ip
+ *
+ * @return ClientCfgEntry*
+ */
+ClientCfgEntry *
+ClientCfgDB::lookup(const std::string &ip) {
+ uint32_t addr = (uint32_t)inet_addr(ip.c_str());
+ addr = PKT_NTOHL(addr);
+
+ return lookup(addr);
+}
+
+
diff --git a/src/trex_client_config.h b/src/trex_client_config.h
new file mode 100644
index 00000000..a5bb83b3
--- /dev/null
+++ b/src/trex_client_config.h
@@ -0,0 +1,265 @@
+/*
+ Itay Marom
+ Cisco Systems, Inc.
+*/
+
+/*
+Copyright (c) 2016 Cisco Systems, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+#ifndef __TREX_CLIENT_CONFIG_H__
+#define __TREX_CLIENT_CONFIG_H__
+
+#include <stdint.h>
+#include <string>
+#include <map>
+
+class YAMLParserWrapper;
+
+
+/**
+ * client configuration per direction
+ *
+ * @author imarom (29-Jun-16)
+ */
+class ClientCfgDir {
+
+private:
+ enum {
+ HAS_SRC_MAC = 0x1,
+ HAS_DST_MAC = 0x2,
+ HAS_VLAN = 0x4,
+ };
+
+ uint8_t m_src_mac[6];
+ uint8_t m_dst_mac[6];
+ uint16_t m_vlan;
+ uint8_t m_bitfield;
+
+
+public:
+ ClientCfgDir() {
+ m_bitfield = 0;
+ }
+
+ bool has_src_mac_addr() const {
+ return (m_bitfield & HAS_SRC_MAC);
+ }
+
+ bool has_dst_mac_addr() const {
+ return (m_bitfield & HAS_DST_MAC);
+ }
+ bool has_vlan() const {
+ return (m_bitfield & HAS_VLAN);
+ }
+
+ void set_src_mac_addr(uint64_t mac_addr) {
+ for (int i = 0; i < 6; i++) {
+ m_src_mac[i] = ( mac_addr >> ((5 - i) * 8) ) & 0xFF;
+ }
+ m_bitfield |= HAS_SRC_MAC;
+ }
+
+ void set_dst_mac_addr(uint64_t mac_addr) {
+ for (int i = 0; i < 6; i++) {
+ m_dst_mac[i] = ( mac_addr >> ((5 - i) * 8) ) & 0xFF;
+ }
+ m_bitfield |= HAS_DST_MAC;
+ }
+
+ void set_vlan(uint16_t vlan_id) {
+ m_vlan = vlan_id;
+ m_bitfield |= HAS_VLAN;
+ }
+
+ /* updates a configuration with a group index member */
+
+ void update(uint32_t index) {
+ if (has_src_mac_addr()) {
+ mac_add(m_src_mac, index);
+ }
+
+ if (has_dst_mac_addr()) {
+ mac_add(m_dst_mac, index);
+ }
+ }
+
+ const uint8_t *get_src_mac_addr() const {
+ assert(has_src_mac_addr());
+ return m_src_mac;
+ }
+
+ const uint8_t *get_dst_mac_addr() const {
+ assert(has_dst_mac_addr());
+ return m_dst_mac;
+ }
+
+ uint16_t get_vlan() const {
+ assert(has_vlan());
+ return m_vlan;
+ }
+
+private:
+ /**
+ * transform MAC address to uint64_t
+ * performs add and return to MAC format
+ *
+ */
+ void mac_add(uint8_t *mac, uint32_t i) {
+ uint64_t tmp = 0;
+
+ for (int i = 0; i < 6; i++) {
+ tmp <<= 8;
+ tmp |= mac[i];
+ }
+
+ tmp += i;
+
+ for (int i = 0; i < 6; i++) {
+ mac[i] = ( tmp >> ((5 - i) * 8) ) & 0xFF;
+ }
+
+ }
+};
+
+/**
+ * single client config
+ *
+ */
+class ClientCfg {
+
+public:
+
+ void update(uint32_t index) {
+ m_initiator.update(index);
+ m_responder.update(index);
+ }
+
+ ClientCfgDir m_initiator;
+ ClientCfgDir m_responder;
+};
+
+/******************************** internal section ********************************/
+
+/**
+ * describes a single client config
+ * entry loaded from the config file
+ *
+ */
+class ClientCfgEntry {
+
+public:
+
+ ClientCfgEntry() {
+ reset();
+ }
+
+
+ void dump() const;
+
+ bool contains(uint32_t ip) const {
+ return ( (ip >= m_ip_start) && (ip <= m_ip_end) );
+ }
+
+ void reset() {
+ m_iterator = 0;
+ }
+
+
+ /**
+ * assings a client config from the group
+ * it will advance MAC addresses andf etc.
+ *
+ * @author imarom (27-Jun-16)
+ *
+ * @param info
+ */
+ void assign(ClientCfg &info) {
+ info = m_cfg;
+ info.update(m_iterator);
+
+ /* advance for the next assign */
+ m_iterator = (m_iterator + 1) % m_count;
+ }
+
+public:
+ uint32_t m_ip_start;
+ uint32_t m_ip_end;
+
+ ClientCfg m_cfg;
+
+ uint32_t m_count;
+
+private:
+ uint32_t m_iterator;
+};
+
+/**
+ * holds all the configured clients groups
+ *
+ */
+class ClientCfgDB {
+public:
+
+ ClientCfgDB() {
+ m_is_empty = true;
+ m_cache_group = NULL;
+ m_under_vlan = false;
+ }
+
+ /**
+ * if no config file was loaded
+ * this should return true
+ *
+ */
+ bool is_empty() {
+ return m_is_empty;
+ }
+
+ /**
+ * loads a YAML file
+ * configuration will be built
+ * according to the YAML config
+ *
+ */
+ void load_yaml_file(const std::string &filename);
+
+ /**
+ * lookup for a specific IP address for
+ * a group that contains this IP
+ *
+ */
+ ClientCfgEntry * lookup(uint32_t ip);
+ ClientCfgEntry * lookup(const std::string &ip);
+
+private:
+ void parse_single_group(YAMLParserWrapper &parser, const YAML::Node &node);
+ void parse_dir(YAMLParserWrapper &parser, const YAML::Node &node, ClientCfgDir &dir);
+
+ /**
+ * verify the YAML file loaded in valid
+ *
+ */
+ void verify(const YAMLParserWrapper &parser) const;
+
+ /* maps the IP start value to client groups */
+ std::map<uint32_t, ClientCfgEntry> m_groups;
+ bool m_under_vlan;
+
+ ClientCfgEntry *m_cache_group;
+ bool m_is_empty;
+};
+
+#endif /* __TREX_CLIENT_CONFIG_H__ */
+
diff --git a/src/tuple_gen.cpp b/src/tuple_gen.cpp
index d221a4d9..6861b73f 100755
--- a/src/tuple_gen.cpp
+++ b/src/tuple_gen.cpp
@@ -25,6 +25,7 @@ limitations under the License.
#include "tuple_gen.h"
#include <string.h>
#include "utl_yaml.h"
+#include "bp_sim.h"
void CServerPool::Create(IP_DIST_t dist_value,
uint32_t min_ip,
@@ -52,97 +53,117 @@ void CServerPool::Create(IP_DIST_t dist_value,
-void CClientPool::Create(IP_DIST_t dist_value,
- uint32_t min_ip,
- uint32_t max_ip,
- double l_flow,
- double t_cps,
- CFlowGenListMac* mac_info,
- bool has_mac_map,
- uint16_t tcp_aging,
- uint16_t udp_aging) {
- assert(max_ip>=min_ip);
+void CClientPool::Create(IP_DIST_t dist_value,
+ uint32_t min_ip,
+ uint32_t max_ip,
+ double l_flow,
+ double t_cps,
+ ClientCfgDB &client_info,
+ uint16_t tcp_aging,
+ uint16_t udp_aging) {
+
+ assert(max_ip >= min_ip);
+
set_dist(dist_value);
- uint32_t total_ip = max_ip - min_ip +1;
- uint32_t avail_ip = total_ip;
- if (has_mac_map && (mac_info!=NULL)) {
- for(int idx=0;idx<total_ip;idx++){
- mac_addr_align_t *mac_adr = NULL;
- mac_adr = mac_info->get_mac_addr_by_ip(min_ip+idx);
- if (mac_adr == NULL) {
- avail_ip--;
- }
- }
- }
- if (avail_ip!=0) {
- m_ip_info.resize(avail_ip);
+
+ uint32_t total_ip = max_ip - min_ip +1;
+ bool is_long_range = total_ip > (l_flow * t_cps / MAX_PORT);
+
+ m_ip_info.resize(total_ip);
+
+ /* if client info is empty - flat allocation o.w use configured clients */
+ if (client_info.is_empty()) {
+ allocate_simple_clients(min_ip, total_ip, is_long_range);
} else {
- printf("\n Error, invalid mac file is configured.\n");
- assert(0);
+ allocate_configured_clients(min_ip, total_ip, is_long_range, client_info);
}
- int skip_cnt=0;
- if (total_ip > ((l_flow*t_cps/MAX_PORT))) {
- if (has_mac_map) {
- skip_cnt=0;
- for(int idx=0;idx<total_ip;idx++){
- mac_addr_align_t *mac_adr = NULL;
- mac_adr = mac_info->get_mac_addr_by_ip( min_ip+idx);
- if (mac_adr != NULL) {
- m_ip_info[idx-skip_cnt] = new CClientInfoL(has_mac_map);
- m_ip_info[idx-skip_cnt]->set_ip(min_ip+idx);
- m_ip_info[idx-skip_cnt]->set_mac(mac_adr);
- } else {
- skip_cnt++;
- }
- }
+ m_tcp_aging = tcp_aging;
+ m_udp_aging = udp_aging;
+
+ CreateBase();
+}
+
+/**
+ * simple allocation of a client - no configuration was provided
+ *
+ * @author imarom (27-Jun-16)
+ *
+ * @param ip
+ * @param index
+ * @param is_long_range
+ */
+void CClientPool::allocate_simple_clients(uint32_t min_ip,
+ uint32_t total_ip,
+ bool is_long_range) {
+
+ /* simple creation of clients - no extended info */
+ for (uint32_t i = 0; i < total_ip; i++) {
+ uint32_t ip = min_ip + i;
+ if (is_long_range) {
+ m_ip_info[i] = new CSimpleClientInfo<CIpInfoL>(ip);
} else {
- for(int idx=0;idx<total_ip;idx++){
- m_ip_info[idx] = new CClientInfoL(has_mac_map);
- m_ip_info[idx]->set_ip(min_ip+idx);
- }
- }
- } else {
- if (has_mac_map) {
- skip_cnt=0;
- for(int idx=0;idx<total_ip;idx++){
- mac_addr_align_t *mac_adr = NULL;
- mac_adr = mac_info->get_mac_addr_by_ip(min_ip+idx);
- if (mac_adr != NULL) {
- m_ip_info[idx-skip_cnt] = new CClientInfo(has_mac_map);
- m_ip_info[idx-skip_cnt]->set_ip(min_ip+idx);
- m_ip_info[idx-skip_cnt]->set_mac(mac_adr);
- } else {
- skip_cnt++;
- }
- }
+ m_ip_info[i] = new CSimpleClientInfo<CIpInfo>(ip);
+ }
+ }
+
+}
+
+/**
+ * simple allocation of a client - no configuration was provided
+ *
+ * @author imarom (27-Jun-16)
+ *
+ * @param ip
+ * @param index
+ * @param is_long_range
+ */
+void CClientPool::allocate_configured_clients(uint32_t min_ip,
+ uint32_t total_ip,
+ bool is_long_range,
+ ClientCfgDB &client_info) {
+
+ for (uint32_t i = 0; i < total_ip; i++) {
+ uint32_t ip = min_ip + i;
+
+ /* lookup for the right group of clients */
+ ClientCfgEntry *group = client_info.lookup(ip);
+ if (!group) {
+ std::stringstream ss;
+ ss << "client configuration error: could not map IP '" << ip_to_str(ip) << "' to a group\n";
+ throw std::runtime_error(ss.str());
+ }
+
+ ClientCfg info;
+ group->assign(info);
+
+ if (is_long_range) {
+ m_ip_info[i] = new CConfiguredClientInfo<CIpInfoL>(ip, info);
} else {
- for(int idx=0;idx<total_ip;idx++){
- m_ip_info[idx] = new CClientInfo(has_mac_map);
- m_ip_info[idx]->set_ip(min_ip+idx);
- }
- }
-
+ m_ip_info[i] = new CConfiguredClientInfo<CIpInfo>(ip, info);
+ }
}
- m_tcp_aging = tcp_aging;
- m_udp_aging = udp_aging;
- CreateBase();
}
-bool CTupleGeneratorSmart::add_client_pool(IP_DIST_t client_dist,
- uint32_t min_client,
- uint32_t max_client,
- double l_flow,
- double t_cps,
- CFlowGenListMac* mac_info,
- uint16_t tcp_aging,
- uint16_t udp_aging){
+bool CTupleGeneratorSmart::add_client_pool(IP_DIST_t client_dist,
+ uint32_t min_client,
+ uint32_t max_client,
+ double l_flow,
+ double t_cps,
+ ClientCfgDB &client_info,
+ uint16_t tcp_aging,
+ uint16_t udp_aging) {
assert(max_client>=min_client);
CClientPool* pool = new CClientPool();
- pool->Create(client_dist, min_client, max_client,
- l_flow, t_cps, mac_info, m_has_mac_mapping,
- tcp_aging, udp_aging);
+ pool->Create(client_dist,
+ min_client,
+ max_client,
+ l_flow,
+ t_cps,
+ client_info,
+ tcp_aging,
+ udp_aging);
m_client_pool.push_back(pool);
return(true);
@@ -170,19 +191,17 @@ bool CTupleGeneratorSmart::add_server_pool(IP_DIST_t server_dist,
bool CTupleGeneratorSmart::Create(uint32_t _id,
- uint32_t thread_id,
- bool has_mac)
+ uint32_t thread_id)
+
{
m_thread_id = thread_id;
m_id = _id;
m_was_init=true;
- m_has_mac_mapping = has_mac;
return(true);
}
void CTupleGeneratorSmart::Delete(){
m_was_init=false;
- m_has_mac_mapping = false;
for (int idx=0;idx<m_client_pool.size();idx++) {
m_client_pool[idx]->Delete();
diff --git a/src/tuple_gen.h b/src/tuple_gen.h
index b2f6e34a..2491f489 100755
--- a/src/tuple_gen.h
+++ b/src/tuple_gen.h
@@ -37,15 +37,17 @@ limitations under the License.
#include "common/c_common.h"
#include <bitset>
#include <yaml-cpp/yaml.h>
-#include <mac_mapping.h>
+#include "trex_client_config.h"
#include <random>
class CTupleBase {
public:
+
CTupleBase() {
- m_client_mac.inused = UNUSED;
+ m_client_cfg = NULL;
}
+
uint32_t getClient() {
return m_client_ip;
}
@@ -83,22 +85,20 @@ public:
void setClientPort(uint16_t port) {
m_client_port = port;
}
- mac_addr_align_t* getClientMac() {
- return &m_client_mac;
+ void setClientCfg(ClientCfg *cfg) {
+ m_client_cfg = cfg;
}
- void setClientMac(mac_addr_align_t* mac_info) {
- if (mac_info != NULL) {
- memcpy(&m_client_mac, mac_info, sizeof(mac_addr_align_t));
- m_client_mac.inused = INUSED;
- } else {
- m_client_mac.inused = UNUSED;
- }
+ ClientCfg *getClientCfg() {
+ return m_client_cfg;
}
- void setClientTuple(uint32_t ip,mac_addr_align_t*mac,uint16_t port) {
+
+
+ void setClientTuple(uint32_t ip, ClientCfg *cfg, uint16_t port) {
setClient(ip);
- setClientMac(mac);
setClientPort(port);
+ setClientCfg(cfg);
}
+
void setClientAll2(uint32_t id, uint32_t ip,uint16_t port) {
setClientId(id);
setClient(ip);
@@ -121,9 +121,12 @@ public:
private:
uint32_t m_client_ip;
uint32_t m_client_idx;
+
uint32_t m_server_ip;
uint32_t m_server_idx;
- mac_addr_align_t m_client_mac;
+
+ ClientCfg *m_client_cfg;
+
uint16_t m_client_port;
uint16_t m_server_port;
};
@@ -171,8 +174,6 @@ typedef enum {
class CIpInfoBase {
public:
- virtual mac_addr_align_t* get_mac() { return NULL;}
- virtual void set_mac(mac_addr_align_t*){;}
virtual uint16_t get_new_free_port() = 0;
virtual void return_port(uint16_t a) = 0;
virtual void generate_tuple(CTupleBase & tuple) = 0;
@@ -303,76 +304,54 @@ class CIpInfo : public CIpInfoBase {
}
};
-class CClientInfo : public CIpInfo {
+
+/**
+ * a flat client info (no configuration)
+ *
+ * using template to avoid duplicating the code for CIpInfo and
+ * CIpInfoL
+ *
+ * @author imarom (27-Jun-16)
+ */
+template <typename T>
+class CSimpleClientInfo : public T {
+
public:
- CClientInfo (bool has_mac) {
- if (has_mac==true) {
- m_mac = new mac_addr_align_t();
- } else {
- m_mac = NULL;
- }
- }
- CClientInfo () {
- m_mac = NULL;
- }
+ CSimpleClientInfo(uint32_t ip) {
+ T::set_ip(ip);
+ }
- mac_addr_align_t* get_mac() {
- return m_mac;
- }
- void set_mac(mac_addr_align_t *mac) {
- memcpy(m_mac, mac, sizeof(mac_addr_align_t));
- }
- ~CClientInfo() {
- if (m_mac!=NULL){
- delete m_mac;
- m_mac=NULL;
- }
- }
-
- void generate_tuple(CTupleBase & tuple) {
- tuple.setClientTuple(m_ip, m_mac,
- get_new_free_port());
+ void generate_tuple(CTupleBase &tuple) {
+ tuple.setClientTuple(T::m_ip,
+ NULL,
+ T::get_new_free_port());
}
-private:
- mac_addr_align_t *m_mac;
};
-class CClientInfoL : public CIpInfoL {
-public:
- CClientInfoL (bool has_mac) {
- if (has_mac==true) {
- m_mac = new mac_addr_align_t();
- } else {
- m_mac = NULL;
- }
- }
- CClientInfoL () {
- m_mac = NULL;
- }
+/**
+ * a configured client object
+ *
+ * @author imarom (26-Jun-16)
+ */
+template <typename T>
+class CConfiguredClientInfo : public T {
- mac_addr_align_t* get_mac() {
- return m_mac;
+public:
+ CConfiguredClientInfo(uint32_t ip, const ClientCfg &cfg) : m_cfg(cfg) {
+ T::set_ip(ip);
}
- void set_mac(mac_addr_align_t *mac) {
- memcpy(m_mac, mac, sizeof(mac_addr_align_t));
+ void generate_tuple(CTupleBase &tuple) {
+ tuple.setClientTuple(T::m_ip,
+ &m_cfg,
+ T::get_new_free_port());
}
- ~CClientInfoL() {
- if (m_mac!=NULL){
- delete m_mac;
- m_mac=NULL;
- }
- }
-
- void generate_tuple(CTupleBase & tuple) {
- tuple.setClientTuple(m_ip, m_mac,
- get_new_free_port());
- }
private:
- mac_addr_align_t *m_mac;
+ ClientCfg m_cfg;
};
+
class CServerInfo : public CIpInfo {
void generate_tuple(CTupleBase & tuple) {
tuple.setServer(m_ip);
@@ -475,12 +454,6 @@ class CIpPool {
client->return_port(port);
}
- mac_addr_align_t * get_curr_mac() {
- return m_ip_info[m_cur_idx]->get_mac();
- }
- mac_addr_align_t *get_mac(uint32_t idx) {
- return m_ip_info[idx]->get_mac();
- }
public:
std::vector<CIpInfoBase*> m_ip_info;
@@ -536,19 +509,30 @@ public:
uint16_t get_udp_aging() {
return m_udp_aging;
}
- void Create(IP_DIST_t dist_value,
- uint32_t min_ip,
- uint32_t max_ip,
- double l_flow,
- double t_cps,
- CFlowGenListMac* mac_info,
- bool has_mac_map,
- uint16_t tcp_aging,
- uint16_t udp_aging);
+
+ void Create(IP_DIST_t dist_value,
+ uint32_t min_ip,
+ uint32_t max_ip,
+ double l_flow,
+ double t_cps,
+ ClientCfgDB &client_info,
+ uint16_t tcp_aging,
+ uint16_t udp_aging);
public:
uint16_t m_tcp_aging;
uint16_t m_udp_aging;
+
+private:
+ void allocate_simple_clients(uint32_t min_ip,
+ uint32_t total_ip,
+ bool is_long_range);
+
+ void allocate_configured_clients(uint32_t min_ip,
+ uint32_t total_ip,
+ bool is_long_range,
+ ClientCfgDB &client_info);
+
};
class CServerPoolBase {
@@ -685,10 +669,9 @@ public:
public:
CTupleGeneratorSmart(){
m_was_init=false;
- m_has_mac_mapping = false;
}
- bool Create(uint32_t _id,
- uint32_t thread_id, bool has_mac=false);
+
+ bool Create(uint32_t _id, uint32_t thread_id);
void Delete();
@@ -704,20 +687,22 @@ public:
return (total_alloc_error);
}
- bool add_client_pool(IP_DIST_t client_dist,
- uint32_t min_client,
- uint32_t max_client,
- double l_flow,
- double t_cps,
- CFlowGenListMac* mac_info,
- uint16_t tcp_aging,
- uint16_t udp_aging);
+ bool add_client_pool(IP_DIST_t client_dist,
+ uint32_t min_client,
+ uint32_t max_client,
+ double l_flow,
+ double t_cps,
+ ClientCfgDB &client_info,
+ uint16_t tcp_aging,
+ uint16_t udp_aging);
+
bool add_server_pool(IP_DIST_t server_dist,
- uint32_t min_server,
- uint32_t max_server,
- double l_flow,
- double t_cps,
- bool is_bundling);
+ uint32_t min_server,
+ uint32_t max_server,
+ double l_flow,
+ double t_cps,
+ bool is_bundling);
+
CClientPool* get_client_pool(uint8_t idx) {
return m_client_pool[idx];
}
@@ -736,7 +721,6 @@ private:
std::vector<CClientPool*> m_client_pool;
std::vector<CServerPoolBase*> m_server_pool;
bool m_was_init;
- bool m_has_mac_mapping;
};
class CTupleTemplateGeneratorSmart {
diff --git a/src/utl_yaml.cpp b/src/utl_yaml.cpp
index 828817e4..8352e887 100755
--- a/src/utl_yaml.cpp
+++ b/src/utl_yaml.cpp
@@ -21,7 +21,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-
+#include <istream>
+#include <fstream>
+#include "common/basic_utils.h"
#define INADDRSZ 4
@@ -65,8 +67,8 @@ static int my_inet_pton4(const char *src, unsigned char *dst)
bool utl_yaml_read_ip_addr(const YAML::Node& node,
- std::string name,
- uint32_t & val){
+ const std::string &name,
+ uint32_t & val){
std::string tmp;
uint32_t ip;
bool res=false;
@@ -83,8 +85,10 @@ bool utl_yaml_read_ip_addr(const YAML::Node& node,
return (res);
}
+
+
bool utl_yaml_read_uint32(const YAML::Node& node,
- std::string name,
+ const std::string &name,
uint32_t & val){
bool res=false;
if ( node.FindValue(name) ) {
@@ -95,8 +99,8 @@ bool utl_yaml_read_uint32(const YAML::Node& node,
}
bool utl_yaml_read_uint16(const YAML::Node& node,
- std::string name,
- uint16_t & val){
+ const std::string &name,
+ uint16_t & val){
uint32_t val_tmp;
bool res=false;
if ( node.FindValue(name) ) {
@@ -108,15 +112,250 @@ bool utl_yaml_read_uint16(const YAML::Node& node,
return (res);
}
-bool utl_yaml_read_bool(const YAML::Node& node,
- std::string name,
- bool & val){
- bool res=false;
- if ( node.FindValue(name) ) {
- node[name] >> val ;
- res=true;
+static void
+split_str_by_delimiter(std::string str, char delim, std::vector<std::string> &tokens) {
+ size_t pos = 0;
+ std::string token;
+
+ while ((pos = str.find(delim)) != std::string::npos) {
+ token = str.substr(0, pos);
+ tokens.push_back(token);
+ str.erase(0, pos + 1);
+ }
+
+ if (str.size() > 0) {
+ tokens.push_back(str);
+ }
+}
+
+static bool mac2uint64(const std::string &mac_str, uint64_t &mac_num) {
+ std::vector<std::string> tokens;
+ uint64_t val;
+
+ split_str_by_delimiter(mac_str, ':', tokens);
+ if (tokens.size() != 6) {
+ return false;
+ }
+
+ val = 0;
+
+ for (int i = 0; i < 6 ; i++) {
+ char *endptr = NULL;
+ unsigned long octet = strtoul(tokens[i].c_str(), &endptr, 16);
+
+ if ( (*endptr != 0) || (octet > 0xff) ) {
+ return false;
+ }
+
+ val = (val << 8) + octet;
}
- return( res);
+
+ mac_num = val;
+
+ return true;
}
+/************************
+ * YAML Parser Wrapper
+ *
+ ***********************/
+void
+YAMLParserWrapper::load(YAML::Node &root) {
+ std::stringstream ss;
+
+ /* first check file exists */
+ if (!utl_is_file_exists(m_filename)){
+ ss << "file '" << m_filename << "' does not exists";
+ throw std::runtime_error(ss.str());
+ }
+
+ std::ifstream fin(m_filename);
+
+ try {
+ YAML::Parser base_parser(fin);
+ base_parser.GetNextDocument(root);
+
+ } catch (const YAML::Exception &e) {
+ parse_err(e.what());
+ }
+}
+
+bool
+YAMLParserWrapper::parse_bool(const YAML::Node &node, const std::string &name, bool def) {
+ if (!node.FindValue(name)) {
+ return def;
+ }
+
+ return parse_bool(node, name);
+}
+
+bool
+YAMLParserWrapper::parse_bool(const YAML::Node &node, const std::string &name) {
+ try {
+ bool val;
+ node[name] >> val;
+ return (val);
+
+ } catch (const YAML::InvalidScalar &ex) {
+ parse_err("expecting true/false for field '" + name + "'", node[name]);
+
+ } catch (const YAML::KeyNotFound &ex) {
+ parse_err("cannot locate mandatory field '" + name + "'", node);
+ }
+
+ assert(0);
+}
+
+const YAML::Node &
+YAMLParserWrapper::parse_list(const YAML::Node &node, const std::string &name) {
+
+ try {
+ const YAML::Node &val = node[name];
+ if (val.Type() != YAML::NodeType::Sequence) {
+ throw YAML::InvalidScalar(node[name].GetMark());
+ }
+ return val;
+
+ } catch (const YAML::InvalidScalar &ex) {
+ parse_err("expecting sequence/list for field '" + name + "'", node[name]);
+
+ } catch (const YAML::KeyNotFound &ex) {
+ parse_err("cannot locate mandatory field '" + name + "'", node);
+ }
+
+ assert(0);
+}
+
+const YAML::Node &
+YAMLParserWrapper::parse_map(const YAML::Node &node, const std::string &name) {
+
+ try {
+ const YAML::Node &val = node[name];
+ if (val.Type() != YAML::NodeType::Map) {
+ throw YAML::InvalidScalar(node[name].GetMark());
+ }
+ return val;
+
+ } catch (const YAML::InvalidScalar &ex) {
+ parse_err("expecting map for field '" + name + "'", node[name]);
+ } catch (const YAML::KeyNotFound &ex) {
+ parse_err("cannot locate mandatory field '" + name + "'", node);
+ }
+
+ assert(0);
+}
+
+uint32_t
+YAMLParserWrapper::parse_ip(const YAML::Node &node, const std::string &name) {
+
+
+ try {
+ std::string ip_str;
+ uint32_t ip_num;
+
+ node[name] >> ip_str;
+ int rc = my_inet_pton4((char *)ip_str.c_str(), (unsigned char *)&ip_num);
+ if (!rc) {
+ parse_err("invalid IP address: " + ip_str, node[name]);
+ }
+
+ return PKT_NTOHL(ip_num);
+
+ } catch (const YAML::InvalidScalar &ex) {
+ parse_err("expecting valid IP address for field '" + name + "'", node[name]);
+
+ } catch (const YAML::KeyNotFound &ex) {
+ parse_err("cannot locate mandatory field '" + name + "'", node);
+ }
+
+ assert(0);
+}
+
+uint64_t
+YAMLParserWrapper::parse_mac_addr(const YAML::Node &node, const std::string &name, uint64_t def) {
+ if (!node.FindValue(name)) {
+ return def;
+ }
+
+ return parse_mac_addr(node, name);
+}
+
+uint64_t
+YAMLParserWrapper::parse_mac_addr(const YAML::Node &node, const std::string &name) {
+
+ std::string mac_str;
+ uint64_t mac_num;
+
+ try {
+
+ node[name] >> mac_str;
+ bool rc = mac2uint64(mac_str, mac_num);
+ if (!rc) {
+ parse_err("invalid MAC address: " + mac_str, node[name]);
+ }
+ return mac_num;
+
+ } catch (const YAML::InvalidScalar &ex) {
+ parse_err("expecting true/false for field '" + name + "'", node[name]);
+
+ } catch (const YAML::KeyNotFound &ex) {
+ parse_err("cannot locate mandatory field '" + name + "'", node);
+ }
+
+ assert(0);
+}
+
+uint64_t
+YAMLParserWrapper::parse_uint(const YAML::Node &node, const std::string &name, uint64_t low, uint64_t high, uint64_t def) {
+ if (!node.FindValue(name)) {
+ return def;
+ }
+
+ return parse_uint(node, name, low, high);
+}
+
+uint64_t
+YAMLParserWrapper::parse_uint(const YAML::Node &node, const std::string &name, uint64_t low, uint64_t high) {
+
+ try {
+
+ uint64_t val;
+ node[name] >> val;
+
+ if ( (val < low) || (val > high) ) {
+ std::stringstream ss;
+ ss << "valid range for field '" << name << "' is: [" << low << " - " << high << "]";
+ parse_err(ss.str(), node[name]);
+ }
+
+ return (val);
+
+ } catch (const YAML::InvalidScalar &ex) {
+ parse_err("expecting true/false for field '" + name + "'", node[name]);
+
+ } catch (const YAML::KeyNotFound &ex) {
+ parse_err("cannot locate mandatory field '" + name + "'", node);
+ }
+
+ assert(0);
+}
+
+void
+YAMLParserWrapper::parse_err(const std::string &err, const YAML::Node &node) const {
+ std::stringstream ss;
+
+ ss << "'" << m_filename << "' - YAML parsing error at line " << node.GetMark().line << ": ";
+ ss << err;
+
+ throw std::runtime_error(ss.str());
+}
+
+void
+YAMLParserWrapper::parse_err(const std::string &err) const {
+ std::stringstream ss;
+
+ ss << "'" << m_filename << "' - YAML parsing error: " << err;
+
+ throw std::runtime_error(ss.str());
+}
diff --git a/src/utl_yaml.h b/src/utl_yaml.h
index 71655488..59104b21 100755
--- a/src/utl_yaml.h
+++ b/src/utl_yaml.h
@@ -24,23 +24,56 @@ limitations under the License.
#include <stdint.h>
#include <yaml-cpp/yaml.h>
-
+
+/* static methods - please prefer the wrapper over those */
bool utl_yaml_read_ip_addr(const YAML::Node& node,
- std::string name,
- uint32_t & val
- );
+ const std::string &name,
+ uint32_t & val);
bool utl_yaml_read_uint32(const YAML::Node& node,
- std::string name,
- uint32_t & val);
+ const std::string &name,
+ uint32_t & val);
+
bool utl_yaml_read_uint16(const YAML::Node& node,
- std::string name,
- uint16_t & val);
+ const std::string &name,
+ uint16_t & val);
+
+/* a thin wrapper to customize errors */
+class YAMLParserWrapper {
+public:
+ /* a header that will start every error message */
+ YAMLParserWrapper(const std::string &filename) : m_filename(filename) {
+ }
+
+ /**
+ * loads the file (while parsing it)
+ *
+ */
+ void load(YAML::Node &root);
+
+ /* bool */
+ bool parse_bool(const YAML::Node &node, const std::string &name);
+ bool parse_bool(const YAML::Node &node, const std::string &name, bool def);
-bool utl_yaml_read_bool(const YAML::Node& node,
- std::string name,
- bool & val);
+ const YAML::Node & parse_list(const YAML::Node &node, const std::string &name);
+ const YAML::Node & parse_map(const YAML::Node &node, const std::string &name);
+ uint32_t parse_ip(const YAML::Node &node, const std::string &name);
+ uint64_t parse_mac_addr(const YAML::Node &node, const std::string &name);
+ uint64_t parse_mac_addr(const YAML::Node &node, const std::string &name, uint64_t def);
+
+ uint64_t parse_uint(const YAML::Node &node, const std::string &name, uint64_t low = 0, uint64_t high = UINT64_MAX);
+ uint64_t parse_uint(const YAML::Node &node, const std::string &name, uint64_t low, uint64_t high, uint64_t def);
+
+
+public:
+ void parse_err(const std::string &err, const YAML::Node &node) const;
+ void parse_err(const std::string &err) const;
+
+
+private:
+ std::string m_filename;
+};
#endif