summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xlinux/ws_main.py2
-rwxr-xr-xlinux_dpdk/ws_main.py2
-rwxr-xr-xscripts/automation/regression/CPlatform.py7
-rw-r--r--scripts/automation/regression/functional_tests/stl_basic_tests.py6
-rw-r--r--scripts/automation/regression/setups/kiwi02/benchmark.yaml2
-rw-r--r--scripts/automation/regression/setups/trex-dan/benchmark.yaml2
-rw-r--r--scripts/automation/regression/setups/trex07/benchmark.yaml374
-rw-r--r--scripts/automation/regression/setups/trex14/benchmark.yaml12
-rw-r--r--scripts/automation/regression/setups/trex25/benchmark.yaml2
-rwxr-xr-xscripts/automation/regression/stateful_tests/trex_general_test.py71
-rwxr-xr-xscripts/automation/regression/stateful_tests/trex_nat_test.py3
-rwxr-xr-xscripts/automation/regression/stateful_tests/trex_rx_test.py2
-rwxr-xr-xscripts/automation/regression/stateless_tests/stl_examples_test.py2
-rw-r--r--scripts/automation/regression/stateless_tests/stl_general_test.py47
-rw-r--r--scripts/automation/regression/stateless_tests/stl_rx_test.py10
-rwxr-xr-xscripts/automation/regression/stateless_tests/trex_client_pkg_test.py6
-rw-r--r--scripts/automation/regression/trex.py2
-rwxr-xr-xscripts/automation/regression/trex_unit_test.py37
-rwxr-xr-xscripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py126
-rwxr-xr-xscripts/automation/trex_control_plane/stl/console/trex_console.py9
-rw-r--r--scripts/automation/trex_control_plane/stl/console/trex_tui.py505
-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
-rw-r--r--scripts/automation/trex_control_plane/stl/examples/stl_imix.py2
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py154
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py129
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py20
-rw-r--r--scripts/cfg/client_cfg_template.yaml56
-rw-r--r--scripts/exp/no_tcp_syn_ack.pcapbin0 -> 264 bytes
-rw-r--r--scripts/exp/tcp_2_pkts.pcapbin0 -> 184 bytes
-rw-r--r--scripts/exp/tcp_low_ipg.pcapbin0 -> 264 bytes
-rwxr-xr-xscripts/ko/src/readme.txt2
-rwxr-xr-xscripts/master_daemon.py12
-rw-r--r--scripts/stl/udp_1pkt_src_ip_split.py14
-rwxr-xr-xscripts/trex-cfg22
-rwxr-xr-xsrc/bp_gtest.cpp86
-rwxr-xr-xsrc/bp_sim.cpp549
-rwxr-xr-xsrc/bp_sim.h198
-rwxr-xr-xsrc/common/Network/Packet/TcpHeader.h5
-rw-r--r--src/flow_stat.cpp26
-rwxr-xr-xsrc/global_io_mode.cpp31
-rwxr-xr-xsrc/global_io_mode.h16
-rwxr-xr-xsrc/gtest/tuple_gen_test.cpp75
-rw-r--r--src/internal_api/trex_platform_api.h10
-rw-r--r--src/latency.cpp15
-rw-r--r--src/latency.h35
-rw-r--r--src/mac_mapping.h60
-rwxr-xr-xsrc/main.cpp39
-rw-r--r--src/main_dpdk.cpp329
-rwxr-xr-xsrc/nat_check.cpp59
-rwxr-xr-xsrc/nat_check.h8
-rw-r--r--src/nat_check_flow_table.cpp314
-rw-r--r--src/nat_check_flow_table.h111
-rw-r--r--src/rpc-server/commands/trex_rpc_cmd_general.cpp23
-rw-r--r--src/rpc-server/commands/trex_rpc_cmds.h1
-rw-r--r--src/rpc-server/trex_rpc_cmds_table.cpp1
-rw-r--r--src/sim/trex_sim_stateful.cpp25
-rw-r--r--src/stateless/cp/trex_stateless.cpp6
-rw-r--r--src/stateless/rx/trex_stateless_rx_core.cpp12
-rw-r--r--src/stateless/rx/trex_stateless_rx_core.h26
-rw-r--r--src/trex_client_config.cpp235
-rw-r--r--src/trex_client_config.h265
-rw-r--r--src/trex_defs.h7
-rw-r--r--src/trex_watchdog.cpp2
-rw-r--r--src/trex_watchdog.h6
-rwxr-xr-xsrc/tuple_gen.cpp185
-rwxr-xr-xsrc/tuple_gen.h194
-rwxr-xr-xsrc/utl_cpuu.cpp31
-rwxr-xr-xsrc/utl_cpuu.h17
-rwxr-xr-xsrc/utl_yaml.cpp267
-rwxr-xr-xsrc/utl_yaml.h55
71 files changed, 3713 insertions, 1321 deletions
diff --git a/linux/ws_main.py b/linux/ws_main.py
index 6dccf597..66d79d13 100755
--- a/linux/ws_main.py
+++ b/linux/ws_main.py
@@ -112,6 +112,7 @@ main_src = SrcGroup(dir='src',
'utl_yaml.cpp',
'rx_check_header.cpp',
'nat_check.cpp',
+ 'nat_check_flow_table.cpp',
'timer_wheel_pq.cpp',
'time_histogram.cpp',
'utl_json.cpp',
@@ -122,6 +123,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 72ea5e3d..00e72edf 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',
@@ -116,6 +117,7 @@ main_src = SrcGroup(dir='src',
'utl_json.cpp',
'utl_yaml.cpp',
'nat_check.cpp',
+ 'nat_check_flow_table.cpp',
'msg_manager.cpp',
'publisher/trex_publisher.cpp',
'pal/linux_dpdk/pal_utl.cpp',
diff --git a/scripts/automation/regression/CPlatform.py b/scripts/automation/regression/CPlatform.py
index de1c22ce..dc5418cb 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:
@@ -70,11 +70,10 @@ class CPlatform(object):
res = self.cmd_link.run_single_command(cache)
if 'Rollback Done' not in res:
print('Failed to load clean config, trying again')
+ time.sleep(2)
if i < 4:
continue
raise Exception('Could not load clean config, response: %s' % res)
- if i > 0: # were errors, better to wait
- time.sleep(2)
def config_pbr (self, mode = 'config'):
idx = 1
diff --git a/scripts/automation/regression/functional_tests/stl_basic_tests.py b/scripts/automation/regression/functional_tests/stl_basic_tests.py
index a4e28ca9..e03c0742 100644
--- a/scripts/automation/regression/functional_tests/stl_basic_tests.py
+++ b/scripts/automation/regression/functional_tests/stl_basic_tests.py
@@ -85,7 +85,7 @@ class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test):
pkts1 = list(RawPcapReader(output))
pkts2 = list(RawPcapReader(golden))
- assert_equal(len(pkts1), len(pkts2))
+ assert_equal(len(pkts1), len(pkts2), 'Lengths of generated pcap (%s) and golden (%s) are different' % (output, golden))
for pkt1, pkt2, i in zip(pkts1, pkts2, range(1, len(pkts1))):
ts1 = float(pkt1[1][0]) + (float(pkt1[1][1]) / 1e6)
@@ -143,7 +143,7 @@ class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test):
os.unlink(output_cap)
try:
rc = self.run_sim(input_file, output_cap, options, silent)
- assert_equal(rc, True)
+ assert_equal(rc, True, 'Simulation on profile %s failed.' % profile)
#s='cp '+output_cap+' '+golden_file;
#print s
#os.system(s)
@@ -165,7 +165,7 @@ class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test):
profile.dump_to_code(generated_filename)
rc = self.run_sim(generated_filename, output_cap, options, silent)
- assert_equal(rc, True)
+ assert_equal(rc, True, 'Simulation on profile %s (generated) failed.' % profile)
if compare:
self.compare_caps(output_cap, golden_file)
diff --git a/scripts/automation/regression/setups/kiwi02/benchmark.yaml b/scripts/automation/regression/setups/kiwi02/benchmark.yaml
index 3332aa5e..e6621085 100644
--- a/scripts/automation/regression/setups/kiwi02/benchmark.yaml
+++ b/scripts/automation/regression/setups/kiwi02/benchmark.yaml
@@ -69,6 +69,8 @@ test_nat_simple_mode1: &test_nat_simple
test_nat_simple_mode2: *test_nat_simple
+test_nat_simple_mode3: *test_nat_simple
+
test_nat_learning:
<< : *test_nat_simple
nat_opened : 100000
diff --git a/scripts/automation/regression/setups/trex-dan/benchmark.yaml b/scripts/automation/regression/setups/trex-dan/benchmark.yaml
index e9f12c45..de56089b 100644
--- a/scripts/automation/regression/setups/trex-dan/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex-dan/benchmark.yaml
@@ -69,6 +69,8 @@ test_nat_simple_mode1: &test_nat_simple
test_nat_simple_mode2: *test_nat_simple
+test_nat_simple_mode3: *test_nat_simple
+
test_nat_learning:
<< : *test_nat_simple
bw_per_core : 7.377
diff --git a/scripts/automation/regression/setups/trex07/benchmark.yaml b/scripts/automation/regression/setups/trex07/benchmark.yaml
index 4778de91..0dc340b0 100644
--- a/scripts/automation/regression/setups/trex07/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex07/benchmark.yaml
@@ -2,169 +2,243 @@
#### TRex benchmark configuration file ####
###############################################################
-test_nbar_simple :
- multiplier : 7.5
- cores : 2
- exp_gbps : 3.5
- cpu_to_core_ratio : 20800000
- cpu2core_custom_dev: YES
- cpu2core_dev : 0.07
- exp_max_latency : 1000
-
- nbar_classification:
- rtp : 32.57
- http : 30.25
- oracle-sqlnet : 11.23
- exchange : 10.80
- citrix : 5.62
- rtsp : 2.84
- dns : 1.95
- smtp : 0.57
- pop3 : 0.36
- ssl : 0.17
- sctp : 0.13
- sip : 0.09
- unknown : 3.41
-
-test_rx_check :
- multiplier : 13
- cores : 3
- rx_sample_rate : 128
- exp_gbps : 6
- cpu_to_core_ratio : 37270000
- exp_bw : 13
- exp_latency : 1
-
-test_nat_simple : &test_nat_simple
- stat_route_dict :
+#### common templates ###
+
+stat_route_dict: &stat_route_dict
clients_start : 16.0.0.1
servers_start : 48.0.0.1
dual_port_mask : 1.0.0.0
client_destination_mask : 255.0.0.0
server_destination_mask : 255.0.0.0
- nat_dict :
+
+nat_dict: &nat_dict
clients_net_start : 16.0.0.0
client_acl_wildcard_mask : 0.0.0.255
dual_port_mask : 1.0.0.0
pool_start : 200.0.0.0
pool_netmask : 255.255.255.0
- multiplier : 12000
- cores : 1
- cpu_to_core_ratio : 37270000
- exp_bw : 1
- exp_latency : 1
- allow_timeout_dev : YES
-
-test_nat_simple_mode1 : *test_nat_simple
-test_nat_simple_mode2 : *test_nat_simple
-
-test_nat_learning :
- stat_route_dict :
- clients_start : 16.0.0.1
- servers_start : 48.0.0.1
- dual_port_mask : 1.0.0.0
- client_destination_mask : 255.0.0.0
- server_destination_mask : 255.0.0.0
- multiplier : 12000
- cores : 1
- nat_opened : 40000
- cpu_to_core_ratio : 270
- exp_bw : 8
- exp_latency : 1
- allow_timeout_dev : YES
-
-test_routing_imix_64 :
- multiplier : 430
- cores : 1
- cpu_to_core_ratio : 280
- exp_latency : 1
-
-test_routing_imix :
- multiplier : 10
- cores : 1
- cpu_to_core_ratio : 1800
- exp_latency : 1
-
-test_static_routing_imix :
- stat_route_dict :
- clients_start : 16.0.0.1
- servers_start : 48.0.0.1
- dual_port_mask : 1.0.0.0
- client_destination_mask : 255.0.0.0
- server_destination_mask : 255.0.0.0
- multiplier : 8
- cores : 1
- cpu_to_core_ratio : 1800
- exp_latency : 1
-test_static_routing_imix_asymmetric:
- stat_route_dict :
- clients_start : 16.0.0.1
- servers_start : 48.0.0.1
- dual_port_mask : 1.0.0.0
- client_destination_mask : 255.0.0.0
- server_destination_mask : 255.0.0.0
- multiplier : 8
- cores : 1
- cpu_to_core_ratio : 1800
- exp_latency : 1
-
-test_ipv6_simple :
- multiplier : 9
- cores : 2
- cpu_to_core_ratio : 30070000
- cpu2core_custom_dev: YES
- cpu2core_dev : 0.07
-
-
-test_rx_check_sfr:
- multiplier : 10
- cores : 2
- rx_sample_rate : 16
- # allow 0.03% errors, bad router
- error_tolerance : 0.03
-
-test_rx_check_http:
- multiplier : 15000
- cores : 1
- rx_sample_rate : 16
- # allow 0.03% errors, bad routerifconfig
- error_tolerance : 0.03
-test_rx_check_sfr_ipv6:
- multiplier : 10
- cores : 2
- rx_sample_rate : 16
- # allow 0.03% errors, bad router
- error_tolerance : 0.03
+### stateful ###
+
+test_jumbo:
+ multiplier : 17
+ cores : 1
+ bw_per_core : 543.232
+
+
+test_routing_imix:
+ multiplier : 10
+ cores : 1
+ bw_per_core : 34.128
+
+
+test_routing_imix_64:
+ multiplier : 430
+ cores : 1
+ bw_per_core : 5.893
+
+
+test_static_routing_imix: &test_static_routing_imix
+ stat_route_dict : *stat_route_dict
+ multiplier : 8
+ cores : 1
+ bw_per_core : 34.339
+
+test_static_routing_imix_asymmetric: *test_static_routing_imix
+
+
+test_ipv6_simple:
+ multiplier : 9
+ cores : 2
+ bw_per_core : 19.064
+
+
+test_nat_simple_mode1: &test_nat_simple
+ stat_route_dict : *stat_route_dict
+ nat_dict : *nat_dict
+ multiplier : 6000
+ cores : 1
+ nat_opened : 500000
+ allow_timeout_dev : True
+ bw_per_core : 44.445
+
+test_nat_simple_mode2: *test_nat_simple
+
+test_nat_simple_mode3: *test_nat_simple
+
+test_nat_learning: *test_nat_simple
+
+
+test_nbar_simple:
+ multiplier : 7.5
+ cores : 2
+ bw_per_core : 17.174
+ nbar_classification:
+ rtp : 32.57
+ http : 30.25
+ oracle_sqlnet : 11.23
+ exchange : 10.80
+ citrix : 5.62
+ rtsp : 2.84
+ dns : 1.95
+ smtp : 0.57
+ pop3 : 0.36
+ ssl : 0.17
+ sctp : 0.13
+ sip : 0.09
+ unknown : 3.41
+
+
+test_rx_check_http: &rx_http
+ multiplier : 15000
+ cores : 1
+ rx_sample_rate : 16
+ bw_per_core : 39.560
test_rx_check_http_ipv6:
- multiplier : 15000
- cores : 1
- rx_sample_rate : 16
- # allow 0.03% errors, bad router
- error_tolerance : 0.03
-
-test_rx_check_http_negative:
- multiplier : 13000
- cores : 1
- rx_sample_rate : 16
- # allow 0.03% errors, bad router
- error_tolerance : 0.03
- stat_route_dict :
- clients_start : 16.0.0.1
- servers_start : 48.0.0.1
- dual_port_mask : 1.0.0.0
- client_destination_mask : 255.0.0.0
- server_destination_mask : 255.0.0.0
- nat_dict :
- clients_net_start : 16.0.0.0
- client_acl_wildcard_mask : 0.0.0.255
- dual_port_mask : 1.0.0.0
- pool_start : 200.0.0.0
- pool_netmask : 255.255.255.0
+ << : *rx_http
+ bw_per_core : 49.237
+
+test_rx_check_http_negative_disabled:
+ << : *rx_http
+ stat_route_dict : *stat_route_dict
+ nat_dict : *nat_dict
+
+
+test_rx_check_sfr: &rx_sfr
+ multiplier : 10
+ cores : 3
+ rx_sample_rate : 16
+ bw_per_core : 16.082
+
+test_rx_check_sfr_ipv6:
+ << : *rx_sfr
+ bw_per_core : 19.198
+
+
+
+### stateless ###
+
+test_CPU_benchmark:
+ profiles:
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# causes queue full
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 64, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# not enough memory + queue full if memory increase
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 9000, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/imix.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/pcap.py
+ kwargs : {ipg_usec: 2, loop_count: 0}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/hlt/hlt_udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
-test_jumbo:
- multiplier : 17
- cores : 1
diff --git a/scripts/automation/regression/setups/trex14/benchmark.yaml b/scripts/automation/regression/setups/trex14/benchmark.yaml
index aa4ac2d4..04f13e79 100644
--- a/scripts/automation/regression/setups/trex14/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex14/benchmark.yaml
@@ -57,17 +57,17 @@ test_ipv6_simple:
test_nat_simple_mode1: &test_nat_simple
stat_route_dict : *stat_route_dict
nat_dict : *nat_dict
- multiplier : 12000
+ multiplier : 6000
cores : 1
- nat_opened : 40000
+ nat_opened : 500000
allow_timeout_dev : True
bw_per_core : 44.445
test_nat_simple_mode2: *test_nat_simple
-test_nat_learning:
- << : *test_nat_simple
- nat_opened : 40000
+test_nat_simple_mode3: *test_nat_simple
+
+test_nat_learning: *test_nat_simple
test_nbar_simple:
@@ -101,7 +101,7 @@ test_rx_check_http_ipv6:
<< : *rx_http
bw_per_core : 49.237
-test_rx_check_http_negative:
+test_rx_check_http_negative_disabled:
<< : *rx_http
stat_route_dict : *stat_route_dict
nat_dict : *nat_dict
diff --git a/scripts/automation/regression/setups/trex25/benchmark.yaml b/scripts/automation/regression/setups/trex25/benchmark.yaml
index 19fab1fe..ccbdf6f5 100644
--- a/scripts/automation/regression/setups/trex25/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex25/benchmark.yaml
@@ -70,6 +70,8 @@ test_nat_simple_mode1: &test_nat_simple
test_nat_simple_mode2: *test_nat_simple
+test_nat_simple_mode3: *test_nat_simple
+
test_nat_learning:
<< : *test_nat_simple
nat_opened : 40000
diff --git a/scripts/automation/regression/stateful_tests/trex_general_test.py b/scripts/automation/regression/stateful_tests/trex_general_test.py
index 82b1d9d1..8ff4fdaf 100755
--- a/scripts/automation/regression/stateful_tests/trex_general_test.py
+++ b/scripts/automation/regression/stateful_tests/trex_general_test.py
@@ -139,41 +139,42 @@ class CTRexGeneral_Test(unittest.TestCase):
if res[name] != float(val):
self.fail('TRex results[%s]==%f and not as expected %f ' % (name, res[name], val))
- def check_CPU_benchmark (self, trex_res, err = 25, minimal_cpu = 30, maximal_cpu = 85):
- #cpu_util = float(trex_res.get_last_value("trex-global.data.m_cpu_util"))
- cpu_util = sum(trex_res.get_value_list("trex-global.data.m_cpu_util")[-4:-1]) / 3.0 # mean of 3 values before last
-
- if '1G' in self.modes:
- minimal_cpu /= 10.0
-
- if not self.is_virt_nics:
- if cpu_util > maximal_cpu:
- self.fail("CPU is too high (%s%%), probably queue full." % cpu_util )
- #if cpu_util < minimal_cpu:
- # self.fail("CPU is too low (%s%%), can't verify performance in such low CPU%%." % cpu_util )
-
- test_norm_cpu = sum(trex_res.get_value_list("trex-global.data.m_bw_per_core")[-4:-1]) / 3.0
-
- print("TRex CPU utilization: %g%%, norm_cpu is : %g Gb/core" % (round(cpu_util, 2), round(test_norm_cpu)))
-
- expected_norm_cpu = self.get_benchmark_param('bw_per_core')
- if not expected_norm_cpu:
- expected_norm_cpu = 1
-
- calc_error_precent = abs(100.0 * test_norm_cpu / expected_norm_cpu - 100)
- print('Err percent: %s' % calc_error_precent)
- #if calc_error_precent > err and cpu_util > 10:
- # self.fail('Excepted bw_per_core ratio: %s, got: %g' % (expected_norm_cpu, round(test_norm_cpu)))
-
- # report benchmarks
- if self.GAManager:
- try:
- setup_test = '%s.%s' % (CTRexScenario.setup_name, self.get_name())
- self.GAManager.gaAddAction(Event = 'stateful_test', action = setup_test, label = 'bw_per_core', value = int(test_norm_cpu))
- self.GAManager.gaAddAction(Event = 'stateful_test', action = setup_test, label = 'bw_per_core_exp', value = int(expected_norm_cpu))
- self.GAManager.emptyAndReportQ()
- except Exception as e:
- print('Sending GA failed: %s' % e)
+ def check_CPU_benchmark (self, trex_res, err = 25, minimal_cpu = 10, maximal_cpu = 85):
+ cpu_util = trex_res.get_avg_steady_state_value('trex-global.data.m_cpu_util_raw')
+ trex_tx_bps = trex_res.get_avg_steady_state_value('trex-global.data.m_tx_bps')
+ expected_norm_cpu = self.get_benchmark_param('bw_per_core')
+ cores = self.get_benchmark_param('cores')
+ ports_count = trex_res.get_ports_count()
+ test_norm_cpu = trex_tx_bps / (cpu_util * ports_count * cores * 2.5e6)
+
+ if '1G' in self.modes:
+ minimal_cpu /= 10.0
+
+ if not self.is_virt_nics:
+ if cpu_util > maximal_cpu:
+ self.fail("CPU is too high (%s%%), probably queue full." % cpu_util )
+ #if cpu_util < minimal_cpu:
+ # self.fail("CPU is too low (%s%%), can't verify performance in such low CPU%%." % cpu_util )
+
+ print("TRex CPU utilization: %g%%, norm_cpu is : %g Gb/core" % (round(cpu_util, 2), round(test_norm_cpu, 2)))
+
+ if not expected_norm_cpu:
+ expected_norm_cpu = 1
+
+ calc_error_precent = abs(100.0 * test_norm_cpu / expected_norm_cpu - 100)
+ print('Err percent: %s' % calc_error_precent)
+ #if calc_error_precent > err and cpu_util > 10:
+ # self.fail('Excepted bw_per_core ratio: %s, got: %g' % (expected_norm_cpu, round(test_norm_cpu)))
+
+ # report benchmarks
+ if self.GAManager:
+ try:
+ setup_test = '%s.%s' % (CTRexScenario.setup_name, self.get_name())
+ self.GAManager.gaAddAction(Event = 'stateful_test', action = setup_test, label = 'bw_per_core', value = int(test_norm_cpu))
+ self.GAManager.gaAddAction(Event = 'stateful_test', action = setup_test, label = 'bw_per_core_exp', value = int(expected_norm_cpu))
+ self.GAManager.emptyAndReportQ()
+ except Exception as e:
+ print('Sending GA failed: %s' % e)
def check_results_gt (self, res, name, val):
if res is None:
diff --git a/scripts/automation/regression/stateful_tests/trex_nat_test.py b/scripts/automation/regression/stateful_tests/trex_nat_test.py
index 6e030ffe..c23f67c4 100755
--- a/scripts/automation/regression/stateful_tests/trex_nat_test.py
+++ b/scripts/automation/regression/stateful_tests/trex_nat_test.py
@@ -93,6 +93,9 @@ class CTRexNat_Test(CTRexGeneral_Test):#(unittest.TestCase):
def test_nat_simple_mode2(self):
self.nat_simple_helper(learn_mode=2)
+ def test_nat_simple_mode3(self):
+ self.nat_simple_helper(learn_mode=3)
+
def nat_simple_helper(self, learn_mode=1):
# test initializtion
self.router.configure_basic_interfaces()
diff --git a/scripts/automation/regression/stateful_tests/trex_rx_test.py b/scripts/automation/regression/stateful_tests/trex_rx_test.py
index 40528d16..c08ad1ea 100755
--- a/scripts/automation/regression/stateful_tests/trex_rx_test.py
+++ b/scripts/automation/regression/stateful_tests/trex_rx_test.py
@@ -250,7 +250,7 @@ class CTRexRx_Test(CTRexGeneral_Test):
print('Run until finish, expect errors')
old_errors = copy.deepcopy(self.fail_reasons)
- nat_dict = self.get_benchmark_param('nat_dict', test_name = 'test_nat_simple')
+ nat_dict = self.get_benchmark_param('nat_dict', test_name = 'test_nat_simple_mode1')
nat_obj = CNatConfig(nat_dict)
self.router.config_nat(nat_obj)
self.router.config_zbf()
diff --git a/scripts/automation/regression/stateless_tests/stl_examples_test.py b/scripts/automation/regression/stateless_tests/stl_examples_test.py
index d8b85dfc..71fc3287 100755
--- a/scripts/automation/regression/stateless_tests/stl_examples_test.py
+++ b/scripts/automation/regression/stateless_tests/stl_examples_test.py
@@ -10,14 +10,12 @@ class STLExamples_Test(CStlGeneral_Test):
def explicitSetUp(self):
# examples connect by their own
if self.is_connected():
- self.recover_after_trex_210_issue()
CTRexScenario.stl_trex.disconnect()
def explicitTearDown(self):
# connect back at end of tests
if not self.is_connected():
self.stl_trex.connect()
- self.recover_after_trex_210_issue()
def test_stl_examples(self):
examples_dir = '../trex_control_plane/stl/examples'
diff --git a/scripts/automation/regression/stateless_tests/stl_general_test.py b/scripts/automation/regression/stateless_tests/stl_general_test.py
index 82738f96..5ae2b326 100644
--- a/scripts/automation/regression/stateless_tests/stl_general_test.py
+++ b/scripts/automation/regression/stateless_tests/stl_general_test.py
@@ -6,10 +6,6 @@ from trex_stl_lib.api import *
import time
from nose.tools import nottest
-def setUpModule():
- if CTRexScenario.stl_trex.is_connected():
- CStlGeneral_Test.recover_after_trex_210_issue()
-
class CStlGeneral_Test(CTRexGeneral_Test):
"""This class defines the general stateless testcase of the TRex traffic generator"""
@@ -20,21 +16,6 @@ class CStlGeneral_Test(CTRexGeneral_Test):
if CTRexScenario.stl_init_error:
self.skip(CTRexScenario.stl_init_error)
- # workaround of http://trex-tgn.cisco.com/youtrack/issue/trex-210
- @staticmethod
- def recover_after_trex_210_issue():
- return
- for i in range(20):
- try:
- stl_map_ports(CTRexScenario.stl_trex)
- break
- except:
- CTRexScenario.stl_trex.disconnect()
- time.sleep(0.5)
- CTRexScenario.stl_trex.connect()
- # verify problem is solved
- stl_map_ports(CTRexScenario.stl_trex)
-
def connect(self, timeout = 100):
# need delay and check only because TRex process might be still starting
sys.stdout.write('Connecting')
@@ -85,18 +66,22 @@ class STLBasic_Test(CStlGeneral_Test):
@nottest
def test_connectivity(self):
if not self.is_loopback:
- if CTRexScenario.router_cfg['forceImageReload']:
- CTRexScenario.router.load_clean_config()
- CTRexScenario.router.configure_basic_interfaces()
- CTRexScenario.router.config_pbr(mode = "config")
-
- err = 'Client could not connect'
- CTRexScenario.stl_init_error = err
+ try:
+ if CTRexScenario.router_cfg['forceImageReload']:
+ CTRexScenario.router.load_clean_config()
+ CTRexScenario.router.configure_basic_interfaces()
+ CTRexScenario.router.config_pbr(mode = "config")
+ except Exception as e:
+ CTRexScenario.stl_init_error = 'Could not configure device, err: %s' % e
+ self.fail(CTRexScenario.stl_init_error)
+ return
if not self.connect():
- self.fail(err)
- err = 'Client could not map ports'
- CTRexScenario.stl_init_error = err
+ CTRexScenario.stl_init_error = 'Client could not connect'
+ self.fail(CTRexScenario.stl_init_error)
+ return
+ print('Connected')
if not self.map_ports():
- self.fail(err)
+ CTRexScenario.stl_init_error = 'Client could not map ports'
+ self.fail(CTRexScenario.stl_init_error)
+ return
print('Got ports mapping: %s' % CTRexScenario.stl_ports_map)
- CTRexScenario.stl_init_error = None
diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py
index 238ff53d..090261ff 100644
--- a/scripts/automation/regression/stateless_tests/stl_rx_test.py
+++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py
@@ -9,7 +9,7 @@ class STLRX_Test(CStlGeneral_Test):
"""Tests for RX feature"""
def setUp(self):
- 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)
@@ -35,6 +35,7 @@ class STLRX_Test(CStlGeneral_Test):
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])
@@ -252,15 +253,16 @@ class STLRX_Test(CStlGeneral_Test):
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 = STLTXSingleBurst(total_pkts = total_pkts,
- percentage = self.rate_percent
- ))
+ mode = mode
+ )
streams.append(s)
print("\ninjecting {0} packets on port {1}".format(total_pkts, self.tx_port))
diff --git a/scripts/automation/regression/stateless_tests/trex_client_pkg_test.py b/scripts/automation/regression/stateless_tests/trex_client_pkg_test.py
index 905882fe..14ef36f7 100755
--- a/scripts/automation/regression/stateless_tests/trex_client_pkg_test.py
+++ b/scripts/automation/regression/stateless_tests/trex_client_pkg_test.py
@@ -3,9 +3,6 @@ from .stl_general_test import CStlGeneral_Test, CTRexScenario
from misc_methods import run_command
from nose.plugins.attrib import attr
-def setUpModule():
- CStlGeneral_Test.unzip_client_package()
-
@attr('client_package')
class CTRexClientPKG_Test(CStlGeneral_Test):
"""This class tests TRex client package"""
@@ -14,14 +11,13 @@ class CTRexClientPKG_Test(CStlGeneral_Test):
CStlGeneral_Test.setUp(self)
# examples connect by their own
if CTRexScenario.stl_trex.is_connected():
- self.recover_after_trex_210_issue()
CTRexScenario.stl_trex.disconnect()
+ CStlGeneral_Test.unzip_client_package()
def tearDown(self):
# connect back at end of tests
if not CTRexScenario.stl_trex.is_connected():
CTRexScenario.stl_trex.connect()
- self.recover_after_trex_210_issue()
CStlGeneral_Test.tearDown(self)
def run_client_package_stl_example(self, python_version):
diff --git a/scripts/automation/regression/trex.py b/scripts/automation/regression/trex.py
index 44f2faba..aad8f041 100644
--- a/scripts/automation/regression/trex.py
+++ b/scripts/automation/regression/trex.py
@@ -38,8 +38,8 @@ class CTRexScenario:
is_copied = False
GAManager = None
no_daemon = False
- router_image = None
debug_image = False
+ test = None
class CTRexRunner:
"""This is an instance for generating a CTRexRunner"""
diff --git a/scripts/automation/regression/trex_unit_test.py b/scripts/automation/regression/trex_unit_test.py
index 915cd682..4f13a50f 100755
--- a/scripts/automation/regression/trex_unit_test.py
+++ b/scripts/automation/regression/trex_unit_test.py
@@ -30,7 +30,7 @@ import outer_packages
import nose
from nose.plugins import Plugin
-import logging
+from nose.selector import Selector
import CustomLogger
import misc_methods
from rednose import RedNose
@@ -43,11 +43,24 @@ from trex_stl_lib.utils.GAObjClass import GAmanager
import trex
import socket
from pprint import pprint
-import subprocess
-import re
import time
from distutils.dir_util import mkpath
+# nose overrides
+
+# option to select wanted test by name without file, class etc.
+def new_Selector_wantMethod(self, method, orig_Selector_wantMethod = Selector.wantMethod):
+ result = orig_Selector_wantMethod(self, method)
+ return result and (not CTRexScenario.test or CTRexScenario.test in getattr(method, '__name__', ''))
+
+Selector.wantMethod = new_Selector_wantMethod
+
+def new_Selector_wantFunction(self, function, orig_Selector_wantFunction = Selector.wantFunction):
+ result = orig_Selector_wantFunction(self, function)
+ return result and (not CTRexScenario.test or CTRexScenario.test in getattr(function, '__name__', ''))
+
+Selector.wantFunction = new_Selector_wantFunction
+
# override nose's strange representation of setUpClass errors
def __suite_repr__(self):
if hasattr(self.context, '__module__'): # inside class, setUpClass etc.
@@ -59,6 +72,8 @@ def __suite_repr__(self):
nose.suite.ContextSuite.__repr__ = __suite_repr__
nose.suite.ContextSuite.__str__ = __suite_repr__
+# /nose overrides
+
def check_trex_path(trex_path):
if os.path.isfile('%s/trex_daemon_server' % trex_path):
return os.path.abspath(trex_path)
@@ -78,7 +93,7 @@ def get_trex_path():
def address_to_ip(address):
- for i in range(10):
+ for i in range(5):
try:
return socket.gethostbyname(address)
except:
@@ -149,6 +164,8 @@ class CTRexTestConfiguringPlugin(Plugin):
parser.add_option('--trex-args', action='store', default = '',
dest="trex_args",
help="Additional TRex arguments (--no-watchdog etc.).")
+ parser.add_option('-t', '--test', action='store', default = '', dest='test',
+ help='Test name to run (without file, class etc.)')
def configure(self, options, conf):
@@ -160,6 +177,7 @@ class CTRexTestConfiguringPlugin(Plugin):
self.json_verbose = options.json_verbose
self.telnet_verbose = options.telnet_verbose
self.no_daemon = options.no_daemon
+ CTRexScenario.test = options.test
if self.collect_only or self.functional:
return
if CTRexScenario.setup_dir and options.config_path:
@@ -181,7 +199,7 @@ class CTRexTestConfiguringPlugin(Plugin):
self.loggerPath = options.log_path
# initialize CTRexScenario global testing class, to be used by all tests
CTRexScenario.configuration = self.configuration
- CTRexScenario.no_daemon = self.no_daemon
+ CTRexScenario.no_daemon = options.no_daemon
CTRexScenario.benchmark = self.benchmark
CTRexScenario.modes = set(self.modes)
CTRexScenario.server_logs = self.server_logs
@@ -347,15 +365,14 @@ if __name__ == "__main__":
nose_argv += sys_args
- config_plugin = CTRexTestConfiguringPlugin()
- red_nose = RedNose()
+ addplugins = [RedNose(), CTRexTestConfiguringPlugin()]
result = True
try:
if len(CTRexScenario.test_types['functional_tests']):
additional_args = ['--func'] + CTRexScenario.test_types['functional_tests']
if xml_arg:
additional_args += ['--with-xunit', xml_arg.replace('.xml', '_functional.xml')]
- result = nose.run(argv = nose_argv + additional_args, addplugins = [red_nose, config_plugin])
+ result = nose.run(argv = nose_argv + additional_args, addplugins = addplugins)
if len(CTRexScenario.test_types['stateful_tests']):
additional_args = ['--stf']
if '--warmup' in sys.argv:
@@ -365,14 +382,14 @@ if __name__ == "__main__":
additional_args.extend(['-a', '!client_package'])
if xml_arg:
additional_args += ['--with-xunit', xml_arg.replace('.xml', '_stateful.xml')]
- result = nose.run(argv = nose_argv + additional_args, addplugins = [red_nose, config_plugin]) and result
+ result = nose.run(argv = nose_argv + additional_args, addplugins = addplugins) and result
if len(CTRexScenario.test_types['stateless_tests']):
additional_args = ['--stl', 'stateless_tests/stl_general_test.py:STLBasic_Test.test_connectivity'] + CTRexScenario.test_types['stateless_tests']
if not test_client_package:
additional_args.extend(['-a', '!client_package'])
if xml_arg:
additional_args += ['--with-xunit', xml_arg.replace('.xml', '_stateless.xml')]
- result = nose.run(argv = nose_argv + additional_args, addplugins = [red_nose, config_plugin]) and result
+ result = nose.run(argv = nose_argv + additional_args, addplugins = addplugins) and result
#except Exception as e:
# result = False
# print(e)
diff --git a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
index f044f623..ecf6083b 100755
--- a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
+++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
@@ -73,6 +73,8 @@ class CTRexClient(object):
sets a verbose output on supported class method.
default value : **False**
+ trex_args : string
+ additional arguments passed to TRex. For example, "-w 3 --no-watchdog"
:raises:
socket errors, in case server could not be reached.
@@ -82,22 +84,23 @@ class CTRexClient(object):
self.trex_host = socket.gethostbyname(trex_host)
except: # give it another try
self.trex_host = socket.gethostbyname(trex_host)
- self.trex_daemon_port = trex_daemon_port
- self.master_daemon_port = master_daemon_port
- self.trex_zmq_port = trex_zmq_port
- self.seq = None
- self._last_sample = time.time()
- self.__default_user = get_current_user()
- self.verbose = verbose
- self.result_obj = CTRexResult(max_history_size, filtered_latency_amount)
- self.decoder = JSONDecoder()
- self.history = jsonrpclib.history.History()
- self.master_daemon_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = master_daemon_port )
- self.master_daemon = jsonrpclib.Server(self.master_daemon_path, history = self.history)
- self.trex_server_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = trex_daemon_port )
- self.server = jsonrpclib.Server(self.trex_server_path, history = self.history)
- self.debug_image = debug_image
- self.trex_args = trex_args
+ self.trex_daemon_port = trex_daemon_port
+ self.master_daemon_port = master_daemon_port
+ self.trex_zmq_port = trex_zmq_port
+ self.seq = None
+ self._last_sample = time.time()
+ self.__default_user = get_current_user()
+ self.verbose = verbose
+ self.result_obj = CTRexResult(max_history_size, filtered_latency_amount)
+ self.decoder = JSONDecoder()
+ self.history = jsonrpclib.history.History()
+ self.master_daemon_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = master_daemon_port )
+ self.master_daemon = jsonrpclib.Server(self.master_daemon_path, history = self.history)
+ self.trex_server_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = trex_daemon_port )
+ self.server = jsonrpclib.Server(self.trex_server_path, history = self.history)
+ self.debug_image = debug_image
+ self.trex_args = trex_args
+ self.sample_to_run_finish = self.sample_until_finish # alias for legacy
def add (self, x, y):
@@ -310,7 +313,7 @@ class CTRexClient(object):
for i in range(int(timeout / poll_rate)):
if not self.get_trex_cmds():
return True
- sleep(poll_rate)
+ time.sleep(poll_rate)
if self.get_trex_cmds():
return False
return True
@@ -535,7 +538,7 @@ class CTRexClient(object):
finally:
self.prompt_verbose_data()
- def sample_until_condition (self, condition_func, time_between_samples = 5):
+ def sample_until_condition (self, condition_func, time_between_samples = 1):
"""
Automatically sets ongoing sampling of TRex data, with sampling rate described by time_between_samples.
@@ -549,7 +552,7 @@ class CTRexClient(object):
time_between_samples : int
determines the time between each sample of the server
- default value : **5**
+ default value : **1**
:return:
the first result object (see :class:`CTRexResult` for further details) of the TRex run on which the condition has been met.
@@ -579,15 +582,15 @@ class CTRexClient(object):
# this could come from provided method 'condition_func'
raise
- def sample_to_run_finish (self, time_between_samples = 5):
+ def sample_until_finish (self, time_between_samples = 1):
"""
- Automatically sets automatically sampling of TRex data with sampling rate described by time_between_samples until TRex run finished.
+ Automatically samples TRex data with sampling rate described by time_between_samples until TRex run finishes.
:parameters:
time_between_samples : int
determines the time between each sample of the server
- default value : **5**
+ default value : **1**
:return:
the latest result object (see :class:`CTRexResult` for further details) with sampled data.
@@ -609,7 +612,7 @@ class CTRexClient(object):
results = self.get_result_obj()
return results
- def sample_x_seconds (self, sample_time, time_between_samples = 5):
+ def sample_x_seconds (self, sample_time, time_between_samples = 1):
"""
Automatically sets ongoing sampling of TRex data for sample_time seconds, with sampling rate described by time_between_samples.
Does not stop the TRex afterwards!
@@ -623,7 +626,7 @@ class CTRexClient(object):
time_between_samples : int
determines the time between each sample of the server
- default value : **5**
+ default value : **1**
:return:
the first result object (see :class:`CTRexResult` for further details) of the TRex run after given sample_time.
@@ -637,12 +640,12 @@ class CTRexClient(object):
"""
# make sure TRex is running. raise exceptions here if any
self.wait_until_kickoff_finish()
- elapsed_time = 0
+ end_time = time.time() + sample_time
while self.is_running():
- if elapsed_time >= sample_time:
+ if time.time() < end_time:
+ time.sleep(time_between_samples)
+ else:
return self.get_result_obj()
- time.sleep(time_between_samples)
- elapsed_time += time_between_samples
raise UserWarning("TRex has stopped at %s seconds (before expected %s seconds)\nTry increasing test duration or decreasing sample_time" % (elapsed_time, sample_time))
def get_result_obj (self, copy_obj = True):
@@ -1271,7 +1274,7 @@ class CTRexResult(object):
.. tip:: | Use '.' to enter one level deeper in dictionary hierarchy.
| Use '[i]' to access the i'th indexed object of an array.
- tree_path_to_key : regex
+ regex : regex
apply a regex to filter results out from a multiple results set.
Filter applies only on keys of dictionary type.
@@ -1299,7 +1302,7 @@ class CTRexResult(object):
.. tip:: | Use '.' to enter one level deeper in dictionary hierarchy.
| Use '[i]' to access the i'th indexed object of an array.
- tree_path_to_key : regex
+ regex : regex
apply a regex to filter results out from a multiple results set.
Filter applies only on keys of dictionary type.
@@ -1352,7 +1355,7 @@ class CTRexResult(object):
if not len(self._history):
return -1
- return len(self.__get_value_by_path(self._history[-1], 'trex-global.data', 'opackets-\d+'))
+ return len(self.get_last_value('trex-global.data', 'opackets-\d+'))
def update_result_data (self, latest_dump):
@@ -1383,14 +1386,21 @@ class CTRexResult(object):
# check for up to 2% change between expected and actual
if (self._current_tx_rate['m_tx_bps'] > 0.98 * self._expected_tx_rate['m_tx_expected_bps']):
self._done_warmup = True
+ latest_dump['warmup_barrier'] = True
# handle latency data
if self.latency_checked:
- latency_per_port = self.get_last_value("trex-latecny-v2.data", "port-")
+ # fix typos, by "pointer"
+ if 'trex-latecny-v2' in latest_dump and 'trex-latency-v2' not in latest_dump:
+ latest_dump['trex-latency-v2'] = latest_dump['trex-latecny-v2']
+ if 'trex-latecny' in latest_dump and 'trex-latency' not in latest_dump:
+ latest_dump['trex-latency'] = latest_dump['trex-latecny']
+
+ latency_per_port = self.get_last_value("trex-latency-v2.data", "port-")
self._max_latency = self.__get_filtered_max_latency(latency_per_port, self.filtered_latency_amount)
- avg_latency = self.get_last_value("trex-latecny.data", "avg-")
+ avg_latency = self.get_last_value("trex-latency.data", "avg-")
self._avg_latency = CTRexResult.__avg_all_and_rename_keys(avg_latency)
- avg_win_latency_list = self.get_value_list("trex-latecny.data", "avg-")
+ avg_win_latency_list = self.get_value_list("trex-latency.data", "avg-")
self._avg_window_latency = CTRexResult.__calc_latency_win_stats(avg_win_latency_list)
tx_pkts = CTRexResult.__get_value_by_path(latest_dump, "trex-global.data.m_total_tx_pkts")
@@ -1427,12 +1437,12 @@ class CTRexResult(object):
for i, p in re.findall(r'(\d+)|([\w|-]+)', tree_path):
dct = dct[p or int(i)]
if regex is not None and isinstance(dct, dict):
- res = {}
- for key,val in dct.items():
- match = re.match(regex, key)
- if match:
- res[key]=val
- return res
+ res = {}
+ for key,val in dct.items():
+ match = re.match(regex, key)
+ if match:
+ res[key]=val
+ return res
else:
return dct
except (KeyError, TypeError):
@@ -1499,6 +1509,42 @@ class CTRexResult(object):
return result
+ # history iterator after warmup period
+ def _get_steady_state_history_iterator(self):
+ if not self.is_done_warmup():
+ raise Exception('Warm-up period not finished')
+ for index, res in enumerate(self._history):
+ if 'warmup_barrier' in res:
+ for steady_state_index in range(index, max(index, len(self._history) - 1)):
+ yield self._history[steady_state_index]
+ return
+ for index in range(len(self._history) - 1):
+ yield self._history[index]
+
+
+ def get_avg_steady_state_value(self, tree_path_to_key):
+ '''
+ Gets average value after warmup period.
+ For example: <result object>.get_avg_steady_state_value('trex-global.data.m_tx_bps')
+ Usually more accurate than latest history value.
+
+ :parameters:
+ tree_path_to_key : str
+ defines a path to desired data.
+
+ :return:
+ average value at steady state
+
+ :raises:
+ Exception in case steady state period was not reached or tree_path_to_key was not found in result.
+ '''
+ values_arr = [self.__get_value_by_path(res, tree_path_to_key) for res in self._get_steady_state_history_iterator()]
+ values_arr = list(filter(lambda x: x is not None, values_arr))
+ if not values_arr:
+ raise Exception('All the keys are None, probably wrong tree_path_to_key: %s' % tree_path_to_key)
+ return sum(values_arr) / float(len(values_arr))
+
+
if __name__ == "__main__":
c = CTRexClient('127.0.0.1')
print('restarting daemon')
diff --git a/scripts/automation/trex_control_plane/stl/console/trex_console.py b/scripts/automation/trex_control_plane/stl/console/trex_console.py
index ab70d357..9e3f2600 100755
--- a/scripts/automation/trex_control_plane/stl/console/trex_console.py
+++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py
@@ -459,7 +459,6 @@ class TRexConsole(TRexGeneralCmd):
self.stateless_client.start_line(line)
-
def help_start(self):
@@ -567,7 +566,8 @@ class TRexConsole(TRexGeneralCmd):
parser = parsing_opts.gen_parser(self,
"tui",
self.do_tui.__doc__,
- parsing_opts.XTERM)
+ parsing_opts.XTERM,
+ parsing_opts.LOCKED)
opts = parser.parse_args(line.split())
if opts is None:
@@ -590,7 +590,7 @@ class TRexConsole(TRexGeneralCmd):
with self.stateless_client.logger.supress():
- self.tui.show()
+ self.tui.show(self.stateless_client, locked = opts.locked)
def help_tui (self):
@@ -872,7 +872,8 @@ def main():
# TUI
if options.tui:
- console.do_tui("-x" if options.xtui else "")
+ console.do_tui("-x" if options.xtui else "-l")
+
else:
console.start()
diff --git a/scripts/automation/trex_control_plane/stl/console/trex_tui.py b/scripts/automation/trex_control_plane/stl/console/trex_tui.py
index d3be4435..32c6b1e0 100644
--- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py
+++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py
@@ -4,6 +4,7 @@ import os
import time
from collections import OrderedDict, deque
import datetime
+import readline
if sys.version_info > (3,0):
from io import StringIO
@@ -15,6 +16,10 @@ from trex_stl_lib.utils import text_tables
from trex_stl_lib import trex_stl_stats
from trex_stl_lib.utils.filters import ToggleFilter
+class TUIQuit(Exception):
+ pass
+
+
# for STL exceptions
from trex_stl_lib.api import *
@@ -210,7 +215,7 @@ class TrexTUILatencyStats(TrexTUIPanel):
super(TrexTUILatencyStats, self).__init__(mng, "lstats")
self.key_actions = OrderedDict()
self.key_actions['c'] = {'action': self.action_clear, 'legend': 'clear', 'show': True}
- self.key_actions['t'] = {'action': self.action_toggle_histogram, 'legend': 'toggle histogram', 'show': True}
+ self.key_actions['h'] = {'action': self.action_toggle_histogram, 'legend': 'histogram toggle', 'show': True}
self.is_histogram = False
@@ -238,6 +243,23 @@ class TrexTUILatencyStats(TrexTUIPanel):
self.stateless_client.latency_stats.clear_stats()
return ""
+
+# utilization stats
+class TrexTUIUtilizationStats(TrexTUIPanel):
+ def __init__ (self, mng):
+ super(TrexTUIUtilizationStats, self).__init__(mng, "ustats")
+ self.key_actions = {}
+
+ def show (self):
+ stats = self.stateless_client._get_formatted_stats(port_id_list = None, stats_mask = trex_stl_stats.UT_COMPAT)
+ # print stats to screen
+ for stat_type, stat_data in stats.items():
+ text_tables.print_table_with_header(stat_data.text_table, stat_type)
+
+ def get_key_actions (self):
+ return self.key_actions
+
+
# log
class TrexTUILog():
def __init__ (self):
@@ -258,24 +280,42 @@ class TrexTUILog():
print(msg)
+# a predicate to wrap function as a bool
+class Predicate(object):
+ def __init__ (self, func):
+ self.func = func
+
+ def __nonzero__ (self):
+ return True if self.func() else False
+ def __bool__ (self):
+ return True if self.func() else False
+
+
# Panels manager (contains server panels)
class TrexTUIPanelManager():
def __init__ (self, tui):
self.tui = tui
self.stateless_client = tui.stateless_client
self.ports = self.stateless_client.get_all_ports()
-
+ self.locked = False
self.panels = {}
self.panels['dashboard'] = TrexTUIDashBoard(self)
self.panels['sstats'] = TrexTUIStreamsStats(self)
self.panels['lstats'] = TrexTUILatencyStats(self)
+ self.panels['ustats'] = TrexTUIUtilizationStats(self)
self.key_actions = OrderedDict()
- self.key_actions['q'] = {'action': self.action_quit, 'legend': 'quit', 'show': True}
+
+ # we allow console only when ports are acquired
+ self.key_actions['ESC'] = {'action': self.action_none, 'legend': 'console', 'show': Predicate(lambda : not self.locked)}
+
+ self.key_actions['q'] = {'action': self.action_none, 'legend': 'quit', 'show': True}
self.key_actions['d'] = {'action': self.action_show_dash, 'legend': 'dashboard', 'show': True}
self.key_actions['s'] = {'action': self.action_show_sstats, 'legend': 'streams', 'show': True}
self.key_actions['l'] = {'action': self.action_show_lstats, 'legend': 'latency', 'show': True}
+ self.key_actions['u'] = {'action': self.action_show_ustats, 'legend': 'util', 'show': True}
+
# start with dashboard
self.main_panel = self.panels['dashboard']
@@ -290,7 +330,6 @@ class TrexTUIPanelManager():
self.show_log = False
-
def generate_legend (self):
self.legend = "\n{:<12}".format("browse:")
@@ -327,14 +366,18 @@ class TrexTUIPanelManager():
# on window switch or turn on / off of the TUI we call this
- def init (self, show_log = False):
+ def init (self, show_log = False, locked = False):
self.show_log = show_log
+ self.locked = locked
self.generate_legend()
- def show (self):
+ def show (self, show_legend):
self.main_panel.show()
self.print_connection_status()
- self.print_legend()
+
+ if show_legend:
+ self.generate_legend()
+ self.print_legend()
if self.show_log:
self.log.show()
@@ -350,21 +393,22 @@ class TrexTUIPanelManager():
msg = self.main_panel.get_key_actions()[ch]['action']()
else:
- msg = ""
+ return False
self.generate_legend()
-
- if msg == None:
- return False
- else:
- if msg:
- self.log.add_event(msg)
- return True
+ return True
+
+ #if msg == None:
+ # return False
+ #else:
+ # if msg:
+ # self.log.add_event(msg)
+ # return True
# actions
- def action_quit (self):
+ def action_none (self):
return None
def action_show_dash (self):
@@ -392,6 +436,11 @@ class TrexTUIPanelManager():
self.init(self.show_log)
return ""
+ def action_show_ustats(self):
+ self.main_panel = self.panels['ustats']
+ self.init(self.show_log)
+ return ""
+
# shows a textual top style window
class TrexTUI():
@@ -404,18 +453,7 @@ class TrexTUI():
self.stateless_client = stateless_client
self.pm = TrexTUIPanelManager(self)
-
-
-
- def handle_key_input (self):
- # try to read a single key
- ch = os.read(sys.stdin.fileno(), 1).decode()
- if ch != None and len(ch) > 0:
- return (self.pm.handle_key(ch), True)
-
- else:
- return (True, False)
-
+
def clear_screen (self):
#os.system('clear')
@@ -423,17 +461,16 @@ class TrexTUI():
sys.stdout.write("\x1b[2J\x1b[H")
+ def show (self, client, show_log = False, locked = False):
+ with AsyncKeys(client, locked) as async_keys:
+ self.async_keys = async_keys
+ self.show_internal(show_log, locked)
- def show (self, show_log = False):
- # init termios
- old_settings = termios.tcgetattr(sys.stdin)
- new_settings = termios.tcgetattr(sys.stdin)
- new_settings[3] = new_settings[3] & ~(termios.ECHO | termios.ICANON) # lflags
- new_settings[6][termios.VMIN] = 0 # cc
- new_settings[6][termios.VTIME] = 0 # cc
- termios.tcsetattr(sys.stdin, termios.TCSADRAIN, new_settings)
- self.pm.init(show_log)
+
+ def show_internal (self, show_log, locked):
+
+ self.pm.init(show_log, locked)
self.state = self.STATE_ACTIVE
self.draw_policer = 0
@@ -441,11 +478,11 @@ class TrexTUI():
try:
while True:
# draw and handle user input
- cont, force_draw = self.handle_key_input()
- self.draw_screen(force_draw)
- if not cont:
- break
- time.sleep(0.1)
+ status = self.async_keys.tick(self.pm)
+
+ self.draw_screen(status)
+ if status == AsyncKeys.STATUS_NONE:
+ time.sleep(0.001)
# regular state
if self.state == self.STATE_ACTIVE:
@@ -473,34 +510,402 @@ class TrexTUI():
self.state = self.STATE_LOST_CONT
- finally:
- # restore
- termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
+ except TUIQuit:
+ print("\nExiting TUI...")
print("")
# draw once
- def draw_screen (self, force_draw = False):
+ def draw_screen (self, status):
- if (self.draw_policer >= 5) or (force_draw):
+ # only redraw the keys line
+ if status == AsyncKeys.STATUS_REDRAW_KEYS:
+ self.clear_screen()
+ sys.stdout.write(self.last_snap)
+ self.async_keys.draw()
+ sys.stdout.flush()
+ return
+
+ if (self.draw_policer >= 500) or (status == AsyncKeys.STATUS_REDRAW_ALL):
# capture stdout to a string
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
- self.pm.show()
+ self.pm.show(show_legend = self.async_keys.is_legend_mode())
+ self.last_snap = mystdout.getvalue()
+
+ self.async_keys.draw()
sys.stdout = old_stdout
self.clear_screen()
- print(mystdout.getvalue())
-
+ sys.stdout.write(mystdout.getvalue())
+
sys.stdout.flush()
self.draw_policer = 0
else:
self.draw_policer += 1
+
def get_state (self):
return self.state
+
+
+
+
+# handles async IO
+class AsyncKeys:
+
+ MODE_LEGEND = 1
+ MODE_CONSOLE = 2
+
+ STATUS_NONE = 0
+ STATUS_REDRAW_KEYS = 1
+ STATUS_REDRAW_ALL = 2
+
+ def __init__ (self, client, locked = False):
+ self.engine_console = AsyncKeysEngineConsole(self, client)
+ self.engine_legend = AsyncKeysEngineLegend(self)
+ self.locked = locked
+
+ if locked:
+ self.engine = self.engine_legend
+ self.locked = True
+ else:
+ self.engine = self.engine_console
+ self.locked = False
+
+ def __enter__ (self):
+ # init termios
+ self.old_settings = termios.tcgetattr(sys.stdin)
+ new_settings = termios.tcgetattr(sys.stdin)
+ new_settings[3] = new_settings[3] & ~(termios.ECHO | termios.ICANON) # lflags
+ new_settings[6][termios.VMIN] = 0 # cc
+ new_settings[6][termios.VTIME] = 0 # cc
+ termios.tcsetattr(sys.stdin, termios.TCSADRAIN, new_settings)
+ return self
+
+ def __exit__ (self, type, value, traceback):
+ termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.old_settings)
+
+
+ def is_legend_mode (self):
+ return self.engine.get_type() == AsyncKeys.MODE_LEGEND
+
+ def is_console_mode (self):
+ return self.engine.get_type == AsyncKeys.MODE_CONSOLE
+
+ def switch (self):
+ if self.is_legend_mode():
+ self.engine = self.engine_console
+ else:
+ self.engine = self.engine_legend
+
+
+ def tick (self, pm):
+ seq = ''
+ # drain all chars
+ while True:
+ ch = os.read(sys.stdin.fileno(), 1).decode()
+ if not ch:
+ break
+ seq += ch
+
+ if not seq:
+ return self.STATUS_NONE
+
+ # ESC for switch
+ if seq == '\x1b':
+ if not self.locked:
+ self.switch()
+ return self.STATUS_REDRAW_ALL
+
+ # EOF (ctrl + D)
+ if seq == '\x04':
+ raise TUIQuit()
+
+ # pass tick to engine
+ return self.engine.tick(seq, pm)
+
+
+ def draw (self):
+ self.engine.draw()
+
+
+
+# Legend engine
+class AsyncKeysEngineLegend:
+ def __init__ (self, async):
+ self.async = async
+
+ def get_type (self):
+ return self.async.MODE_LEGEND
+
+ def tick (self, seq, pm):
+
+ if seq == 'q':
+ raise TUIQuit()
+
+ # ignore escapes
+ if len(seq) > 1:
+ return AsyncKeys.STATUS_NONE
+
+ rc = pm.handle_key(seq)
+ return AsyncKeys.STATUS_REDRAW_ALL if rc else AsyncKeys.STATUS_NONE
+
+ def draw (self):
+ pass
+
+
+# console engine
+class AsyncKeysEngineConsole:
+ def __init__ (self, async, client):
+ self.async = async
+ self.lines = deque(maxlen = 100)
+
+ self.ac = {'start' : client.start_line,
+ 'stop' : client.stop_line,
+ 'pause' : client.pause_line,
+ 'resume': client.resume_line,
+ 'update': client.update_line,
+ 'quit' : self.action_quit,
+ 'q' : self.action_quit,
+ 'exit' : self.action_quit,
+ 'help' : self.action_help,
+ '?' : self.action_help}
+
+ # fetch readline history and add relevants
+ for i in range(0, readline.get_current_history_length()):
+ cmd = readline.get_history_item(i)
+ if cmd and cmd.split()[0] in self.ac:
+ self.lines.appendleft(CmdLine(cmd))
+
+ # new line
+ self.lines.appendleft(CmdLine(''))
+ self.line_index = 0
+ self.last_status = ''
+
+ def action_quit (self, _):
+ raise TUIQuit()
+
+ def action_help (self, _):
+ return ' '.join([format_text(cmd, 'bold') for cmd in self.ac.keys()])
+
+ def get_type (self):
+ return self.async.MODE_CONSOLE
+
+
+ def handle_escape_char (self, seq):
+ # up
+ if seq == '\x1b[A':
+ self.line_index = min(self.line_index + 1, len(self.lines) - 1)
+
+ # down
+ elif seq == '\x1b[B':
+ self.line_index = max(self.line_index - 1, 0)
+
+ # left
+ elif seq == '\x1b[D':
+ self.lines[self.line_index].go_left()
+
+ # right
+ elif seq == '\x1b[C':
+ self.lines[self.line_index].go_right()
+
+ # del
+ elif seq == '\x1b[3~':
+ self.lines[self.line_index].del_key()
+
+ # home
+ elif seq == '\x1b[H':
+ self.lines[self.line_index].home_key()
+
+ # end
+ elif seq == '\x1b[F':
+ self.lines[self.line_index].end_key()
+ return True
+
+ # unknown key
+ else:
+ return AsyncKeys.STATUS_NONE
+
+ return AsyncKeys.STATUS_REDRAW_KEYS
+
+
+ def tick (self, seq, _):
+
+ # handle escape chars
+ if len(seq) > 1:
+ return self.handle_escape_char(seq)
+
+ # handle each char
+ for ch in seq:
+ return self.handle_single_key(ch)
+
+
+
+ def handle_single_key (self, ch):
+
+ # newline
+ if ch == '\n':
+ self.handle_cmd()
+
+ # backspace
+ elif ch == '\x7f':
+ self.lines[self.line_index].backspace()
+
+ # TAB
+ elif ch == '\t':
+ cur = self.lines[self.line_index].get()
+ if not cur:
+ return
+
+ matching_cmds = [x for x in self.ac if x.startswith(cur)]
+
+ common = os.path.commonprefix([x for x in self.ac if x.startswith(cur)])
+ if common:
+ if len(matching_cmds) == 1:
+ self.lines[self.line_index].set(common + ' ')
+ self.last_status = ''
+ else:
+ self.lines[self.line_index].set(common)
+ self.last_status = 'ambigious: '+ ' '.join([format_text(cmd, 'bold') for cmd in matching_cmds])
+
+
+ # simple char
+ else:
+ self.lines[self.line_index] += ch
+
+ return AsyncKeys.STATUS_REDRAW_KEYS
+
+
+ def split_cmd (self, cmd):
+ s = cmd.split(' ', 1)
+ op = s[0]
+ param = s[1] if len(s) == 2 else ''
+ return op, param
+
+
+ def handle_cmd (self):
+ cmd = self.lines[self.line_index].get().strip()
+ if not cmd:
+ return
+
+ op, param = self.split_cmd(cmd)
+
+ func = self.ac.get(op)
+ if func:
+ func_rc = func(param)
+
+ # take out the empty line
+ empty_line = self.lines.popleft()
+ assert(empty_line.ro_line == '')
+
+ if not self.lines or self.lines[0].ro_line != cmd:
+ self.lines.appendleft(CmdLine(cmd))
+
+ # back in
+ self.lines.appendleft(empty_line)
+ self.line_index = 0
+ readline.add_history(cmd)
+
+ # back to readonly
+ for line in self.lines:
+ line.invalidate()
+
+ assert(self.lines[0].modified == False)
+ if not func:
+ self.last_status = "unknown command: '{0}'".format(format_text(cmd.split()[0], 'bold'))
+ else:
+ if isinstance(func_rc, str):
+ self.last_status = func_rc
+ else:
+ self.last_status = format_text("[OK]", 'green') if func_rc else format_text(str(func_rc).replace('\n', ''), 'red')
+
+
+ def draw (self):
+ sys.stdout.write("\nPress 'ESC' for navigation panel...\n")
+ sys.stdout.write("status: {0}\n".format(self.last_status))
+ sys.stdout.write("\ntui>")
+ self.lines[self.line_index].draw()
+
+
+# a readline alike command line - can be modified during edit
+class CmdLine(object):
+ def __init__ (self, line):
+ self.ro_line = line
+ self.w_line = None
+ self.modified = False
+ self.cursor_index = len(line)
+
+ def get (self):
+ if self.modified:
+ return self.w_line
+ else:
+ return self.ro_line
+
+ def set (self, line, cursor_pos = None):
+ self.w_line = line
+ self.modified = True
+
+ if cursor_pos is None:
+ self.cursor_index = len(self.w_line)
+ else:
+ self.cursor_index = cursor_pos
+
+
+ def __add__ (self, other):
+ assert(0)
+
+
+ def __str__ (self):
+ return self.get()
+
+
+ def __iadd__ (self, other):
+
+ self.set(self.get()[:self.cursor_index] + other + self.get()[self.cursor_index:],
+ cursor_pos = self.cursor_index + len(other))
+
+ return self
+
+
+ def backspace (self):
+ if self.cursor_index == 0:
+ return
+
+ self.set(self.get()[:self.cursor_index - 1] + self.get()[self.cursor_index:],
+ self.cursor_index - 1)
+
+
+ def del_key (self):
+ if self.cursor_index == len(self.get()):
+ return
+
+ self.set(self.get()[:self.cursor_index] + self.get()[self.cursor_index + 1:],
+ self.cursor_index)
+
+ def home_key (self):
+ self.cursor_index = 0
+
+ def end_key (self):
+ self.cursor_index = len(self.get())
+
+ def invalidate (self):
+ self.modified = False
+ self.w_line = None
+ self.cursor_index = len(self.ro_line)
+
+ def go_left (self):
+ self.cursor_index = max(0, self.cursor_index - 1)
+
+ def go_right (self):
+ self.cursor_index = min(len(self.get()), self.cursor_index + 1)
+
+ def draw (self):
+ sys.stdout.write(self.get())
+ sys.stdout.write('\b' * (len(self.get()) - self.cursor_index))
+
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/examples/stl_imix.py b/scripts/automation/trex_control_plane/stl/examples/stl_imix.py
index 46d86b2b..875186ba 100644
--- a/scripts/automation/trex_control_plane/stl/examples/stl_imix.py
+++ b/scripts/automation/trex_control_plane/stl/examples/stl_imix.py
@@ -5,6 +5,7 @@ import time
import json
from pprint import pprint
import argparse
+import sys
# IMIX test
# it maps the ports to sides
@@ -97,6 +98,7 @@ def imix_test (server, mult):
except STLError as e:
passed = False
print(e)
+ sys.exit(1)
finally:
c.disconnect()
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 c1833754..7c8a5fbf 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
@@ -511,8 +511,7 @@ class STLClient(object):
self.connected = False
# API classes
- self.api_vers = [ {'type': 'core', 'major': 1, 'minor':2 }
- ]
+ self.api_vers = [ {'type': 'core', 'major': 1, 'minor': 3 } ]
self.api_h = {'core': None}
# logger
@@ -555,14 +554,17 @@ class STLClient(object):
self.latency_stats = trex_stl_stats.CLatencyStats(self.ports)
+ self.util_stats = trex_stl_stats.CUtilStats(self)
+
self.stats_generator = trex_stl_stats.CTRexInfoGenerator(self.global_stats,
self.ports,
self.flow_stats,
self.latency_stats,
+ self.util_stats,
self.async_client.monitor)
-
+
############# private functions - used by the class itself ###########
@@ -1284,7 +1286,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
@@ -1303,21 +1305,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:
@@ -1340,7 +1355,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
@@ -1394,7 +1411,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
@@ -1664,6 +1690,23 @@ class STLClient(object):
if not rc:
raise STLError(rc)
+ @__api_check(True)
+ def get_util_stats(self):
+ """
+ Get utilization stats:
+ History of TRex CPU utilization per thread (list of lists)
+ MBUFs memory consumption per CPU socket.
+
+ :parameters:
+ None
+
+ :raises:
+ + :exc:`STLError`
+
+ """
+ self.logger.pre_cmd('Getting Utilization stats')
+ return self.util_stats.get_stats()
+
@__api_check(True)
def reset(self, ports = None):
@@ -1878,8 +1921,6 @@ class STLClient(object):
raise STLError(rc)
-
-
@__api_check(True)
def stop (self, ports = None, rx_delay_ms = 10):
"""
@@ -2274,6 +2315,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:
@@ -2284,12 +2327,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
@@ -2385,13 +2427,14 @@ class STLClient(object):
rc = f(*args)
except STLError as e:
client.logger.log("Log:\n" + format_text(e.brief() + "\n", 'bold'))
- return
+ return RC_ERR(e.brief())
# if got true - print time
if rc:
delta = time.time() - time1
client.logger.log(format_time(delta) + "\n")
+ return rc
return wrap
@@ -2544,14 +2587,14 @@ class STLClient(object):
opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True)
if opts is None:
- return
+ return RC_ERR("invalid arguments for 'start'")
active_ports = list_intersect(self.get_active_ports(), opts.ports)
if active_ports:
if not opts.force:
msg = "Port(s) {0} are active - please stop them or add '--force'\n".format(active_ports)
self.logger.log(format_text(msg, 'bold'))
- return
+ return RC_ERR(msg)
else:
self.stop(active_ports)
@@ -2569,8 +2612,10 @@ class STLClient(object):
else:
# must be exact
if len(opts.ports) != len(opts.tunables):
- self.logger.log('tunables section count must be 1 or exactly as the number of ports: got {0}'.format(len(opts.tunables)))
- return
+ msg = 'tunables section count must be 1 or exactly as the number of ports: got {0}'.format(len(opts.tunables))
+ self.logger.log(msg)
+ return RC_ERR(msg)
+
tunables = opts.tunables
@@ -2590,9 +2635,10 @@ class STLClient(object):
self.add_streams(profile.get_streams(), ports = port)
except STLError as e:
- self.logger.log(format_text("\nError while loading profile '{0}'\n".format(opts.file[0]), 'bold'))
+ msg = format_text("\nError while loading profile '{0}'\n".format(opts.file[0]), 'bold')
+ self.logger.log(msg)
self.logger.log(e.brief() + "\n")
- return
+ return RC_ERR(msg)
if opts.dry:
@@ -2604,8 +2650,7 @@ class STLClient(object):
opts.duration,
opts.total)
- # true means print time
- return True
+ return RC_OK()
@@ -2619,23 +2664,25 @@ class STLClient(object):
opts = parser.parse_args(line.split(), default_ports = self.get_active_ports(), verify_acquired = True)
if opts is None:
- return
+ return RC_ERR("invalid arguments for 'stop'")
# find the relevant ports
ports = list_intersect(opts.ports, self.get_active_ports())
if not ports:
if not opts.ports:
- self.logger.log('stop - no active ports')
+ msg = 'stop - no active ports'
else:
- self.logger.log('stop - no active traffic on ports {0}'.format(opts.ports))
- return
+ msg = 'stop - no active traffic on ports {0}'.format(opts.ports)
+
+ self.logger.log(msg)
+ return RC_ERR(msg)
# call API
self.stop(ports)
# true means print time
- return True
+ return RC_OK()
@__console
@@ -2651,22 +2698,24 @@ class STLClient(object):
opts = parser.parse_args(line.split(), default_ports = self.get_active_ports(), verify_acquired = True)
if opts is None:
- return
+ return RC_ERR("invalid arguments for 'update'")
# find the relevant ports
ports = list_intersect(opts.ports, self.get_active_ports())
if not ports:
if not opts.ports:
- self.logger.log('update - no active ports')
+ msg = 'update - no active ports'
else:
- self.logger.log('update - no active traffic on ports {0}'.format(opts.ports))
- return
+ msg = 'update - no active traffic on ports {0}'.format(opts.ports)
+
+ self.logger.log(msg)
+ return RC_ERR(msg)
self.update(ports, opts.mult, opts.total, opts.force)
# true means print time
- return True
+ return RC_OK()
@__console
@@ -2679,26 +2728,29 @@ class STLClient(object):
opts = parser.parse_args(line.split(), default_ports = self.get_transmitting_ports(), verify_acquired = True)
if opts is None:
- return
+ return RC_ERR("invalid arguments for 'pause'")
# check for already paused case
if opts.ports and is_sub_list(opts.ports, self.get_paused_ports()):
- self.logger.log('pause - all of port(s) {0} are already paused'.format(opts.ports))
- return
+ msg = 'pause - all of port(s) {0} are already paused'.format(opts.ports)
+ self.logger.log(msg)
+ return RC_ERR(msg)
# find the relevant ports
ports = list_intersect(opts.ports, self.get_transmitting_ports())
if not ports:
if not opts.ports:
- self.logger.log('pause - no transmitting ports')
+ msg = 'pause - no transmitting ports'
else:
- self.logger.log('pause - none of ports {0} are transmitting'.format(opts.ports))
- return
+ msg = 'pause - none of ports {0} are transmitting'.format(opts.ports)
+
+ self.logger.log(msg)
+ return RC_ERR(msg)
self.pause(ports)
# true means print time
- return True
+ return RC_OK()
@__console
@@ -2711,22 +2763,24 @@ class STLClient(object):
opts = parser.parse_args(line.split(), default_ports = self.get_paused_ports(), verify_acquired = True)
if opts is None:
- return
+ return RC_ERR("invalid arguments for 'resume'")
# find the relevant ports
ports = list_intersect(opts.ports, self.get_paused_ports())
if not ports:
if not opts.ports:
- self.logger.log('resume - no paused ports')
+ msg = 'resume - no paused ports'
else:
- self.logger.log('resume - none of ports {0} are paused'.format(opts.ports))
- return
+ msg = 'resume - none of ports {0} are paused'.format(opts.ports)
+
+ self.logger.log(msg)
+ return RC_ERR(msg)
self.resume(ports)
# true means print time
- return True
+ return RC_OK()
@__console
@@ -2931,7 +2985,7 @@ class STLClient(object):
if profile_type == 'python':
self.logger.log('Type: {:^12}'.format('Python Module'))
- self.logger.log('Tunables: {:^12}'.format(['{0} = {1}'.format(k ,v) for k, v in info['tunables'].items()]))
+ self.logger.log('Tunables: {:^12}'.format(str(['{0} = {1}'.format(k ,v) for k, v in info['tunables'].items()])))
elif profile_type == 'yaml':
self.logger.log('Type: {:^12}'.format('YAML'))
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 3effa1f0..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
@@ -11,7 +11,6 @@ import datetime
import time
import re
import math
-import copy
import threading
import pprint
@@ -22,13 +21,16 @@ PORT_STATUS = 'ps'
STREAMS_STATS = 's'
LATENCY_STATS = 'ls'
LATENCY_HISTOGRAM = 'lh'
+CPU_STATS = 'c'
+MBUF_STATS = 'm'
-ALL_STATS_OPTS = [GLOBAL_STATS, PORT_STATS, PORT_STATUS, STREAMS_STATS, LATENCY_STATS, PORT_GRAPH, LATENCY_HISTOGRAM]
+ALL_STATS_OPTS = [GLOBAL_STATS, PORT_STATS, PORT_STATUS, STREAMS_STATS, LATENCY_STATS, PORT_GRAPH, LATENCY_HISTOGRAM, CPU_STATS, MBUF_STATS]
COMPACT = [GLOBAL_STATS, PORT_STATS]
GRAPH_PORT_COMPACT = [GLOBAL_STATS, PORT_GRAPH]
SS_COMPAT = [GLOBAL_STATS, STREAMS_STATS] # stream stats
LS_COMPAT = [GLOBAL_STATS, LATENCY_STATS] # latency stats
LH_COMPAT = [GLOBAL_STATS, LATENCY_HISTOGRAM] # latency histogram
+UT_COMPAT = [GLOBAL_STATS, CPU_STATS, MBUF_STATS] # utilization
ExportableStats = namedtuple('ExportableStats', ['raw_data', 'text_table'])
@@ -104,6 +106,13 @@ def calculate_diff_raw (samples):
return total
+get_number_of_bytes_cache = {}
+# get number of bytes: '64b'->64, '9kb'->9000 etc.
+def get_number_of_bytes(val):
+ if val not in get_number_of_bytes_cache:
+ get_number_of_bytes_cache[val] = int(val[:-1].replace('k', '000'))
+ return get_number_of_bytes_cache[val]
+
# a simple object to keep a watch over a field
class WatchedField(object):
@@ -138,11 +147,12 @@ class CTRexInfoGenerator(object):
STLClient and the ports.
"""
- def __init__(self, global_stats_ref, ports_dict_ref, rx_stats_ref, latency_stats_ref, async_monitor):
+ def __init__(self, global_stats_ref, ports_dict_ref, rx_stats_ref, latency_stats_ref, util_stats_ref, async_monitor):
self._global_stats = global_stats_ref
self._ports_dict = ports_dict_ref
self._rx_stats_ref = rx_stats_ref
self._latency_stats_ref = latency_stats_ref
+ self._util_stats_ref = util_stats_ref
self._async_monitor = async_monitor
def generate_single_statistic(self, port_id_list, statistic_type):
@@ -167,6 +177,12 @@ class CTRexInfoGenerator(object):
elif statistic_type == LATENCY_HISTOGRAM:
return self._generate_latency_histogram()
+ elif statistic_type == CPU_STATS:
+ return self._generate_cpu_util_stats()
+
+ elif statistic_type == MBUF_STATS:
+ return self._generate_mbuf_util_stats()
+
else:
# ignore by returning empty object
return {}
@@ -404,6 +420,73 @@ class CTRexInfoGenerator(object):
stats_table.header(header)
return {"latency_histogram": ExportableStats(None, stats_table)}
+ def _generate_cpu_util_stats(self):
+ util_stats = self._util_stats_ref.get_stats(use_1sec_cache = True)
+ stats_table = text_tables.TRexTextTable()
+ if util_stats:
+ if 'cpu' not in util_stats:
+ raise Exception("Excepting 'cpu' section in stats %s" % util_stats)
+ cpu_stats = util_stats['cpu']
+ hist_len = len(cpu_stats[0])
+ avg_len = min(5, hist_len)
+ show_len = min(15, hist_len)
+ stats_table.header(['Thread', 'Avg', 'Latest'] + list(range(-1, 0 - show_len, -1)))
+ stats_table.set_cols_align(['l'] + ['r'] * (show_len + 1))
+ stats_table.set_cols_width([8, 3, 6] + [3] * (show_len - 1))
+ stats_table.set_cols_dtype(['t'] * (show_len + 2))
+ for i in range(min(14, len(cpu_stats))):
+ avg = int(round(sum(cpu_stats[i][:avg_len]) / avg_len))
+ stats_table.add_row([i, avg] + cpu_stats[i][:show_len])
+ else:
+ stats_table.add_row(['No Data.'])
+ return {'cpu_util(%)': ExportableStats(None, stats_table)}
+
+ def _generate_mbuf_util_stats(self):
+ util_stats = self._util_stats_ref.get_stats(use_1sec_cache = True)
+ stats_table = text_tables.TRexTextTable()
+ if util_stats:
+ if 'mbuf_stats' not in util_stats:
+ raise Exception("Excepting 'mbuf_stats' section in stats %s" % util_stats)
+ mbuf_stats = util_stats['mbuf_stats']
+ for mbufs_per_socket in mbuf_stats.values():
+ first_socket_mbufs = mbufs_per_socket
+ break
+ if not self._util_stats_ref.mbuf_types_list:
+ mbuf_keys = list(first_socket_mbufs.keys())
+ mbuf_keys.sort(key = get_number_of_bytes)
+ self._util_stats_ref.mbuf_types_list = mbuf_keys
+ types_len = len(self._util_stats_ref.mbuf_types_list)
+ stats_table.set_cols_align(['l'] + ['r'] * (types_len + 1))
+ stats_table.set_cols_width([10] + [7] * (types_len + 1))
+ stats_table.set_cols_dtype(['t'] * (types_len + 2))
+ stats_table.header([''] + self._util_stats_ref.mbuf_types_list + ['RAM(MB)'])
+ total_list = []
+ sum_totals = 0
+ for mbuf_type in self._util_stats_ref.mbuf_types_list:
+ sum_totals += first_socket_mbufs[mbuf_type][1] * get_number_of_bytes(mbuf_type) + 64
+ total_list.append(first_socket_mbufs[mbuf_type][1])
+ sum_totals *= len(list(mbuf_stats.values()))
+ total_list.append(int(sum_totals/1e6))
+ stats_table.add_row(['Total:'] + total_list)
+ stats_table.add_row(['Used:'] + [''] * (types_len + 1))
+ for socket_name in sorted(list(mbuf_stats.keys())):
+ mbufs = mbuf_stats[socket_name]
+ socket_show_name = socket_name.replace('cpu-', '').replace('-', ' ').capitalize() + ':'
+ sum_used = 0
+ used_list = []
+ percentage_list = []
+ for mbuf_type in self._util_stats_ref.mbuf_types_list:
+ used = mbufs[mbuf_type][1] - mbufs[mbuf_type][0]
+ sum_used += used * get_number_of_bytes(mbuf_type) + 64
+ used_list.append(used)
+ percentage_list.append('%s%%' % int(100 * used / mbufs[mbuf_type][1]))
+ used_list.append(int(sum_used/1e6))
+ stats_table.add_row([socket_show_name] + used_list)
+ stats_table.add_row(['Percent:'] + percentage_list + [''])
+ else:
+ stats_table.add_row(['No Data.'])
+ return {'mbuf_util': ExportableStats(None, stats_table)}
+
@staticmethod
def _get_rational_block_char(value, range_start, interval):
# in Konsole, utf-8 is sometimes printed with artifacts, return ascii for now
@@ -1010,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()))
@@ -1107,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()))
@@ -1254,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)] = {}
@@ -1276,7 +1377,27 @@ class CRxStats(CTRexStats):
return stats
-
+class CUtilStats(CTRexStats):
+
+ def __init__(self, client):
+ super(CUtilStats, self).__init__()
+ self.client = client
+ self.history = deque(maxlen = 1)
+ self.mbuf_types_list = None
+ self.last_update_ts = 0
+
+ def get_stats(self, use_1sec_cache = False):
+ time_now = time.time()
+ if self.last_update_ts + 1 < time_now or not self.history or not use_1sec_cache:
+ if self.client.is_connected():
+ rc = self.client._transmit('get_utilization')
+ if not rc:
+ raise Exception(rc)
+ self.last_update_ts = time_now
+ self.history.append(rc.data())
+ else:
+ self.history.append({})
+ return self.history[-1]
if __name__ == "__main__":
pass
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
index bb28d3af..1b417d65 100755
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
@@ -33,12 +33,15 @@ NO_PROMISCUOUS = 20
PROMISCUOUS_SWITCH = 21
TUNABLES = 22
REMOTE_FILE = 23
+LOCKED = 24
GLOBAL_STATS = 50
PORT_STATS = 51
PORT_STATUS = 52
STREAMS_STATS = 53
STATS_MASK = 54
+CPU_STATS = 55
+MBUF_STATS = 56
STREAMS_MASK = 60
# ALL_STREAMS = 61
@@ -319,6 +322,11 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
'default': False,
'help': "Starts TUI in xterm window"}),
+ LOCKED: ArgumentPack(['-l', '--locked'],
+ {'action': 'store_true',
+ 'dest': 'locked',
+ 'default': False,
+ 'help': "Locks TUI on legend mode"}),
FULL_OUTPUT: ArgumentPack(['--full'],
{'action': 'store_true',
@@ -340,6 +348,14 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
{'action': 'store_true',
'help': "Fetch only streams stats"}),
+ CPU_STATS: ArgumentPack(['-c'],
+ {'action': 'store_true',
+ 'help': "Fetch only CPU utilization stats"}),
+
+ MBUF_STATS: ArgumentPack(['-m'],
+ {'action': 'store_true',
+ 'help': "Fetch only MBUF utilization stats"}),
+
STREAMS_MASK: ArgumentPack(['--streams'],
{"nargs": '+',
'dest':'streams',
@@ -365,7 +381,9 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
STATS_MASK: ArgumentGroup(MUTEX, [GLOBAL_STATS,
PORT_STATS,
PORT_STATUS,
- STREAMS_STATS],
+ STREAMS_STATS,
+ CPU_STATS,
+ MBUF_STATS],
{})
}
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/exp/no_tcp_syn_ack.pcap b/scripts/exp/no_tcp_syn_ack.pcap
new file mode 100644
index 00000000..0cdcf91b
--- /dev/null
+++ b/scripts/exp/no_tcp_syn_ack.pcap
Binary files differ
diff --git a/scripts/exp/tcp_2_pkts.pcap b/scripts/exp/tcp_2_pkts.pcap
new file mode 100644
index 00000000..37c4f46b
--- /dev/null
+++ b/scripts/exp/tcp_2_pkts.pcap
Binary files differ
diff --git a/scripts/exp/tcp_low_ipg.pcap b/scripts/exp/tcp_low_ipg.pcap
new file mode 100644
index 00000000..78fb987e
--- /dev/null
+++ b/scripts/exp/tcp_low_ipg.pcap
Binary files differ
diff --git a/scripts/ko/src/readme.txt b/scripts/ko/src/readme.txt
index a8d028c9..0e9cddad 100755
--- a/scripts/ko/src/readme.txt
+++ b/scripts/ko/src/readme.txt
@@ -5,7 +5,7 @@
Fedora
$sudo yum install kernel-headers
or
-Ubunto
+Ubuntu
$sudo apt-get install linux-headers-$(uname -r)
2. from this dir do this
diff --git a/scripts/master_daemon.py b/scripts/master_daemon.py
index aa49f207..3a9d7ffe 100755
--- a/scripts/master_daemon.py
+++ b/scripts/master_daemon.py
@@ -181,7 +181,7 @@ parser.add_argument('--type', '--daemon-type', '--daemon_type', choices = daemon
action = 'store', help = 'Specify daemon type to start/stop etc.\nDefault is master_daemon.')
args = parser.parse_args()
-args.trex_dir = os.path.normpath(args.trex_dir)
+args.trex_dir = os.path.abspath(args.trex_dir)
args.daemon_type = args.daemon_type or 'master_daemon'
stl_rpc_proxy_dir = os.path.join(args.trex_dir, 'automation', 'trex_control_plane', 'stl', 'examples')
@@ -221,9 +221,13 @@ if args.action != 'show':
raise Exception('%s does not have function %s' % (daemon.name, args.action))
try:
func()
- except Exception as e:
- print(termstyle.red(e))
- sys.exit(1)
+ except:
+ try: # give it another try
+ sleep(1)
+ func()
+ except Exception as e:
+ print(termstyle.red(e))
+ sys.exit(1)
passive = {'start': 'started', 'restart': 'restarted', 'stop': 'stopped', 'show': 'running'}
diff --git a/scripts/stl/udp_1pkt_src_ip_split.py b/scripts/stl/udp_1pkt_src_ip_split.py
index 48e02433..99e7496c 100644
--- a/scripts/stl/udp_1pkt_src_ip_split.py
+++ b/scripts/stl/udp_1pkt_src_ip_split.py
@@ -8,11 +8,15 @@ class STLS1(object):
def __init__ (self):
self.fsize =64;
- def create_stream (self):
+ def create_stream (self, direction, cache_size):
# Create base packet and pad it to size
size = self.fsize - 4; # HW will add 4 bytes ethernet FCS
+ src_ip = '16.0.0.1'
+ dst_ip = '48.0.0.1'
+ if direction:
+ src_ip, dst_ip = dst_ip, src_ip
- base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)
+ base_pkt = Ether()/IP(src=src_ip,dst=dst_ip)/UDP(dport=12,sport=1025)
pad = max(0, size - len(base_pkt)) * 'x'
@@ -22,7 +26,7 @@ class STLS1(object):
STLVmFixIpv4(offset = "IP") # fix checksum
]
,split_by_field = "ip_src" # split to cores base on the tuple generator
- ,cache_size =255 # the cache size
+ ,cache_size = cache_size # the cache size
);
pkt = STLPktBuilder(pkt = base_pkt/pad,
@@ -33,9 +37,9 @@ class STLS1(object):
return stream
- def get_streams (self, direction = 0, **kwargs):
+ def get_streams (self, direction = 0, cache_size = 255, **kwargs):
# create 1 stream
- return [ self.create_stream() ]
+ return [ self.create_stream(direction, cache_size) ]
# dynamic load - used for trex console or simulator
diff --git a/scripts/trex-cfg b/scripts/trex-cfg
index 7a533b1f..44ef678c 100755
--- a/scripts/trex-cfg
+++ b/scripts/trex-cfg
@@ -11,7 +11,7 @@ fi
MACHINE_TYPE=`uname -m`
if [ ${MACHINE_TYPE} != 'x86_64' ]; then
- echo "ERROR, only 64bit OS are supported, please reinstall x86 64 bit OS"
+ echo "ERROR, only 64bit operating systems are supported, please reinstall x86 64 bit OS"
exit 1
fi
@@ -20,7 +20,7 @@ fi
if [ -d /mnt/huge ]; then
echo >> /dev/null
else
- echo "Create huge node"
+ echo "Creating huge node"
mkdir -p /mnt/huge
fi
@@ -38,19 +38,27 @@ for file in /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepag
done
if ! lsmod | grep -q igb_uio ; then
- echo "Load the drivers for the first time "
+ echo "Loading kernel drivers for the first time"
modprobe uio
+ if [ $? -ne 0 ]; then
+ echo "Failed inserting uio module, please check if it is installed"
+ exit 1
+ fi
km=ko/$SYS/igb_uio.ko
if [ -e $km ] ; then
insmod $km
+ if [ $? -ne 0 ]; then
+ echo "Failed inserting igb_uio module"
+ exit 1
+ fi
else
- echo "ERROR kernel module is not supported for this OS"
- echo "Please run the following commands: "
+ echo "ERROR: We don't have precompiled igb_uio.ko module for your kernel version"
+ echo "You can try compiling yourself, using the following commands:"
echo "\$cd ko/src "
echo "\$make "
echo "\$make install "
echo "\$cd - "
- echo "and try to run it again "
+ echo "Then try to run Trex again"
exit 1
fi
fi
@@ -60,5 +68,3 @@ PARENT_ARGS="$0 $@"
if ! ./dpdk_setup_ports.py --parent "$PARENT_ARGS"; then
exit 1
fi
-
-
diff --git a/src/bp_gtest.cpp b/src/bp_gtest.cpp
index 79ea2458..a1f80407 100755
--- a/src/bp_gtest.cpp
+++ b/src/bp_gtest.cpp
@@ -32,6 +32,7 @@ limitations under the License.
#include <common/cgen_map.h>
#include "platform_cfg.h"
#include "latency.h"
+#include "nat_check_flow_table.h"
int test_policer(){
CPolicer policer;
@@ -2400,7 +2401,20 @@ TEST_F(rx_check_system, rx_json) {
printf(" %s \n",json.c_str());
}
+class nat_check_flow_table : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ }
+
+ virtual void TearDown() {
+ }
+public:
+ CNatCheckFlowTable m_ft;
+};
+TEST_F(nat_check_flow_table, test1) {
+ m_ft.test();
+};
//////////////////////////////////////////////////////////////
@@ -2416,7 +2430,7 @@ public:
assert(ipv4->getTimeToLive()==255);
/* ip option packet */
printf(" rx got ip option packet ! \n");
- mg->handle_packet_ipv4(option,ipv4);
+ mg->handle_packet_ipv4(option, ipv4, true);
delay(10); // delay for queue flush
mg->handle_aging(); // flush the RxRing
}
@@ -2481,8 +2495,10 @@ protected:
m_flow_info.Delete();
}
public:
- CCapFileFlowInfo m_flow_info;
+ void load_cap_file_errors_helper(std::string cap_file, enum CCapFileFlowInfo::load_cap_file_err expect);
+public:
+ CCapFileFlowInfo m_flow_info;
};
TEST_F(file_flow_info, f1) {
@@ -2612,30 +2628,58 @@ TEST_F(file_flow_info, http_add_ipv6_option) {
po->preview.set_ipv6_mode_enable(false);
}
+void file_flow_info::load_cap_file_errors_helper(std::string cap_file, enum CCapFileFlowInfo::load_cap_file_err expect) {
+ enum CCapFileFlowInfo::load_cap_file_err err;
+
+ err = m_flow_info.load_cap_file(cap_file, 1, 0);
+ if (err == 0) err = m_flow_info.is_valid_template_load_time();
+ if (err != expect) {
+ printf("Error in testing file %s. Expected error to be %d, but it is %d\n", cap_file.c_str(), expect, err);
+ }
+ assert (err == expect);
+}
+
// Test error conditions when loading cap file
TEST_F(file_flow_info, load_cap_file_errors) {
- enum CCapFileFlowInfo::load_cap_file_err err;
- CParserOption * po =&CGlobalInfo::m_options;
- po->m_learn_mode = CParserOption::LEARN_MODE_TCP_ACK;
+ CParserOption *po = &CGlobalInfo::m_options;
- // file does not exist
- err = m_flow_info.load_cap_file("/tmp/not_exist",1,0);
- assert (err == CCapFileFlowInfo::kFileNotExist);
+ po->m_learn_mode = CParserOption::LEARN_MODE_DISABLED;
+ load_cap_file_errors_helper("/tmp/not_exist", CCapFileFlowInfo::kFileNotExist);
// file format not supported
- err = m_flow_info.load_cap_file("cap2/dns.yaml",1,0);
- assert (err == CCapFileFlowInfo::kFileNotExist);
- // udp in tcp learn mode
- err = m_flow_info.load_cap_file("./cap2/dns.pcap",1,0);
- assert (err == CCapFileFlowInfo::kNoTCPFromServer);
- // First TCP packet without syn
- err = m_flow_info.load_cap_file("./exp/tcp_no_syn.pcap",1,0);
- assert (err == CCapFileFlowInfo::kNoSyn);
- // TCP flags offset is too big
- err = m_flow_info.load_cap_file("./exp/many_ip_options.pcap",1,0);
- assert (err == CCapFileFlowInfo::kTCPOffsetTooBig);
+ load_cap_file_errors_helper("cap2/dns.yaml", CCapFileFlowInfo::kFileNotExist);
+ load_cap_file_errors_helper("cap2/dns.pcap", CCapFileFlowInfo::kOK);
+ load_cap_file_errors_helper("./exp/tcp_no_syn.pcap", CCapFileFlowInfo::kOK);
+ load_cap_file_errors_helper("./exp/many_ip_options.pcap", CCapFileFlowInfo::kOK);
// Non IP packet
- err = m_flow_info.load_cap_file("./exp/bad_not_ip.pcap",1,0);
- assert (err == CCapFileFlowInfo::kPktProcessFail);
+ load_cap_file_errors_helper("./exp/bad_not_ip.pcap", CCapFileFlowInfo::kPktProcessFail);
+ load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kOK);
+ // more than 1 flow in cap file
+ load_cap_file_errors_helper("./exp/syn_attack.pcap", CCapFileFlowInfo::kCapFileErr);
+
+ po->m_learn_mode = CParserOption::LEARN_MODE_IP_OPTION;
+ load_cap_file_errors_helper("cap2/dns.pcap", CCapFileFlowInfo::kOK);
+ load_cap_file_errors_helper("./exp/tcp_no_syn.pcap", CCapFileFlowInfo::kOK);
+ load_cap_file_errors_helper("./exp/many_ip_options.pcap", CCapFileFlowInfo::kIPOptionNotAllowed);
+ load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kOK);
+
+ po->m_learn_mode = CParserOption::LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND;
+ // udp in tcp learn mode
+ load_cap_file_errors_helper("cap2/dns.pcap", CCapFileFlowInfo::kNoTCPFromServer);
+ // no SYN in first packet
+ load_cap_file_errors_helper("./exp/tcp_no_syn.pcap", CCapFileFlowInfo::kNoSyn);
+ // TCP flags offset is too big. We don't allow IP option, so can comment this.
+ // open this if we do allow IP options in the future
+ // load_cap_file_errors_helper("./exp/many_ip_options.pcap", CCapFileFlowInfo::kTCPOffsetTooBig);
+ load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kOK);
+ load_cap_file_errors_helper("./exp/no_tcp_syn_ack.pcap", CCapFileFlowInfo::kOK);
+
+ po->m_learn_mode = CParserOption::LEARN_MODE_TCP_ACK;
+ // too short. only two packets
+ load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kTCPLearnModeBadFlow);
+ // no SYN+ACK
+ load_cap_file_errors_helper("./exp/no_tcp_syn_ack.pcap", CCapFileFlowInfo::kNoTCPSynAck);
+ // IPG between TCP handshake packets too low
+ load_cap_file_errors_helper("./exp/tcp_low_ipg.pcap", CCapFileFlowInfo::kTCPIpgTooLow);
}
//////////////////////////////////////////////////////////////
diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp
index db90107d..b229d9bf 100755
--- a/src/bp_sim.cpp
+++ b/src/bp_sim.cpp
@@ -498,40 +498,23 @@ void CRteMemPool::dump_in_case_of_error(FILE *fd){
dump(fd);
}
-std::string CRteMemPool::add_to_json(
- std::string name,
- rte_mempool_t * pool,
- bool last){
+void CRteMemPool::add_to_json(Json::Value &json, std::string name, rte_mempool_t * pool){
uint32_t p_free = rte_mempool_count(pool);
uint32_t p_size = pool->size;
- char buff[200];
- sprintf(buff,"\"%s\":[%llu,%llu]",name.c_str(),(unsigned long long)p_free,(unsigned long long)p_size);
- std::string json = std::string(buff) + (last?std::string(""):std::string(","));
- return (json);
+ json[name].append((unsigned long long)p_free);
+ json[name].append((unsigned long long)p_size);
}
-std::string CRteMemPool::dump_as_json(uint8_t id,bool last){
-
- char buff[200];
- sprintf(buff,"\"socket-%d\":{", (int)id);
-
- std::string json=std::string(buff);
-
- json+=add_to_json("64b",m_small_mbuf_pool);
- json+=add_to_json("128b",m_mbuf_pool_128);
- json+=add_to_json("256b",m_mbuf_pool_256);
- json+=add_to_json("512b",m_mbuf_pool_512);
- json+=add_to_json("1024b",m_mbuf_pool_1024);
- json+=add_to_json("2048b",m_mbuf_pool_2048);
- json+=add_to_json("4096b",m_mbuf_pool_4096);
- json+=add_to_json("9kb",m_mbuf_pool_9k,true);
-
- json+="}" ;
- if (last==false) {
- json+="," ;
- }
- return (json);
+void CRteMemPool::dump_as_json(Json::Value &json){
+ add_to_json(json, "64b", m_small_mbuf_pool);
+ add_to_json(json, "128b", m_mbuf_pool_128);
+ add_to_json(json, "256b", m_mbuf_pool_256);
+ add_to_json(json, "512b", m_mbuf_pool_512);
+ add_to_json(json, "1024b", m_mbuf_pool_1024);
+ add_to_json(json, "2048b", m_mbuf_pool_2048);
+ add_to_json(json, "4096b", m_mbuf_pool_4096);
+ add_to_json(json, "9kb", m_mbuf_pool_9k);
}
@@ -548,30 +531,24 @@ void CRteMemPool::dump(FILE *fd){
DUMP_MBUF("mbuf_9k",m_mbuf_pool_9k);
}
-////////////////////////////////////////
-
-std::string CGlobalInfo::dump_pool_as_json(void){
+////////////////////////////////////////
- std::string json="\"mbuf_stats\":{";
+void CGlobalInfo::dump_pool_as_json(Json::Value &json){
CPlatformSocketInfo * lpSocket =&m_socket;
- int last_socket=-1;
- /* calc the last socket */
- int i;
- for (i=0; i<(int)MAX_SOCKETS_SUPPORTED; i++) {
+ for (int i=0; i<(int)MAX_SOCKETS_SUPPORTED; i++) {
if (lpSocket->is_sockets_enable((socket_id_t)i)) {
- last_socket=i;
+ std::string socket_id = "cpu-socket-" + std::to_string(i);
+ m_mem_pool[i].dump_as_json(json["mbuf_stats"][socket_id]);
}
}
+}
- for (i=0; i<(int)MAX_SOCKETS_SUPPORTED; i++) {
- if (lpSocket->is_sockets_enable((socket_id_t)i)) {
- json+=m_mem_pool[i].dump_as_json(i,last_socket==i?true:false);
- }
- }
- json+="},";
- return json;
+std::string CGlobalInfo::dump_pool_as_json_str(void){
+ Json::Value json;
+ dump_pool_as_json(json);
+ return (json.toStyledString());
}
void CGlobalInfo::free_pools(){
@@ -771,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 );
}
@@ -785,8 +761,10 @@ void CFlowGenStats::clear(){
m_total_close_flows =0;
m_nat_lookup_no_flow_id=0;
m_nat_lookup_remove_flow_id=0;
+ m_nat_lookup_wait_ack_state = 0;
m_nat_lookup_add_flow_id=0;
m_nat_flow_timeout=0;
+ m_nat_flow_timeout_wait_ack = 0;
m_nat_flow_learn_error=0;
}
@@ -815,9 +793,12 @@ void CFlowGenStats::dump(FILE *fd){
DP(m_nat_lookup_no_flow_id);
DP(m_nat_lookup_remove_flow_id);
+ DP(m_nat_lookup_wait_ack_state);
DP(m_nat_lookup_add_flow_id);
DP(m_nat_flow_timeout);
+ DP(m_nat_flow_timeout_wait_ack);
DP_name("active_nat",(m_nat_lookup_add_flow_id-m_nat_lookup_remove_flow_id));
+ DP_name("active_nat_wait_syn", (m_nat_lookup_add_flow_id - m_nat_lookup_wait_ack_state));
DP(m_nat_flow_learn_error);
}
@@ -1962,42 +1943,115 @@ typedef CTmpFlowInfo * flow_tmp_t;
typedef std::map<uint16_t, flow_tmp_t> flow_tmp_map_t;
typedef flow_tmp_map_t::iterator flow_tmp_map_iter_t;
-
-
-bool CCapFileFlowInfo::is_valid_template_load_time(std::string & err){
- err="";
+enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::is_valid_template_load_time(){
int i;
for (i=0; i<Size(); i++) {
CFlowPktInfo * lp= GetPacket((uint32_t)i);
CPacketIndication * lpd=&lp->m_pkt_indication;
if ( lpd->getEtherOffset() !=0 ){
- err=" supported template Ether offset start is 0 \n";
- return (false);
+ fprintf(stderr, "Error: Bad CAP file. Ether offset start is not 0 in packet %d \n", i+1);
+ return kPktNotSupp;
}
- if ( lpd->getIpOffset() !=14 ){
- err=" supported template ip offset is 14 \n";
- return (false);
- }
- if ( lpd->is_ipv6() ){
- if ( lpd->getTcpOffset() != (14+40) ){
- err=" supported template tcp/udp offset is 54, no ipv6 option header is supported \n";
- return (false);
+
+ if ( CGlobalInfo::is_learn_mode() ) {
+ // We change TCP ports. Need them to be in first 64 byte mbuf.
+ // Since we also add IP option, and rx-check feature might add another IP option, better not allow
+ // OP options in this mode. If needed this limitation can be refined a bit.
+ if ( lpd->getTcpOffset() - lpd->getIpOffset() != 20 ) {
+ fprintf(stderr, "Error: Bad CAP file. In learn (NAT) mode, no IP options allowed \n");
+ return kIPOptionNotAllowed;
}
- }else{
- if ( lpd->getTcpOffset() != (14+20) ){
- err=" supported template tcp/udp offset is 34, no ipv4 option is allowed in this version \n";
- return (false);
+ if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP)) {
+ if (lpd->getIpProto() != IPPROTO_TCP && !lpd->m_desc.IsInitSide()) {
+ fprintf(stderr, "Error: In the chosen learn mode, all packets from server to client in CAP file should be TCP.\n");
+ fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n");
+ return kNoTCPFromServer;
+ }
}
}
}
if ( CGlobalInfo::is_learn_mode() ) {
- if ( GetPacket(0)->m_pkt_indication.m_desc.IsPluginEnable() ) {
- err="plugins are not supported with --learn mode \n";
- return(false);
+ CPacketIndication &pkt_0_indication = GetPacket(0)->m_pkt_indication;
+
+ if ( pkt_0_indication.m_desc.IsPluginEnable() ) {
+ fprintf(stderr, "Error: plugins are not supported with --learn mode \n");
+ return kPlugInWithLearn;
+ }
+
+ if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP)) {
+ if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
+ if (Size() < 3) {
+ fprintf(stderr
+ , "Error: In the chosen learn mode, need at least the 3 TCP handshake packets.\n");
+ fprintf(stderr
+ , " Please give different CAP file, or try different --learn-mode\n");
+ return kTCPLearnModeBadFlow;
+ }
+ }
+ CPacketIndication &pkt_1_indication = GetPacket(1)->m_pkt_indication;
+
+
+ // verify first packet is TCP SYN from client
+ TCPHeader *tcp = (TCPHeader *)(pkt_0_indication.getBasePtr() + pkt_0_indication.getTcpOffset());
+ if ( (! pkt_0_indication.m_desc.IsInitSide()) || (! tcp->getSynFlag()) ) {
+ fprintf(stderr, "Error: In the chosen learn mode, first TCP packet should be SYN from client side.\n");
+ fprintf(stderr, " In cap file, first packet side direction is %s. TCP header is:\n"
+ , pkt_0_indication.m_desc.IsInitSide() ? "outside":"inside");
+ tcp->dump(stderr);
+ fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n");
+ return kNoSyn;
+ }
+
+ // We want at least the TCP flags to be inside first mbuf
+ if (pkt_0_indication.getTcpOffset() + 14 > FIRST_PKT_SIZE) {
+ fprintf(stderr
+ , "Error: In the chosen learn mode, TCP flags offset should be less than %d, but it is %d.\n"
+ , FIRST_PKT_SIZE, pkt_0_indication.getTcpOffset() + 14);
+ fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n");
+ return kTCPOffsetTooBig;
+ }
+ if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
+ // To support TCP seq randomization from server to client, we need second packet in flow to be the server SYN+ACK
+ bool error = false;
+ if (pkt_1_indication.getIpProto() != IPPROTO_TCP) {
+ error = true;
+ } else {
+ TCPHeader *tcp = (TCPHeader *)(pkt_1_indication.getBasePtr() + pkt_1_indication.getTcpOffset());
+ if ( (! tcp->getSynFlag()) || (! tcp->getAckFlag()) || ( pkt_1_indication.m_desc.IsInitSide())) {
+ error = true;
+ }
+ }
+ if (error) {
+ fprintf(stderr, "Error: In the chosen learn mode, second packet should be SYN+ACK from server.\n");
+ fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n");
+ return kNoTCPSynAck;
+ }
+
+ CPacketIndication &pkt_2_indication = GetPacket(2)->m_pkt_indication;
+ if ( (! pkt_2_indication.m_desc.IsInitSide()) ) {
+ fprintf(stderr
+ , "Error: Wrong third packet. In the chosen learn mode, need at least the 3 TCP handshake packets.\n");
+ fprintf(stderr
+ , " Please give different CAP file, or try different --learn-mode\n");
+ return kTCPLearnModeBadFlow;
+ }
+ if ((pkt_0_indication.m_cap_ipg < (double)LEARN_MODE_MIN_IPG / 1000)
+ || (pkt_1_indication.m_cap_ipg < (double)LEARN_MODE_MIN_IPG / 1000)) {
+ fprintf(stderr
+ , "Error: Bad cap file timings. In the chosen learn mode");
+ fprintf(stderr, "IPG between TCP handshake packets should be at least %d msec.\n", LEARN_MODE_MIN_IPG);
+ fprintf(stderr, " Current delay is %f between second and first, %f between third and second"
+ , pkt_0_indication.m_cap_ipg, pkt_1_indication.m_cap_ipg);
+ fprintf(stderr
+ , " Please give different CAP file, try different --learn-mode, or edit ipg parameters in template file\n");
+ return kTCPIpgTooLow;
+ }
+ }
}
}
- return(true);
+
+ return(kOK);
}
@@ -2085,6 +2139,13 @@ void CCapFileFlowInfo::update_info(){
if ( lp->m_pkt_indication.m_desc.IsBiDirectionalFlow() ){
lp->mask_as_learn();
}
+
+ if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
+ // In this mode, we need to see the SYN+ACK as well.
+ lp = GetPacket(1);
+ assert(lp);
+ lp->m_pkt_indication.setTTL(TTL_RESERVE_DUPLICATE);
+ }
}
if ( ft.empty() )
@@ -2124,7 +2185,6 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st
m_total_errors=0;
CFlow * first_flow=0;
bool first_flow_fif_is_swap=false;
-
bool time_was_set=false;
double last_time=0.0;
CCapPktRaw raw_packet;
@@ -2199,35 +2259,10 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st
m_total_errors++;
}
}
-
- if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
- // in this mode, first TCP packet must be SYN from client.
- if (pkt_indication.getIpProto() == IPPROTO_TCP) {
- TCPHeader *tcp = (TCPHeader *)(pkt_indication.getBasePtr() + pkt_indication.getTcpOffset());
- if ( (! pkt_indication.m_desc.IsInitSide()) || (! tcp->getSynFlag()) ) {
- fprintf(stderr, "Error: In the chosen learn mode, first TCP packet should be SYN from client side.\n");
- fprintf(stderr, " In cap file, first packet side direction is %s. TCP header is:\n", pkt_indication.m_desc.IsInitSide() ? "outside":"inside");
- tcp->dump(stderr);
- fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n");
-
- return kNoSyn;
- }
- // We want at least the TCP flags to be inside first mbuf
- if (pkt_indication.getTcpOffset() + 14 > FIRST_PKT_SIZE) {
- fprintf(stderr, "Error: In the chosen learn mode, first TCP packet TCP flags offset should be less than %d, but it is %d.\n"
- , FIRST_PKT_SIZE, pkt_indication.getTcpOffset() + 14);
- fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n");
- return kTCPOffsetTooBig;
- }
- }
- }
-
}else{ /* no FIF */
-
pkt_indication.m_desc.SetFlowId(lpflow->flow_id);
if ( multi_flow_enable ==false ){
-
if (lpflow == first_flow) {
// add to
bool init_side=
@@ -2249,16 +2284,6 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st
}
}
-
- if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
- // This test must be down here, after initializing init side indication
- if (pkt_indication.getIpProto() != IPPROTO_TCP && !pkt_indication.m_desc.IsInitSide()) {
- fprintf(stderr, "Error: In the chosen learn mode, all packets from server to client in CAP file should be TCP.\n");
- fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n");
- return kNoTCPFromServer;
- }
- }
-
}else{
fprintf(stderr, "ERROR packet %d is not supported, should be Ethernet/IP(0x0800)/(TCP|UDP) format try to convert it using Wireshark !\n",cnt);
return kPktNotSupp;
@@ -2269,7 +2294,6 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st
}
}
-
/* set the last */
CFlowPktInfo * last_pkt =GetPacket((uint32_t)(Size()-1));
last_pkt->m_pkt_indication.m_desc.SetIsLastPkt(true);
@@ -2282,9 +2306,6 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st
lp_prev->m_pkt_indication.m_cap_ipg = lp->m_pkt_indication.m_cap_ipg-
lp_prev->m_pkt_indication.m_cap_ipg;
-
-
-
if ( lp->m_pkt_indication.m_desc.IsInitSide() !=
lp_prev->m_pkt_indication.m_desc.IsInitSide()) {
lp_prev->m_pkt_indication.m_desc.SetRtt(true);
@@ -2449,26 +2470,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;
@@ -3166,11 +3167,8 @@ bool CFlowGeneratorRec::Create(CFlowYamlInfo * info,
int res=m_flow_info.load_cap_file(info->m_name.c_str(),_id,m_info->m_plugin_id);
if ( res==0 ) {
fixup_ipg_if_needed();
- std::string err;
- /* verify that template are valid */
- bool is_valid=m_flow_info.is_valid_template_load_time(err);
- if (!is_valid) {
- printf("\n ERROR template file is not valid '%s' \n",err.c_str());
+
+ if (m_flow_info.is_valid_template_load_time() != 0) {
return (false);
}
m_flow_info.update_info();
@@ -3348,7 +3346,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;
@@ -3358,14 +3356,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(),
@@ -3744,11 +3742,11 @@ inline int CNodeGenerator::teardown(CFlowGenListPerThread * thread,
}
return (0);
}
-
+
template<int SCH_MODE>
-inline int CNodeGenerator::flush_file_realtime(dsec_t max_time,
+inline int CNodeGenerator::flush_file_realtime(dsec_t max_time,
dsec_t d_time,
bool always,
CFlowGenListPerThread * thread,
@@ -3767,9 +3765,9 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time,
sch_state_t state = scINIT;
node = m_p_queue.top();
- n_time = node->m_time + offset;
+ n_time = node->m_time + offset;
cur_time = now_sec();
-
+
while (state!=scTERMINATE) {
switch (state) {
@@ -3795,7 +3793,7 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time,
break;
}
node = m_p_queue.top();
- n_time = node->m_time + offset;
+ n_time = node->m_time + offset;
if ((n_time-cur_time)>EAT_WINDOW_DTIME) {
state=scINIT;
@@ -3805,7 +3803,7 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time,
break;
case scWAIT:
- do_sleep(cur_time,thread,n_time); // estimate loop
+ do_sleep(cur_time,thread,n_time); // estimate loop
state=scWORK;
break;
default:
@@ -3816,7 +3814,7 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time,
return (teardown(thread,always,old_offset,offset));
}
-FORCE_NO_INLINE int CNodeGenerator::flush_file_sim(dsec_t max_time,
+FORCE_NO_INLINE int CNodeGenerator::flush_file_sim(dsec_t max_time,
dsec_t d_time,
bool always,
CFlowGenListPerThread * thread,
@@ -3843,7 +3841,7 @@ FORCE_NO_INLINE int CNodeGenerator::flush_file_sim(dsec_t max_time,
return (teardown(thread,always,old_offset,0));
}
-int CNodeGenerator::flush_file(dsec_t max_time,
+int CNodeGenerator::flush_file(dsec_t max_time,
dsec_t d_time,
bool always,
CFlowGenListPerThread * thread,
@@ -3864,7 +3862,7 @@ int CNodeGenerator::flush_file(dsec_t max_time,
void CNodeGenerator::handle_flow_pkt(CGenNode *node, CFlowGenListPerThread *thread) {
- /*repeat and NAT is not supported */
+ /*repeat and NAT is not supported together */
if ( node->is_nat_first_state() ) {
node->set_nat_wait_state();
flush_one_node_to_file(node);
@@ -3875,7 +3873,7 @@ void CNodeGenerator::handle_flow_pkt(CGenNode *node, CFlowGenListPerThread *thre
if ( node->is_nat_wait_state() ) {
if (node->is_responder_pkt()) {
m_p_queue.pop();
- /* time out, need to free the flow and remove the association , we didn't get convertion yet*/
+ /* time out, need to free the flow and remove the association , we didn't get conversion yet*/
thread->terminate_nat_flows(node);
return;
@@ -3886,7 +3884,22 @@ void CNodeGenerator::handle_flow_pkt(CGenNode *node, CFlowGenListPerThread *thre
#endif
}
} else {
- assert(0);
+ if ( node->is_nat_wait_ack_state() ) {
+ if (node->is_initiator_pkt()) {
+ m_p_queue.pop();
+ /* time out, need to free the flow and remove the association , we didn't get conversion yet*/
+ thread->terminate_nat_flows(node);
+ return;
+
+ } else {
+ flush_one_node_to_file(node);
+#ifdef _DEBUG
+ update_stats(node);
+#endif
+ }
+ } else {
+ assert(0);
+ }
}
}
m_p_queue.pop();
@@ -4166,6 +4179,11 @@ int CFlowGenListPerThread::reschedule_flow(CGenNode *node){
void CFlowGenListPerThread::terminate_nat_flows(CGenNode *p){
m_stats.m_nat_flow_timeout++;
m_stats.m_nat_lookup_remove_flow_id++;
+ if (p->is_nat_wait_ack_state()) {
+ m_stats.m_nat_flow_timeout_wait_ack++;
+ } else {
+ m_stats.m_nat_lookup_wait_ack_state++;
+ }
m_flow_id_to_node_lookup.remove_no_lookup(p->get_short_fid());
free_last_flow_node( p);
}
@@ -4190,38 +4208,74 @@ void CFlowGenListPerThread::handle_latency_pkt_msg(CGenNodeLatencyPktInfo * msg)
m_node_gen.m_v_if->send_one_pkt((pkt_dir_t)msg->m_dir,msg->m_pkt);
}
-
void CFlowGenListPerThread::handle_nat_msg(CGenNodeNatInfo * msg){
int i;
+ bool first = true, second = true;
+
for (i=0; i<msg->m_cnt; i++) {
+ first = true;
+ second = true;
CNatFlowInfo * nat_msg=&msg->m_data[i];
CGenNode * node=m_flow_id_to_node_lookup.lookup(nat_msg->m_fid);
if (!node) {
- /* this should be move to a notification module */
- #ifdef NAT_TRACE_
+ /* this should be moved to a notification module */
+#ifdef NAT_TRACE_
printf(" ERORR not valid flow_id %d probably flow was aged \n",nat_msg->m_fid);
- #endif
+#endif
m_stats.m_nat_lookup_no_flow_id++;
continue;
}
- #ifdef NAT_TRACE_
- printf(" %.03f RX :set node %p:%x %x:%x:%x \n",now_sec() ,node,nat_msg->m_fid,nat_msg->m_external_ip,nat_msg->m_external_ip_server,nat_msg->m_external_port);
- #endif
- node->set_nat_ipv4_addr(nat_msg->m_external_ip);
- node->set_nat_ipv4_port(nat_msg->m_external_port);
- node->set_nat_ipv4_addr_server(nat_msg->m_external_ip_server);
-
- assert(node->is_nat_wait_state());
- if ( CGlobalInfo::is_learn_verify_mode() ){
- if (!node->is_external_is_eq_to_internal_ip() ){
- m_stats.m_nat_flow_learn_error++;
+
+ // Calculate diff between tcp seq of SYN packet, and TCP ack of SYN+ACK packet
+ // For supporting firewalls who do TCP seq num randomization
+ if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP)) {
+ if (node->is_nat_wait_state()) {
+ char *syn_pkt = node->m_flow_info->GetPacket(0)->m_packet->raw;
+ TCPHeader *tcp = (TCPHeader *)(syn_pkt + node->m_pkt_info->m_pkt_indication.getFastTcpOffset());
+ node->set_nat_tcp_seq_diff_client(nat_msg->m_tcp_seq - tcp->getSeqNumber());
+ if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
+ node->set_nat_wait_ack_state();
+ m_stats.m_nat_lookup_wait_ack_state++;
+ second = false;
+ } else {
+ node->set_nat_learn_state();
+ }
+ } else {
+ char *syn_ack_pkt = node->m_flow_info->GetPacket(1)->m_packet->raw;
+ TCPHeader *tcp = (TCPHeader *)(syn_ack_pkt + node->m_pkt_info->m_pkt_indication.getFastTcpOffset());
+ node->set_nat_tcp_seq_diff_server(nat_msg->m_tcp_seq - tcp->getSeqNumber());
+ assert(node->is_nat_wait_ack_state());
+ node->set_nat_learn_state();
+ first = false;
+ }
+ } else {
+ assert(node->is_nat_wait_state());
+ node->set_nat_learn_state();
+ }
+
+ if (first) {
+#ifdef NAT_TRACE_
+ printf(" %.03f RX :set node %p:%x %x:%x TCP diff %x\n"
+ , now_sec(), node,nat_msg->m_fid, nat_msg->m_external_ip, nat_msg->m_external_port
+ , node->get_nat_tcp_seq_diff_client());
+#endif
+
+ node->set_nat_ipv4_addr(nat_msg->m_external_ip);
+ node->set_nat_ipv4_port(nat_msg->m_external_port);
+
+ if ( CGlobalInfo::is_learn_verify_mode() ){
+ if (!node->is_external_is_eq_to_internal_ip() ||
+ node->get_nat_tcp_seq_diff_client() != 0) {
+ m_stats.m_nat_flow_learn_error++;
+ }
}
}
- node->set_nat_learn_state();
- /* remove from the hash */
- m_flow_id_to_node_lookup.remove_no_lookup(nat_msg->m_fid);
- m_stats.m_nat_lookup_remove_flow_id++;
+ if (second) {
+ /* remove from the hash */
+ m_flow_id_to_node_lookup.remove_no_lookup(nat_msg->m_fid);
+ m_stats.m_nat_lookup_remove_flow_id++;
+ }
}
}
@@ -4317,7 +4371,7 @@ 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();
@@ -4419,32 +4473,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;
@@ -4524,6 +4557,16 @@ double CFlowGenList::GetCpuUtil(){
return (c/m_threads_info.size());
}
+double CFlowGenList::GetCpuUtilRaw(){
+ int i;
+ double c=0.0;
+ for (i=0; i<(int)m_threads_info.size(); i++) {
+ CFlowGenListPerThread * lp=m_threads_info[i];
+ c+=lp->m_cpu_cp_u.GetValRaw();
+ }
+ return (c/m_threads_info.size());
+}
+
void CFlowGenList::UpdateFast(){
@@ -4755,21 +4798,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++) {
@@ -4782,6 +4824,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){
@@ -5068,37 +5119,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;
- fill_raw_packet(m,node,dir);
+ 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());
+ }
+}
+
+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);
@@ -5107,8 +5189,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){
@@ -6438,7 +6520,7 @@ void CGenNodeBase::free_base(){
CGenNodePCAP *p = (CGenNodePCAP *)this;
p->destroy();
return;
- }
+ }
if ( m_type == COMMAND ) {
CGenNodeCommand* p=(CGenNodeCommand*)this;
@@ -6446,4 +6528,3 @@ void CGenNodeBase::free_base(){
}
}
-
diff --git a/src/bp_sim.h b/src/bp_sim.h
index 18db61ca..b4ef54d1 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>
@@ -68,8 +69,6 @@ limitations under the License.
class CGenNodePCAP;
-#undef NAT_TRACE_
-
#define FORCE_NO_INLINE __attribute__ ((noinline))
#define FORCE_INLINE __attribute__((always_inline))
@@ -87,10 +86,6 @@ typedef struct {
*/
#define INET_PORTSTRLEN 5
-
-
-
-
/* VM commands */
class CMiniVMCmdBase {
@@ -186,6 +181,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;
@@ -627,7 +628,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);
}
}
@@ -639,26 +640,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);
}
@@ -684,16 +686,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);
@@ -771,7 +763,10 @@ public:
LEARN_MODE_DISABLED=0,
LEARN_MODE_TCP_ACK=1,
LEARN_MODE_IP_OPTION=2,
- LEARN_MODE_MAX=LEARN_MODE_IP_OPTION
+ LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND=3,
+ LEARN_MODE_MAX=LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND,
+ // This is used to check if 1 or 3 exist
+ LEARN_MODE_TCP=100
};
public:
@@ -782,6 +777,7 @@ public:
m_latency_rate =0;
m_latency_mask =0xffffffff;
m_latency_prev=0;
+ m_wait_before_traffic=1;
m_zmq_port=4500;
m_telnet_port =4501;
m_platform_factor=1.0;
@@ -793,7 +789,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;
@@ -812,6 +807,7 @@ public:
uint32_t m_latency_rate; /* pkt/sec for each thread/port zero disable */
uint32_t m_latency_mask;
uint32_t m_latency_prev;
+ uint32_t m_wait_before_traffic;
uint16_t m_rx_check_sample; /* the sample rate of flows */
uint16_t m_rx_check_hops;
uint16_t m_zmq_port;
@@ -819,7 +815,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;
@@ -829,7 +824,7 @@ public:
std::string cfg_file;
- std::string mac_file;
+ std::string client_cfg_file;
std::string platform_cfg_file;
std::string out_file;
@@ -887,6 +882,8 @@ public:
}
void dump(FILE *fd);
bool is_valid_opt_val(int val, int min, int max, const std::string &opt_name);
+
+ void verify();
};
@@ -1196,13 +1193,10 @@ public:
void dump_in_case_of_error(FILE *fd);
- std::string dump_as_json(uint8_t id,bool last);
+ void dump_as_json(Json::Value &json);
private:
- std::string add_to_json(std::string name,
- rte_mempool_t * pool,
- bool last=false);
-
+ void add_to_json(Json::Value &json, std::string name, rte_mempool_t * pool);
public:
rte_mempool_t * m_small_mbuf_pool; /* pool for start packets */
@@ -1261,7 +1255,11 @@ public:
}
static inline bool is_learn_mode(CParserOption::trex_learn_mode_e mode){
- return ( (m_options.m_learn_mode == mode));
+ if (mode == CParserOption::LEARN_MODE_TCP) {
+ return ((m_options.m_learn_mode == CParserOption::LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND)
+ || (m_options.m_learn_mode == CParserOption::LEARN_MODE_TCP_ACK));
+ } else
+ return (m_options.m_learn_mode == mode);
}
static inline bool is_ipv6_enable(void){
@@ -1296,7 +1294,8 @@ public:
}
- static std::string dump_pool_as_json(void);
+ static void dump_pool_as_json(Json::Value &json);
+ static std::string dump_pool_as_json_str(void);
public:
@@ -1572,12 +1571,12 @@ public:
CTupleGeneratorSmart *m_tuple_gen;
// cache line 1 - 64bytes waste of space !
- uint32_t m_nat_external_ipv4; /* client */
- uint32_t m_nat_external_ipv4_server;
- uint16_t m_nat_external_port;
-
- uint16_t m_nat_pad[3];
- mac_addr_align_t m_src_mac;
+ uint32_t m_nat_external_ipv4; // NAT client IP
+ uint32_t m_nat_tcp_seq_diff_client; // support for firewalls that do TCP seq num randomization
+ uint32_t m_nat_tcp_seq_diff_server; // And some do seq num randomization for server->client also
+ uint16_t m_nat_external_port; // NAT client port
+ uint16_t m_nat_pad[1];
+ const ClientCfg *m_client_cfg;
uint32_t m_src_idx;
uint32_t m_dest_idx;
uint32_t m_end_of_cache_line[6];
@@ -1713,6 +1712,15 @@ public:
return (btGetMaskBit16(m_flags,4,3)==2?true:false) ;
}
+ // We saw first TCP SYN. Waiting for SYN+ACK
+ inline void set_nat_wait_ack_state() {
+ btSetMaskBit16(m_flags, 4, 3, 3);
+ }
+
+ inline bool is_nat_wait_ack_state(){
+ return (btGetMaskBit16(m_flags,4,3) == 3) ? true : false;
+ }
+
inline void set_nat_learn_state(){
m_type=FLOW_PKT; /* normal operation .. repeat might work too */
}
@@ -1726,14 +1734,21 @@ public:
return (m_thread_id);
}
- inline void set_nat_ipv4_addr_server(uint32_t ip){
- m_nat_external_ipv4_server =ip;
+ inline void set_nat_tcp_seq_diff_client(uint32_t diff) {
+ m_nat_tcp_seq_diff_client = diff;
+ }
+
+ inline uint32_t get_nat_tcp_seq_diff_client() {
+ return m_nat_tcp_seq_diff_client;
}
- inline uint32_t get_nat_ipv4_addr_server(){
- return ( m_nat_external_ipv4_server );
+ inline void set_nat_tcp_seq_diff_server(uint32_t diff) {
+ m_nat_tcp_seq_diff_server = diff;
}
+ inline uint32_t get_nat_tcp_seq_diff_server() {
+ return m_nat_tcp_seq_diff_server;
+ }
inline void set_nat_ipv4_addr(uint32_t ip){
m_nat_external_ipv4 =ip;
@@ -1754,8 +1769,7 @@ public:
bool is_external_is_eq_to_internal_ip(){
/* this API is used to check TRex itself */
if ( (get_nat_ipv4_addr() == m_src_ip ) &&
- (get_nat_ipv4_port()==m_src_port) &&
- ( get_nat_ipv4_addr_server() == m_dest_ip) ) {
+ (get_nat_ipv4_port()==m_src_port)) {
return (true);
}else{
return (false);
@@ -1869,8 +1883,10 @@ public:
uint64_t m_total_close_flows;
uint64_t m_nat_lookup_no_flow_id;
uint64_t m_nat_lookup_remove_flow_id;
+ uint64_t m_nat_lookup_wait_ack_state;
uint64_t m_nat_lookup_add_flow_id;
uint64_t m_nat_flow_timeout;
+ uint64_t m_nat_flow_timeout_wait_ack;
uint64_t m_nat_flow_learn_error;
public:
@@ -1923,7 +1939,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;
@@ -3017,6 +3034,8 @@ inline void CFlowPktInfo::update_pkt_info(char *p,
(void)et;
uint16_t src_port = node->m_src_port;
+ uint32_t tcp_seq_diff_client = 0;
+ uint32_t tcp_seq_diff_server = 0;
pkt_dir_t ip_dir = node->cur_pkt_ip_addr_dir();
pkt_dir_t port_dir = node->cur_pkt_port_addr_dir();
@@ -3037,7 +3056,6 @@ inline void CFlowPktInfo::update_pkt_info(char *p,
}else{
if ( unlikely ( CGlobalInfo::is_learn_mode() ) ){
-
if (m_pkt_indication.m_desc.IsLearn()) {
/* might be done twice */
#ifdef NAT_TRACE_
@@ -3046,42 +3064,48 @@ inline void CFlowPktInfo::update_pkt_info(char *p,
ipv4->setTimeToLive(TTL_RESERVE_DUPLICATE);
/* first ipv4 option add the info in case of learn packet, usualy only the first packet */
- if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_IP_OPTION)) {
- CNatOption *lpNat =(CNatOption *)ipv4->getOption();
- lpNat->set_fid(node->get_short_fid());
- lpNat->set_thread_id(node->get_thread_id());
- } else {
- // This method only work on first TCP SYN
- if (ipv4->getProtocol() == IPPROTO_TCP) {
- TCPHeader *tcp = (TCPHeader *)(((uint8_t *)ipv4) + ipv4->getHeaderLength());
- if (tcp->getSynFlag()) {
- tcp->setAckNumber(CNatRxManager::calc_tcp_ack_val(node->get_short_fid(), node->get_thread_id()));
- }
+ if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_IP_OPTION)) {
+ CNatOption *lpNat =(CNatOption *)ipv4->getOption();
+ lpNat->set_fid(node->get_short_fid());
+ lpNat->set_thread_id(node->get_thread_id());
+ } else {
+ // This method only work on first TCP SYN
+ if (ipv4->getProtocol() == IPPROTO_TCP) {
+ TCPHeader *tcp = (TCPHeader *)(((uint8_t *)ipv4) + ipv4->getHeaderLength());
+ if (tcp->getSynFlag()) {
+ tcp->setAckNumber(CNatRxManager::calc_tcp_ack_val(node->get_short_fid(), node->get_thread_id()));
+ }
#ifdef NAT_TRACE_
- printf(" %.3f : flow_id: %x thread_id %x TCP ack %x\n",now_sec(), node->get_short_fid(), node->get_thread_id(), tcp->getAckNumber());
+ printf(" %.3f : flow_id: %x thread_id %x TCP ack %x seq %x\n"
+ ,now_sec(), node->get_short_fid(), node->get_thread_id(), tcp->getAckNumber()
+ , tcp->getSeqNumber());
#endif
- }
- }
+ }
+ }
}
/* in all cases update the ip using the outside ip */
if ( m_pkt_indication.m_desc.IsInitSide() ) {
#ifdef NAT_TRACE_
if (node->m_flags != CGenNode::NODE_FLAGS_LATENCY ) {
- printf(" %.3f : DP : i %x:%x -> %x flow_id: %lx\n",now_sec(),node->m_src_ip,node->m_src_port,node->m_dest_ip,node->m_flow_id);
+ printf(" %.3f : DP : i %x:%x -> %x flow_id: %lx\n",now_sec(), node->m_src_ip
+ , node->m_src_port, node->m_dest_ip, node->m_flow_id);
}
#endif
+ tcp_seq_diff_server = node->get_nat_tcp_seq_diff_server();
ipv4->updateIpSrc(node->m_src_ip);
ipv4->updateIpDst(node->m_dest_ip);
- }else{
+ } else {
#ifdef NAT_TRACE_
if (node->m_flags != CGenNode::NODE_FLAGS_LATENCY ) {
- printf(" %.3f : r %x -> %x:%x flow_id: %lx \n",now_sec(),node->m_dest_ip,node->m_src_ip,node->m_src_port,node->m_flow_id);
+ printf(" %.3f : r %x -> %x:%x flow_id: %lx \n", now_sec(), node->m_dest_ip
+ , node->m_src_ip, node->m_src_port, node->m_flow_id);
}
#endif
src_port = node->get_nat_ipv4_port();
- ipv4->updateIpSrc(node->get_nat_ipv4_addr_server());
+ tcp_seq_diff_client = node->get_nat_tcp_seq_diff_client();
+ ipv4->updateIpSrc(node->m_dest_ip);
ipv4->updateIpDst(node->get_nat_ipv4_addr());
}
@@ -3089,7 +3113,7 @@ inline void CFlowPktInfo::update_pkt_info(char *p,
#ifdef NAT_TRACE_
if (node->m_flags != CGenNode::NODE_FLAGS_LATENCY ) {
if ( m_pkt_indication.m_desc.IsInitSide() ==false ){
- printf(" %.3f : pkt ==> %x:%x %x:%x \n",now_sec(),node->get_nat_ipv4_addr(),node->get_nat_ipv4_addr_server(),
+ printf(" %.3f : pkt ==> %x %x:%x \n",now_sec(),node->get_nat_ipv4_addr(),
node->get_nat_ipv4_port(),node->m_src_port);
}else{
printf(" %.3f : pkt ==> init pkt sent \n",now_sec());
@@ -3137,8 +3161,10 @@ inline void CFlowPktInfo::update_pkt_info(char *p,
/* replace port */
if ( port_dir == CLIENT_SIDE ) {
m_tcp->setSourcePort(src_port);
+ m_tcp->setAckNumber(m_tcp->getAckNumber() + tcp_seq_diff_server);
}else{
m_tcp->setDestPort(src_port);
+ m_tcp->setAckNumber(m_tcp->getAckNumber() + tcp_seq_diff_client);
}
#ifdef RTE_DPDK
@@ -3409,6 +3435,8 @@ public:
class CCapFileFlowInfo {
public:
+ const int LEARN_MODE_MIN_IPG = 10; // msec
+
enum load_cap_file_err {
kOK = 0,
kFileNotExist,
@@ -3416,9 +3444,14 @@ public:
kNoSyn,
kTCPOffsetTooBig,
kNoTCPFromServer,
+ kNoTCPSynAck,
+ kTCPLearnModeBadFlow,
kPktNotSupp,
kPktProcessFail,
- kCapFileErr
+ kCapFileErr,
+ kPlugInWithLearn,
+ kIPOptionNotAllowed,
+ kTCPIpgTooLow
};
bool Create();
@@ -3435,7 +3468,7 @@ public:
/* update flow info */
void update_info();
- bool is_valid_template_load_time(std::string & err);
+ enum CCapFileFlowInfo::load_cap_file_err is_valid_template_load_time();
void save_to_erf(std::string cap_file_name,int pcap);
@@ -3930,13 +3963,15 @@ 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);
void DumpPktSize();
void UpdateFast();
double GetCpuUtil();
+ double GetCpuUtilRaw();
public:
double get_total_kcps();
@@ -3944,12 +3979,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;
};
@@ -3989,9 +4024,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/common/Network/Packet/TcpHeader.h b/src/common/Network/Packet/TcpHeader.h
index c19cd262..97575a60 100755
--- a/src/common/Network/Packet/TcpHeader.h
+++ b/src/common/Network/Packet/TcpHeader.h
@@ -23,6 +23,11 @@ class TCPHeader
{
public:
+ enum TCPHeader_enum_t
+ {
+ TCP_INVALID_PORT = 0
+ };
+
TCPHeader(){}
TCPHeader(uint16_t argSourcePort,
diff --git a/src/flow_stat.cpp b/src/flow_stat.cpp
index 5503434f..8c2f2566 100644
--- a/src/flow_stat.cpp
+++ b/src/flow_stat.cpp
@@ -796,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.
@@ -910,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;
}
@@ -947,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;
@@ -973,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++) {
@@ -1046,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/global_io_mode.cpp b/src/global_io_mode.cpp
index 2457599e..289863c9 100755
--- a/src/global_io_mode.cpp
+++ b/src/global_io_mode.cpp
@@ -103,6 +103,14 @@ bool CTrexGlobalIoMode::handle_io_modes(void){
m_g_mode=gNORMAL;
}
break;
+ case ccNat:
+ m_g_mode=gNAT;
+ m_nat_mode++;
+ if (m_nat_mode==natLAST) {
+ m_nat_mode = natDISABLE;
+ m_g_mode = gNORMAL;
+ }
+ break;
}
@@ -121,17 +129,18 @@ void CTrexGlobalIoMode::Dump(FILE *fd){
}
void CTrexGlobalIoMode::DumpHelp(FILE *fd){
- fprintf(fd,"Help for Interactive Commands - Trex \n" );
- fprintf(fd," d : Toggle, Disable all -> Noraml \n");
- fprintf(fd," n : Default mode all in Normal mode \n");
- fprintf(fd," h : Toggle, Help->Normal \n");
- fprintf(fd,"\n");
- fprintf(fd," p : Per ports Toggle mode, disable -> table -> normal \n");
- fprintf(fd," a : Global ports Toggle mode, disable -> enable \n");
- fprintf(fd," l : Latency Toggle mode, disable -> enable -> enhanced \n");
- fprintf(fd," r : Rx check Toggle mode, disable -> enable -> enhanced \n");
- fprintf(fd," m : memory stats , disable -> enable \n");
- fprintf(fd," Press h or 1 to go back to Normal mode \n");
+ fprintf(fd, "Help for Interactive Commands\n" );
+ fprintf(fd, " %c : Toggle, Disable all/Default \n", ccGDISABLE);
+ fprintf(fd, " %c : Go back to default mode \n", ccGNORAML);
+ fprintf(fd, " %c : Toggle, Help/Default \n", ccHELP);
+ fprintf(fd, "\n");
+ fprintf(fd, " %c : Per ports toggle disable -> table -> normal \n", ccGPP);
+ fprintf(fd, " %c : Global ports toggle disable/enable \n", ccGAP);
+ fprintf(fd, " %c : Latency toggle disable -> enable -> enhanced \n", ccGL);
+ fprintf(fd, " %c : Rx check toggle disable -> enable -> enhanced \n", ccGRC);
+ fprintf(fd, " %c : Memory stats toggle disable/enable \n", ccMem);
+ fprintf(fd, " %c : NAT pending flows toggle disable/enable \n", ccNat);
+ fprintf(fd, " Press %c or %c to go back to Normal mode \n", ccHELP, ccGNORAML);
}
diff --git a/src/global_io_mode.h b/src/global_io_mode.h
index 84b402b7..44fa4be5 100755
--- a/src/global_io_mode.h
+++ b/src/global_io_mode.h
@@ -48,12 +48,13 @@ public:
enum Chars{
ccHELP='h',
ccGDISABLE='d',
- ccGNORAML='n',
+ ccGNORAML='0',
ccGPP='p',
ccGAP='a',
ccGL='l',
ccGRC='r',
- ccMem='m'
+ ccMem='m',
+ ccNat='n'
};
enum CliDumpMode {
@@ -67,7 +68,8 @@ public:
gDISABLE=0, // no print at all
gHELP=1, // help
gNORMAL=2, // normal
- gMem=3
+ gMem=3,
+ gNAT
};
@@ -104,12 +106,20 @@ public:
};
typedef uint8_t RxCheckMode_t;
+ enum NatMode {
+ natDISABLE = 0,
+ natENABLE = 1,
+ natLAST = 2
+ };
+ typedef uint8_t NatMode_t;
+
Global_t m_g_mode;
bool m_g_disable_first;
PerPortCountersMode_t m_pp_mode;
AllPortCountersMode_t m_ap_mode;
LatecnyMode_t m_l_mode;
RxCheckMode_t m_rc_mode;
+ NatMode_t m_nat_mode;
public:
void set_mode(CliDumpMode mode);
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 a52f9e60..7037584b 100644
--- a/src/internal_api/trex_platform_api.h
+++ b/src/internal_api/trex_platform_api.h
@@ -28,6 +28,7 @@ limitations under the License.
#include <string.h>
#include "flow_stat_parser.h"
#include "trex_defs.h"
+#include <json/json.h>
/**
* Global stats
@@ -148,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;
@@ -156,6 +158,8 @@ public:
virtual bool get_promiscuous(uint8_t port_id) const = 0;
virtual void flush_dp_messages() const = 0;
virtual int get_active_pgids(flow_stat_active_t &result) const = 0;
+ virtual int get_cpu_util_full(cpu_util_full_t &result) const = 0;
+ virtual int get_mbuf_util(Json::Value &result) const = 0;
virtual CFlowStatParser *get_flow_stat_parser() const = 0;
virtual ~TrexPlatformApi() {}
};
@@ -180,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;
@@ -188,6 +193,8 @@ public:
bool get_promiscuous(uint8_t port_id) const;
void flush_dp_messages() const;
int get_active_pgids(flow_stat_active_t &result) const;
+ int get_cpu_util_full(cpu_util_full_t &result) const;
+ int get_mbuf_util(Json::Value &result) const;
CFlowStatParser *get_flow_stat_parser() const;
};
@@ -237,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;}
@@ -252,6 +260,8 @@ public:
void flush_dp_messages() const {
}
int get_active_pgids(flow_stat_active_t &result) const {return 0;}
+ int get_cpu_util_full(cpu_util_full_t &result) const {return 0;}
+ int get_mbuf_util(Json::Value &result) const {return 0;}
CFlowStatParser *get_flow_stat_parser() const {return new CFlowStatParser();}
private:
diff --git a/src/latency.cpp b/src/latency.cpp
index 841913cf..768e161b 100644
--- a/src/latency.cpp
+++ b/src/latency.cpp
@@ -436,7 +436,7 @@ bool CCPortLatency::check_packet(rte_mbuf_t * m,CRx_check_header * & rx_p) {
m_no_ipv4_option++;
return (false);
}
- m_parent->get_nat_manager()->handle_packet_ipv4(lp,parser.m_ipv4);
+ m_parent->get_nat_manager()->handle_packet_ipv4(lp, parser.m_ipv4, true);
opt_len -= CNatOption::noOPTION_LEN;
opt_ptr += CNatOption::noOPTION_LEN;
break;
@@ -445,10 +445,11 @@ bool CCPortLatency::check_packet(rte_mbuf_t * m,CRx_check_header * & rx_p) {
return (false);
} // End of switch
} // End of while
- if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)
- && parser.IsNatInfoPkt()) {
- m_parent->get_nat_manager()->handle_packet_ipv4(NULL, parser.m_ipv4);
- }
+
+ bool first;
+ if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP) && parser.IsNatInfoPkt(first)) {
+ m_parent->get_nat_manager()->handle_packet_ipv4(NULL, parser.m_ipv4, first);
+ }
return (true);
} // End of check for non-latency packet
@@ -910,6 +911,10 @@ void CLatencyManager::DumpShortRxCheck(FILE *fd){
}
}
+void CLatencyManager::dump_nat_flow_table(FILE *fd) {
+ m_nat_check_manager.Dump(fd);
+}
+
void CLatencyManager::rx_check_dump_json(std::string & json){
if ( get_is_rx_check_mode() ) {
m_rx_check_manager.dump_json(json );
diff --git a/src/latency.h b/src/latency.h
index 724621f0..63e50337 100644
--- a/src/latency.h
+++ b/src/latency.h
@@ -107,19 +107,27 @@ public:
}
// Check if this packet contains NAT info in TCP ack
- inline bool IsNatInfoPkt() {
- if (!m_ipv4 || (m_protocol != IPPROTO_TCP)) {
- return false;
- }
- if (! m_l4 || (m_l4 - rte_pktmbuf_mtod(m_m, uint8_t*) + TCP_HEADER_LEN) > m_m->data_len) {
- return false;
- }
- // If we are here, relevant fields from tcp header are guaranteed to be in first mbuf
- TCPHeader *tcp = (TCPHeader *)m_l4;
- if (!tcp->getSynFlag() || (tcp->getAckNumber() == 0)) {
- return false;
- }
- return true;
+ // first - set to true if this is the first packet of the flow. false otherwise.
+ // relevant only if return value is true
+ inline bool IsNatInfoPkt(bool &first) {
+ if (!m_ipv4 || (m_protocol != IPPROTO_TCP)) {
+ return false;
+ }
+ if (! m_l4 || (m_l4 - rte_pktmbuf_mtod(m_m, uint8_t*) + TCP_HEADER_LEN) > m_m->data_len) {
+ return false;
+ }
+ // If we are here, relevant fields from tcp header are guaranteed to be in first mbuf
+ // We want to handle SYN and SYN+ACK packets
+ TCPHeader *tcp = (TCPHeader *)m_l4;
+ if (! tcp->getSynFlag())
+ return false;
+
+ if (! tcp->getAckFlag()) {
+ first = true;
+ } else {
+ first = false;
+ }
+ return true;
}
public:
@@ -354,6 +362,7 @@ public:
void DumpRxCheck(FILE *fd); // dump all
void DumpShortRxCheck(FILE *fd); // dump short histogram of latency
+ void dump_nat_flow_table(FILE *fd);
void rx_check_dump_json(std::string & json);
uint16_t get_latency_header_offset(){
return ( m_pkt_gen.get_payload_offset() );
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 820371ab..7bff8253 100644
--- a/src/main_dpdk.cpp
+++ b/src/main_dpdk.cpp
@@ -141,8 +141,8 @@ public:
virtual int configure_drop_queue(CPhyEthIF * _if);
virtual void get_extended_stats(CPhyEthIF * _if,CPhyEthIFStats *stats)=0;
virtual void clear_extended_stats(CPhyEthIF * _if)=0;
- virtual int wait_for_stable_link()=0;
- virtual void wait_after_link_up(){};
+ virtual int wait_for_stable_link();
+ virtual void wait_after_link_up();
virtual bool flow_control_disable_supported(){return true;}
virtual bool hw_rx_stat_supported(){return false;}
virtual int get_rx_stats(CPhyEthIF * _if, uint32_t *pkts, uint32_t *prev_pkts, uint32_t *bytes, uint32_t *prev_bytes
@@ -538,6 +538,7 @@ enum { OPT_HELP,
OPT_ONLY_LATENCY,
OPT_1G_MODE,
OPT_LATENCY_PREVIEW ,
+ OPT_WAIT_BEFORE_TRAFFIC,
OPT_PCAP,
OPT_RX_CHECK,
OPT_IO_MODE,
@@ -548,12 +549,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,
@@ -602,6 +601,7 @@ static CSimpleOpt::SOption parser_options[] =
{ OPT_1G_MODE, "-1g", SO_NONE },
{ OPT_LATENCY_PREVIEW , "-k", SO_REQ_SEP },
+ { OPT_WAIT_BEFORE_TRAFFIC , "-w", SO_REQ_SEP },
{ OPT_PCAP, "--pcap", SO_NONE },
{ OPT_RX_CHECK, "--rx-check", SO_REQ_SEP },
{ OPT_IO_MODE, "--iom", SO_REQ_SEP },
@@ -612,12 +612,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 },
@@ -644,7 +642,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");
@@ -682,6 +680,8 @@ static int usage(){
printf(" \n");
printf(" -k [sec] : run latency test before starting the test. it will wait for x sec sending packet and x sec after that \n");
printf(" \n");
+ printf(" -w [sec] : wait between init of interfaces and sending traffic, default is 1\n");
+ printf(" \n");
printf(" --cfg [platform_yaml] : load and configure platform using this file see example in cfg/cfg_examplexx.yaml file \n");
printf(" this file is used to configure/mask interfaces cores affinity and mac addr \n");
@@ -690,9 +690,10 @@ static int usage(){
printf(" --ipv6 : work in ipv6 mode\n");
printf(" --learn (deprecated). Replaced by --learn-mode. To get older behaviour, use --learn-mode 2\n");
- printf(" --learn-mode [1-2] : Work in NAT environments, learn the dynamic NAT translation and ALG \n");
- printf(" 1 Use TCP ACK in first SYN to pass NAT translation information. Will work only for TCP streams. Initial SYN packet must be present in stream.\n");
+ printf(" --learn-mode [1-3] : Work in NAT environments, learn the dynamic NAT translation and ALG \n");
+ printf(" 1 Use TCP ACK in first SYN to pass NAT translation information. Will work only for TCP streams. Initial SYN packet must be first packet in stream.\n");
printf(" 2 Add special IP option to pass NAT translation information. Will not work on certain firewalls if they drop packets with IP options\n");
+ printf(" 3 Like 1, but without support for sequence number randomization in server->clien direction. Performance (flow/second) better than 1\n");
printf(" --learn-verify : Learn the translation, but intended for verification of the mechanism in cases that NAT does not exist \n");
printf(" \n");
printf(" --l-pkt-mode [0-3] : Set mode for sending latency packets.\n");
@@ -719,8 +720,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");
@@ -745,7 +744,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");
@@ -830,8 +829,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 :
@@ -846,9 +845,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;
@@ -954,6 +950,10 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t
sscanf(args.OptionArg(),"%d", &po->m_latency_prev);
break;
+ case OPT_WAIT_BEFORE_TRAFFIC :
+ sscanf(args.OptionArg(),"%d", &po->m_wait_before_traffic);
+ break;
+
case OPT_PCAP:
po->preview.set_pcap_mode_enable(true);
break;
@@ -980,13 +980,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;
@@ -1014,17 +1007,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()) {
@@ -1777,7 +1762,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);
@@ -1810,6 +1797,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;
@@ -2034,27 +2023,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.
@@ -2180,8 +2148,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;
@@ -2199,33 +2218,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++;
@@ -2233,19 +2249,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);
@@ -2446,8 +2460,10 @@ public:
uint64_t m_active_sockets;
uint64_t m_total_nat_time_out;
+ uint64_t m_total_nat_time_out_wait_ack;
uint64_t m_total_nat_no_fid ;
uint64_t m_total_nat_active ;
+ uint64_t m_total_nat_syn_wait;
uint64_t m_total_nat_open ;
uint64_t m_total_nat_learn_error ;
@@ -2468,6 +2484,7 @@ public:
float m_active_flows;
float m_open_flows;
float m_cpu_util;
+ float m_cpu_util_raw;
float m_rx_cpu_util;
float m_bw_per_core;
uint8_t m_threads;
@@ -2477,7 +2494,7 @@ public:
public:
void Dump(FILE *fd,DumpFormat mode);
void DumpAllPorts(FILE *fd);
- void dump_json(std::string & json, bool baseline,uint32_t stats_tick);
+ void dump_json(std::string & json, bool baseline);
private:
std::string get_field(std::string name,float &f);
std::string get_field(std::string name,uint64_t &f);
@@ -2517,7 +2534,7 @@ std::string CGlobalStats::get_field_port(int port,std::string name,uint64_t &f){
}
-void CGlobalStats::dump_json(std::string & json, bool baseline,uint32_t stats_tick){
+void CGlobalStats::dump_json(std::string & json, bool baseline){
/* refactor this to JSON */
json="{\"name\":\"trex-global\",\"type\":0,";
@@ -2535,6 +2552,7 @@ void CGlobalStats::dump_json(std::string & json, bool baseline,uint32_t stats_ti
#define GET_FIELD_PORT(p,f) get_field_port(p,std::string(#f),lp->f)
json+=GET_FIELD(m_cpu_util);
+ json+=GET_FIELD(m_cpu_util_raw);
json+=GET_FIELD(m_bw_per_core);
json+=GET_FIELD(m_rx_cpu_util);
json+=GET_FIELD(m_platform_factor);
@@ -2564,8 +2582,10 @@ void CGlobalStats::dump_json(std::string & json, bool baseline,uint32_t stats_ti
json+=GET_FIELD(m_socket_util);
json+=GET_FIELD(m_total_nat_time_out);
+ json+=GET_FIELD(m_total_nat_time_out_wait_ack);
json+=GET_FIELD(m_total_nat_no_fid );
json+=GET_FIELD(m_total_nat_active );
+ json+=GET_FIELD(m_total_nat_syn_wait);
json+=GET_FIELD(m_total_nat_open );
json+=GET_FIELD(m_total_nat_learn_error);
@@ -2584,10 +2604,6 @@ void CGlobalStats::dump_json(std::string & json, bool baseline,uint32_t stats_ti
json+=GET_FIELD_PORT(i,m_total_rx_pps);
}
json+=m_template.dump_as_json("template");
- if ( stats_tick %4==0){
- json+=CGlobalInfo::dump_pool_as_json(); /* no need a feq update beacuse it trash the cores D cache, once in 2 sec */
- }
-
json+="\"unknown\":0}}" ;
}
@@ -2605,7 +2621,12 @@ void CGlobalStats::DumpAllPorts(FILE *fd){
fprintf (fd," Platform_factor : %2.1f \n",m_platform_factor);
fprintf (fd," Total-Tx : %s ",double_to_human_str(m_tx_bps,"bps",KBYE_1000).c_str());
if ( CGlobalInfo::is_learn_mode() ) {
- fprintf (fd," Nat_time_out : %8llu \n", (unsigned long long)m_total_nat_time_out);
+ fprintf (fd," NAT time out : %8llu", (unsigned long long)m_total_nat_time_out);
+ if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
+ fprintf (fd," (%llu in wait for syn+ack)\n", (unsigned long long)m_total_nat_time_out_wait_ack);
+ } else {
+ fprintf (fd, "\n");
+ }
}else{
fprintf (fd,"\n");
}
@@ -2613,28 +2634,33 @@ void CGlobalStats::DumpAllPorts(FILE *fd){
fprintf (fd," Total-Rx : %s ",double_to_human_str(m_rx_bps,"bps",KBYE_1000).c_str());
if ( CGlobalInfo::is_learn_mode() ) {
- fprintf (fd," Nat_no_fid : %8llu \n", (unsigned long long)m_total_nat_no_fid);
+ fprintf (fd," NAT aged flow id: %8llu \n", (unsigned long long)m_total_nat_no_fid);
}else{
fprintf (fd,"\n");
}
fprintf (fd," Total-PPS : %s ",double_to_human_str(m_tx_pps,"pps",KBYE_1000).c_str());
if ( CGlobalInfo::is_learn_mode() ) {
- fprintf (fd," Total_nat_active: %8llu \n", (unsigned long long)m_total_nat_active);
+ fprintf (fd," Total NAT active: %8llu", (unsigned long long)m_total_nat_active);
+ if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
+ fprintf (fd," (%llu waiting for syn)\n", (unsigned long long)m_total_nat_syn_wait);
+ } else {
+ fprintf (fd, "\n");
+ }
}else{
fprintf (fd,"\n");
}
fprintf (fd," Total-CPS : %s ",double_to_human_str(m_tx_cps,"cps",KBYE_1000).c_str());
if ( CGlobalInfo::is_learn_mode() ) {
- fprintf (fd," Total_nat_open : %8llu \n", (unsigned long long)m_total_nat_open);
+ fprintf (fd," Total NAT opened: %8llu \n", (unsigned long long)m_total_nat_open);
}else{
fprintf (fd,"\n");
}
fprintf (fd,"\n");
fprintf (fd," Expected-PPS : %s ",double_to_human_str(m_tx_expected_pps,"pps",KBYE_1000).c_str());
if ( CGlobalInfo::is_learn_verify_mode() ) {
- fprintf (fd," Nat_learn_errors: %8llu \n", (unsigned long long)m_total_nat_learn_error);
+ fprintf (fd," NAT learn errors: %8llu \n", (unsigned long long)m_total_nat_learn_error);
}else{
fprintf (fd,"\n");
}
@@ -3381,7 +3407,9 @@ int CGlobalTRex::ixgbe_prob_init(void){
if ( !CTRexExtendedDriverDb::Ins()->is_driver_exists(dev_info.driver_name) ){
- printf(" ERROR driver name %s is not supported \n",dev_info.driver_name);
+ printf(" Error: driver %s is not supported. Please consult the documentation for a list of supported drivers\n"
+ ,dev_info.driver_name);
+ exit(1);
}
int i;
@@ -3604,6 +3632,7 @@ void CGlobalTRex::get_stats(CGlobalStats & stats){
stats.m_num_of_ports = m_max_ports;
stats.m_cpu_util = m_fl.GetCpuUtil();
+ stats.m_cpu_util_raw = m_fl.GetCpuUtilRaw();
if (get_is_stateless()) {
stats.m_rx_cpu_util = m_rx_sl.get_cpu_util();
}
@@ -3655,8 +3684,10 @@ void CGlobalTRex::get_stats(CGlobalStats & stats){
uint64_t total_nat_time_out =0;
+ uint64_t total_nat_time_out_wait_ack =0;
uint64_t total_nat_no_fid =0;
uint64_t total_nat_active =0;
+ uint64_t total_nat_syn_wait = 0;
uint64_t total_nat_open =0;
uint64_t total_nat_learn_error=0;
@@ -3685,8 +3716,10 @@ void CGlobalTRex::get_stats(CGlobalStats & stats){
total_sockets += lpt->m_smart_gen.MaxSockets();
total_nat_time_out +=lpt->m_stats.m_nat_flow_timeout;
+ total_nat_time_out_wait_ack += lpt->m_stats.m_nat_flow_timeout_wait_ack;
total_nat_no_fid +=lpt->m_stats.m_nat_lookup_no_flow_id ;
total_nat_active +=lpt->m_stats.m_nat_lookup_add_flow_id - lpt->m_stats.m_nat_lookup_remove_flow_id;
+ total_nat_syn_wait += lpt->m_stats.m_nat_lookup_add_flow_id - lpt->m_stats.m_nat_lookup_wait_ack_state;
total_nat_open +=lpt->m_stats.m_nat_lookup_add_flow_id;
total_nat_learn_error +=lpt->m_stats.m_nat_flow_learn_error;
uint8_t port0 = lpt->getDualPortId() *2;
@@ -3708,8 +3741,10 @@ void CGlobalTRex::get_stats(CGlobalStats & stats){
}
stats.m_total_nat_time_out = total_nat_time_out;
+ stats.m_total_nat_time_out_wait_ack = total_nat_time_out_wait_ack;
stats.m_total_nat_no_fid = total_nat_no_fid;
stats.m_total_nat_active = total_nat_active;
+ stats.m_total_nat_syn_wait = total_nat_syn_wait;
stats.m_total_nat_open = total_nat_open;
stats.m_total_nat_learn_error = total_nat_learn_error;
@@ -3835,7 +3870,7 @@ CGlobalTRex::publish_async_data(bool sync_now, bool baseline) {
get_stats(m_stats);
}
- m_stats.dump_json(json, baseline,m_stats_cnt);
+ m_stats.dump_json(json, baseline);
m_zmq_publisher.publish_json(json);
/* generator json , all cores are the same just sample the first one */
@@ -3929,7 +3964,7 @@ CGlobalTRex::handle_slow_path(bool &was_stopped) {
if (m_io_modes.m_g_mode == CTrexGlobalIoMode::gMem) {
if ( m_stats_cnt%4==0) {
- fprintf (stdout," %s \n",CGlobalInfo::dump_pool_as_json().c_str());
+ fprintf (stdout," %s \n",CGlobalInfo::dump_pool_as_json_str().c_str());
}
}
@@ -3967,9 +4002,17 @@ CGlobalTRex::handle_slow_path(bool &was_stopped) {
m_mg.DumpRxCheck(stdout);
break;
}
-
}
-
+ }
+ }
+ if ( m_io_modes.m_g_mode == CTrexGlobalIoMode::gNAT ) {
+ if ( m_io_modes.m_nat_mode == CTrexGlobalIoMode::natENABLE ) {
+ if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
+ fprintf(stdout, "NAT flow table info\n");
+ m_mg.dump_nat_flow_table(stdout);
+ } else {
+ fprintf(stdout, "\nThis is only relevant in --learn-mode %d\n", CParserOption::LEARN_MODE_TCP_ACK);
+ }
}
}
@@ -4221,12 +4264,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();
@@ -4860,6 +4915,18 @@ int main_test(int argc , char * argv[]){
return (0);
}
+void wait_x_sec(int sec) {
+ int i;
+ printf(" wait %d sec ", sec);
+ fflush(stdout);
+ for (i=0; i<sec; i++) {
+ delay(1000);
+ printf(".");
+ fflush(stdout);
+ }
+ printf("\n");
+ fflush(stdout);
+}
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
@@ -4870,32 +4937,28 @@ int CTRexExtendedDriverBase::configure_drop_queue(CPhyEthIF * _if) {
return (rte_eth_dev_rx_queue_stop(port_id, 0));
}
+int CTRexExtendedDriverBase::wait_for_stable_link() {
+ wait_x_sec(CGlobalInfo::m_options.m_wait_before_traffic);
+ return 0;
+}
+
+void CTRexExtendedDriverBase::wait_after_link_up() {
+ wait_x_sec(CGlobalInfo::m_options.m_wait_before_traffic);
+}
+
CFlowStatParser *CTRexExtendedDriverBase::get_flow_stat_parser() {
CFlowStatParser *parser = new CFlowStatParser();
assert (parser);
return parser;
}
-void wait_x_sec(int sec) {
- int i;
- printf(" wait %d sec ", sec);
- fflush(stdout);
- for (i=0; i<sec; i++) {
- delay(1000);
- printf(".");
- fflush(stdout);
- }
- printf("\n");
- fflush(stdout);
-}
-
// in 1G we need to wait if links became ready to soon
void CTRexExtendedDriverBase1G::wait_after_link_up(){
- wait_x_sec(7);
+ wait_x_sec(6 + CGlobalInfo::m_options.m_wait_before_traffic);
}
int CTRexExtendedDriverBase1G::wait_for_stable_link(){
- wait_x_sec(10);
+ wait_x_sec(9 + CGlobalInfo::m_options.m_wait_before_traffic);
return(0);
}
@@ -5277,7 +5340,7 @@ void CTRexExtendedDriverBase10G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFSta
}
int CTRexExtendedDriverBase10G::wait_for_stable_link(){
- delay(2000);
+ wait_x_sec(1 + CGlobalInfo::m_options.m_wait_before_traffic);
return (0);
}
@@ -5531,11 +5594,10 @@ void CTRexExtendedDriverBase40G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFSta
stats->f_ibytes = 0;
- stats->ierrors = stats1.ierrors + stats1.imissed + stats1.ibadcrc +
+ stats->ierrors = stats1.imissed + stats1.ibadcrc +
stats1.ibadlen +
stats1.ierrors +
stats1.oerrors +
- stats1.imcasts +
stats1.rx_nombuf +
stats1.tx_pause_xon +
stats1.rx_pause_xon +
@@ -5550,7 +5612,7 @@ void CTRexExtendedDriverBase40G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFSta
}
int CTRexExtendedDriverBase40G::wait_for_stable_link(){
- delay(2000);
+ wait_x_sec(1 + CGlobalInfo::m_options.m_wait_before_traffic);
return (0);
}
@@ -5605,11 +5667,10 @@ void CTRexExtendedDriverBase1GVm::get_extended_stats(CPhyEthIF * _if,CPhyEthIFSt
stats->f_ibytes = 0;
- stats->ierrors = stats1.ierrors + stats1.imissed + stats1.ibadcrc +
+ stats->ierrors = stats1.imissed + stats1.ibadcrc +
stats1.ibadlen +
stats1.ierrors +
stats1.oerrors +
- stats1.imcasts +
stats1.rx_nombuf +
stats1.tx_pause_xon +
stats1.rx_pause_xon +
@@ -5624,7 +5685,7 @@ void CTRexExtendedDriverBase1GVm::get_extended_stats(CPhyEthIF * _if,CPhyEthIFSt
}
int CTRexExtendedDriverBase1GVm::wait_for_stable_link(){
- delay(10);
+ wait_x_sec(CGlobalInfo::m_options.m_wait_before_traffic);
return (0);
}
@@ -5782,6 +5843,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();
}
@@ -5812,6 +5877,20 @@ int TrexDpdkPlatformApi::get_active_pgids(flow_stat_active_t &result) const {
return g_trex.m_trex_stateless->m_rx_flow_stat.get_active_pgids(result);
}
+int TrexDpdkPlatformApi::get_cpu_util_full(cpu_util_full_t &cpu_util_full) const {
+ cpu_util_full.resize((int)g_trex.m_fl.m_threads_info.size());
+ for (int thread_id=0; thread_id<(int)g_trex.m_fl.m_threads_info.size(); thread_id++) {
+ CFlowGenListPerThread * lp=g_trex.m_fl.m_threads_info[thread_id];
+ lp->m_cpu_cp_u.GetHistory(cpu_util_full[thread_id]);
+ }
+ return 0;
+}
+
+int TrexDpdkPlatformApi::get_mbuf_util(Json::Value &mbuf_pool) const {
+ CGlobalInfo::dump_pool_as_json(mbuf_pool);
+ return 0;
+}
+
CFlowStatParser *TrexDpdkPlatformApi::get_flow_stat_parser() const {
return CTRexExtendedDriverDb::Ins()->get_drv()->get_flow_stat_parser();
}
diff --git a/src/nat_check.cpp b/src/nat_check.cpp
index 7e224430..f3dd93d1 100755
--- a/src/nat_check.cpp
+++ b/src/nat_check.cpp
@@ -41,7 +41,8 @@ void CGenNodeNatInfo::dump(FILE *fd){
int i;
for (i=0; i<m_cnt; i++) {
CNatFlowInfo * lp=&m_data[i];
- fprintf (fd," id:%d , external ip:%08x:%x , ex_port: %04x , fid: %d \n",i,lp->m_external_ip,lp->m_external_ip_server,lp->m_external_port,lp->m_fid);
+ fprintf (fd," id:%d , external ip:%08x , ex_port: %04x , TCP seq:%x fid: %d \n"
+ , i, lp->m_external_ip, lp->m_external_port, lp->m_tcp_seq, lp->m_fid);
}
}
@@ -51,7 +52,6 @@ void CGenNodeNatInfo::init(){
m_cnt=0;
}
-
void CNatStats::reset(){
m_total_rx=0;
m_total_msg=0;
@@ -147,14 +147,17 @@ void CNatRxManager::get_info_from_tcp_ack(uint32_t tcp_ack, uint32_t &fid, uint8
* option - pointer to our proprietary NAT info IP option.
* If it is NULL, the NAT info is in the TCP ACK number
* ipv4 - pointer to ipv4 header to extract info from.
+ * is_first - Is this the first packet of the flow or second. To handle firewalls that do
+ * TCP seq randomization on the server->client side, we also look at the second
+ * packet of the flow (SYN+ACK), and extract its seq num.
*/
-void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4) {
+void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4, bool is_first) {
CNatPerThreadInfo * thread_info;
uint32_t fid=0;
+ uint32_t tcp_seq;
/* Extract info from the packet ! */
uint32_t ext_ip = ipv4->getSourceIp();
- uint32_t ext_ip_server = ipv4->getDestIp();
uint8_t proto = ipv4->getProtocol();
/* must be TCP/UDP this is the only supported proto */
if (!( (proto==6) || (proto==17) )){
@@ -165,21 +168,48 @@ void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4) {
TCPHeader *tcp = (TCPHeader *) (((char *)ipv4)+ ipv4->getHeaderLength());
uint16_t ext_port = tcp->getSourcePort();
+ tcp_seq = tcp->getSeqNumber();
+
if (option) {
thread_info = get_thread_info(option->get_thread_id());
fid = option->get_fid();
} else {
- uint8_t thread_id;
- get_info_from_tcp_ack(tcp->getAckNumber(), fid, thread_id);
- thread_info = get_thread_info(thread_id);
+ uint8_t thread_id;
+
+ if (is_first) {
+ uint32_t tcp_ack = tcp->getAckNumber();
+ get_info_from_tcp_ack(tcp_ack, fid, thread_id);
+ thread_info = get_thread_info(thread_id);
+ if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) {
+ uint32_t dst_ip = ipv4->getDestIp();
+ uint16_t dst_port = tcp->getDestPort();
+ uint64_t map_key = (dst_ip << 16) + dst_port;
+ double time_stamp = now_sec();
+ m_ft.insert(map_key, tcp_ack, time_stamp);
+ m_ft.clear_old(time_stamp - 1);
+ }
+ } else {
+ uint32_t val;
+ // server->client packet. IP/port reversed in regard to first SYN packet
+ uint64_t map_key = (ext_ip << 16) + ext_port;
+ if (m_ft.erase(map_key, val)) {
+ get_info_from_tcp_ack(val, fid, thread_id);
+ thread_info = get_thread_info(thread_id);
+ } else {
+ // flow was not found in the table
+ thread_info = 0;
+ }
+ }
}
+
if (unlikely(!thread_info)) {
return;
}
#ifdef NAT_TRACE_
- printf("rx msg ext ip : %08x:%08x ext port : %04x flow_id : %d \n",ext_ip,ext_ip_server,ext_port, fid);
+ printf("rx msg ext ip: %08x ext port: %04x TCP Seq: %08x flow_id : %d (%s) \n", ext_ip, ext_port, tcp_seq, fid
+ , is_first ? "first":"second");
#endif
CGenNodeNatInfo * node=thread_info->m_cur_nat_msg;
@@ -194,9 +224,13 @@ void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4) {
CNatFlowInfo * msg=node->get_next_msg();
/* fill the message */
- msg->m_external_ip = ext_ip;
- msg->m_external_ip_server = ext_ip_server;
- msg->m_external_port = ext_port;
+ if (is_first) {
+ msg->m_external_ip = ext_ip;
+ msg->m_external_port = ext_port;
+ } else {
+ msg->m_external_port = TCPHeader::TCP_INVALID_PORT;
+ }
+ msg->m_tcp_seq = tcp_seq;
msg->m_fid = fid;
msg->m_pad = 0xee;
@@ -221,7 +255,8 @@ void CNatStats::Dump(FILE *fd){
void CNatRxManager::Dump(FILE *fd){
- m_stats.Dump(stdout);
+ m_stats.Dump(fd);
+ m_ft.dump(fd);
}
void CNatRxManager::DumpShort(FILE *fd){
diff --git a/src/nat_check.h b/src/nat_check.h
index 133501ae..18add5e0 100755
--- a/src/nat_check.h
+++ b/src/nat_check.h
@@ -21,6 +21,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
+#include <map>
#include "msg_manager.h"
#include <common/Network/Packet/TcpHeader.h>
#include <common/Network/Packet/UdpHeader.h>
@@ -28,6 +29,7 @@ limitations under the License.
#include <common/Network/Packet/IPv6Header.h>
#include <common/Network/Packet/EthernetHeader.h>
#include "os_time.h"
+#include "nat_check_flow_table.h"
// 2msec timeout
#define MAX_TIME_MSG_IN_QUEUE_SEC ( 0.002 )
@@ -121,7 +123,7 @@ private:
struct CNatFlowInfo {
uint32_t m_external_ip;
- uint32_t m_external_ip_server;
+ uint32_t m_tcp_seq;
uint32_t m_fid;
uint16_t m_external_port;
uint16_t m_pad;
@@ -210,13 +212,12 @@ public:
void Dump(FILE *fd);
};
-
class CNatRxManager {
public:
bool Create();
void Delete();
- void handle_packet_ipv4(CNatOption * option, IPHeader * ipv4);
+ void handle_packet_ipv4(CNatOption * option, IPHeader * ipv4, bool is_first);
void handle_aging();
void Dump(FILE *fd);
void DumpShort(FILE *fd);
@@ -232,6 +233,7 @@ private:
uint8_t m_max_threads;
CNatPerThreadInfo * m_per_thread;
CNatStats m_stats;
+ CNatCheckFlowTable m_ft;
};
diff --git a/src/nat_check_flow_table.cpp b/src/nat_check_flow_table.cpp
new file mode 100644
index 00000000..18ba8142
--- /dev/null
+++ b/src/nat_check_flow_table.cpp
@@ -0,0 +1,314 @@
+/*
+ Ido Barnea
+ Cisco Systems, Inc.
+*/
+
+/*
+ Copyright (c) 2016-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 <assert.h>
+#include <stdint.h>
+#include <string>
+#include <iostream>
+#include <new>
+#include "nat_check_flow_table.h"
+/*
+ classes in this file:
+ CNatCheckFlowTableList - List implementation
+ CNatCheckFlowTableMap - Map implementation
+ CNatData - element of data that exists in the map and the list.
+ CNatCheckFlowTable - Wrapper class which is the interface to outside.
+ New element is always inserted to the list and map.
+ If element is removed, it is always removed from list and map together.
+ Map is used to lookup elemnt by key.
+ List is used to clean up old elements by timestamp. We guarantee the list is always sorted by timestamp,
+ since we always insert at the end, with increasing timestamp.
+*/
+
+std::ostream& operator<<(std::ostream& os, const CNatData &cn) {
+ os << "(" << &cn << ")" << "data:" << cn.m_data << " time:" << cn.m_timestamp;
+ os << " prev:" << cn.m_prev << " next:" << cn.m_next;
+ return os;
+}
+
+// map implementation
+CNatData *CNatCheckFlowTableMap::erase(uint64_t key) {
+ nat_check_flow_map_iter_no_const_t it = m_map.find(key);
+
+ if (it != m_map.end()) {
+ CNatData *val = it->second;
+ m_map.erase(it);
+ return val;
+ }
+
+ return NULL;
+}
+
+bool CNatCheckFlowTableMap::find(uint64_t key, uint32_t &val) {
+ nat_check_flow_map_iter_t it = m_map.find(key);
+
+ if (it != m_map.end()) {
+ CNatData *data = it->second;
+ val = data->get_data();
+ return true;
+ }
+
+ return false;
+}
+
+// for testing
+bool CNatCheckFlowTableMap::verify(uint32_t *arr, int size) {
+ uint32_t val = -1;
+ int real_size = 0;
+
+ for (int i = 0; i < size; i++) {
+ if (arr[i] == -1)
+ continue;
+ real_size++;
+ find(i, val);
+ if (val != arr[i])
+ return false;
+ }
+
+ if (m_map.size() != real_size) {
+ std::cout << "Error: Wrong map size " << m_map.size() << ". Should be " << real_size << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+CNatData * CNatCheckFlowTableMap::insert(uint64_t key, uint32_t val, double time) {
+ CNatData *elem = new CNatData;
+ assert(elem);
+ elem->set_data(val);
+ elem->set_key(key);
+ elem->set_timestamp(time);
+ std::pair<uint64_t, CNatData *> pair = std::pair<uint64_t, CNatData *>(key, elem);
+
+ if (m_map.insert(pair).second == false) {
+ delete elem;
+ return NULL;
+ } else {
+ return elem;
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, const CNatCheckFlowTableMap& cf) {
+ nat_check_flow_map_iter_t it;
+
+ os << "NAT check flow table map:\n";
+ for (it = cf.m_map.begin(); it != cf.m_map.end(); it++) {
+ CNatData *data = it->second;
+ uint32_t key = it->first;
+ os << " " << key << ":" << *data << std::endl;
+ }
+ return os;
+}
+
+void CNatCheckFlowTableList::dump_short(FILE *fd) {
+ fprintf(fd, "list:\n");
+ // this is not fully safe, since we call it from CP core, and rx core changes the list.
+ // It is usefull as a debug function
+ if (m_head) {
+ fprintf(fd, " head: time:%f key:%x data:%x\n", m_head->get_timestamp(), m_head->get_key(), m_head->get_data());
+ fprintf(fd, " tail: time:%f key:%x data:%x\n", m_tail->get_timestamp(), m_tail->get_key(), m_tail->get_data());
+ }
+}
+
+// list implementation
+// The list is always sorted by timestamp, since we always insert at the end, with increasing timestamp
+void CNatCheckFlowTableList::erase(CNatData *data) {
+ if (m_head == data) {
+ m_head = data->m_next;
+ }
+ if (m_tail == data) {
+ m_tail = data->m_prev;
+ }
+ if (data->m_prev) {
+ data->m_prev->m_next = data->m_next;
+ }
+ if (data->m_next) {
+ data->m_next->m_prev = data->m_prev;
+ }
+}
+
+// insert as last element in list
+void CNatCheckFlowTableList::insert(CNatData *data) {
+ data->m_prev = m_tail;
+ data->m_next = NULL;
+
+ if (m_tail != NULL) {
+ m_tail->m_next = data;
+ } else {
+ // if m_tail is NULL m_head is also NULL
+ m_head = data;
+ }
+ m_tail = data;
+}
+
+bool CNatCheckFlowTableList::verify(uint32_t *arr, int size) {
+ int index = -1;
+ CNatData *elem = m_head;
+ int count = 0;
+
+ while (index < size - 1) {
+ index++;
+ if (arr[index] == -1) {
+ continue;
+ }
+ count++;
+
+ if (elem->get_data() != arr[index]) {
+ return false;
+ }
+
+ if (elem == NULL) {
+ std::cout << "Too few items in list. Only " << count << std::endl;
+ return false;
+ }
+ elem = elem->m_next;
+ }
+
+ // We expect number of items in list to be like num of val!=-1 in arr
+ if (elem != NULL) {
+ std::cout << "Too many items in list" << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+std::ostream& operator<<(std::ostream& os, const CNatCheckFlowTableList& cf) {
+ CNatData *it = cf.m_head;
+
+ os << "NAT check flow table list:\n";
+ os << " head:" << cf.m_head << " tail:" << cf.m_tail << std::endl;
+ while (it != NULL) {
+ os << " " << *it << std::endl;
+ it = it->m_next;
+ }
+
+ return os;
+}
+
+// flow table
+std::ostream& operator<<(std::ostream& os, const CNatCheckFlowTable& cf) {
+ os << "========= Flow table start =========" << std::endl;
+ os << cf.m_map;
+ os << cf.m_list;
+ os << "========= Flow table end =========" << std::endl;
+
+ return os;
+}
+
+void CNatCheckFlowTable::clear_old(double time) {
+ CNatData *data = m_list.get_head();
+ uint32_t val;
+
+ while (data) {
+ if (data->get_timestamp() < time) {
+ erase(data->get_key(), val);
+ data = m_list.get_head();
+ } else {
+ break;
+ }
+ }
+}
+
+void CNatCheckFlowTable::dump(FILE *fd) {
+ fprintf(fd, "map size:%lu\n", m_map.size());
+ m_list.dump_short(fd);
+}
+
+bool CNatCheckFlowTable::erase(uint64_t key, uint32_t &val) {
+ CNatData *data = m_map.erase(key);
+
+ if (!data)
+ return false;
+
+ val = data->get_data();
+ m_list.erase(data);
+ delete data;
+
+ return true;
+}
+
+bool CNatCheckFlowTable::insert(uint64_t key, uint32_t val, double time) {
+ CNatData *res;
+
+ res = m_map.insert(key, val, time);
+ if (!res) {
+ return false;
+ } else {
+ m_list.insert(res);
+ }
+ return true;
+}
+
+CNatCheckFlowTable::~CNatCheckFlowTable() {
+ clear_old(UINT64_MAX);
+}
+
+bool CNatCheckFlowTable::test() {
+ uint32_t size = 100;
+ uint32_t arr[size];
+ int i;
+ uint32_t val;
+
+ for (i = 0; i < size; i++) {
+ arr[i] = i+200;
+ }
+
+ // insert some elements
+ for (i = 0; i < size; i++) {
+ val = arr[i];
+ assert(insert(i, val, i) == true);
+ }
+
+ // insert same elements. should fail
+ for (i = 0; i < size; i++) {
+ val = arr[i];
+ assert(insert(i, val, val) == false);
+ }
+
+ // remove element we did not insert
+ assert(erase(size, val) == false);
+
+ assert (m_map.verify(arr, size) == true);
+ assert (m_list.verify(arr, size) == true);
+
+ // remove even keys
+ for (i = 0; i < size; i += 2) {
+ assert(erase(i, val) == true);
+ assert (val == arr[i]);
+ arr[i] = -1;
+ }
+
+ assert (m_map.verify(arr, size) == true);
+ assert (m_list.verify(arr, size) == true);
+
+ // clear half of the old values (We removed the even already, so 1/4 should be left)
+ clear_old(size/2);
+ for (i = 0; i < size/2; i++) {
+ arr [i] = -1;
+ }
+
+ assert (m_map.verify(arr, size) == true);
+ assert (m_list.verify(arr, size) == true);
+
+ return true;
+}
diff --git a/src/nat_check_flow_table.h b/src/nat_check_flow_table.h
new file mode 100644
index 00000000..dddd45b6
--- /dev/null
+++ b/src/nat_check_flow_table.h
@@ -0,0 +1,111 @@
+/*
+ Ido Barnea
+ Cisco Systems, Inc.
+*/
+
+/*
+ Copyright (c) 2016-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 NAT_CHECK_FLOW_TABLE_H
+#define NAT_CHECK_FLOW_TABLE_H
+#include <map>
+
+class CNatData;
+class CNatCheckFlowTableList;
+
+typedef std::map<uint64_t, CNatData *, std::less<uint64_t> > nat_check_flow_map_t;
+typedef nat_check_flow_map_t::const_iterator nat_check_flow_map_iter_t;
+typedef nat_check_flow_map_t::iterator nat_check_flow_map_iter_no_const_t;
+
+// One element of list and map
+class CNatData {
+ friend class CNatCheckFlowTableList; // access m_next and m_prev
+ friend std::ostream& operator<<(std::ostream& os, const CNatCheckFlowTableList& cf);
+ friend std::ostream& operator<<(std::ostream& os, const CNatData &cn);
+
+ public:
+ uint32_t get_data() {return m_data;}
+ void set_data(uint32_t val) {m_data = val;}
+ uint32_t get_key() {return m_key;}
+ void set_key(uint32_t val) {m_key = val;}
+ double get_timestamp() {return m_timestamp;}
+ void set_timestamp(double val) {m_timestamp = val;}
+ CNatData() {
+ m_next = NULL;
+ m_prev = NULL;
+ }
+
+ private:
+ double m_timestamp;
+ uint64_t m_key;
+ CNatData *m_prev;
+ CNatData *m_next;
+ uint32_t m_data;
+};
+
+class CNatCheckFlowTableMap {
+ friend class CNatCheckFlowTable;
+ friend std::ostream& operator<<(std::ostream& os, const CNatCheckFlowTableMap& cf);
+
+ private:
+ void clear(void) {m_map.clear();}
+ CNatData *erase(uint64_t key);
+ bool find(uint64_t key, uint32_t &val);
+ CNatData *insert(uint64_t key, uint32_t val, double time);
+ bool verify(uint32_t *arr, int size);
+ uint64_t size(void) {return m_map.size();}
+
+ private:
+ nat_check_flow_map_t m_map;
+};
+
+class CNatCheckFlowTableList {
+ friend class CNatCheckFlowTable;
+ friend std::ostream& operator<<(std::ostream& os, const CNatCheckFlowTableList& cf);
+
+ private:
+ void dump_short(FILE *fd);
+ void erase(CNatData *data);
+ CNatData *get_head() {return m_head;}
+ void insert(CNatData *data);
+ bool verify(uint32_t *arr, int size);
+
+ CNatCheckFlowTableList() {
+ m_head = NULL;
+ m_tail = NULL;
+ }
+
+ private:
+ CNatData *m_head;
+ CNatData *m_tail;
+};
+
+class CNatCheckFlowTable {
+ friend std::ostream& operator<<(std::ostream& os, const CNatCheckFlowTable& cf);
+
+ public:
+ ~CNatCheckFlowTable();
+ void clear_old(double time);
+ void dump(FILE *fd);
+ bool erase(uint64_t key, uint32_t &val);
+ bool insert(uint64_t key, uint32_t val, double time);
+ bool test();
+
+ private:
+ CNatCheckFlowTableMap m_map;
+ CNatCheckFlowTableList m_list;
+};
+
+#endif
diff --git a/src/rpc-server/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/commands/trex_rpc_cmd_general.cpp
index 68ea2587..27010e0e 100644
--- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp
+++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp
@@ -157,6 +157,29 @@ TrexRpcCmdGetActivePGIds::_run(const Json::Value &params, Json::Value &result) {
return (TREX_RPC_CMD_OK);
}
+// get utilization of CPU per thread with up to 20 latest values + mbufs per socket
+trex_rpc_cmd_rc_e
+TrexRpcCmdGetUtilization::_run(const Json::Value &params, Json::Value &result) {
+ cpu_util_full_t cpu_util_full;
+
+ Json::Value &section = result["result"];
+
+ if (get_stateless_obj()->get_platform_api()->get_mbuf_util(section) != 0) {
+ return TREX_RPC_CMD_INTERNAL_ERR;
+ }
+
+ if (get_stateless_obj()->get_platform_api()->get_cpu_util_full(cpu_util_full) != 0) {
+ return TREX_RPC_CMD_INTERNAL_ERR;
+ }
+
+ for (int thread_id = 0; thread_id < cpu_util_full.size(); thread_id++) {
+ for (int history_id = 0; history_id < cpu_util_full[thread_id].size(); history_id++) {
+ section["cpu"][thread_id].append(cpu_util_full[thread_id][history_id]);
+ }
+ }
+ return (TREX_RPC_CMD_OK);
+}
+
/**
* get the CPU model
*
diff --git a/src/rpc-server/commands/trex_rpc_cmds.h b/src/rpc-server/commands/trex_rpc_cmds.h
index affa65c1..2776727d 100644
--- a/src/rpc-server/commands/trex_rpc_cmds.h
+++ b/src/rpc-server/commands/trex_rpc_cmds.h
@@ -67,6 +67,7 @@ TREX_RPC_CMD_DEFINE(TrexRpcPublishNow, "publish_now", 2, false,
TREX_RPC_CMD_DEFINE(TrexRpcCmdGetCmds, "get_supported_cmds", 0, false, APIClass::API_CLASS_TYPE_CORE);
TREX_RPC_CMD_DEFINE(TrexRpcCmdGetVersion, "get_version", 0, false, APIClass::API_CLASS_TYPE_CORE);
TREX_RPC_CMD_DEFINE(TrexRpcCmdGetActivePGIds, "get_active_pgids", 0, false, APIClass::API_CLASS_TYPE_CORE);
+TREX_RPC_CMD_DEFINE(TrexRpcCmdGetUtilization, "get_utilization", 0, false, APIClass::API_CLASS_TYPE_CORE);
TREX_RPC_CMD_DEFINE_EXTENDED(TrexRpcCmdGetSysInfo, "get_system_info", 0, false, APIClass::API_CLASS_TYPE_CORE,
diff --git a/src/rpc-server/trex_rpc_cmds_table.cpp b/src/rpc-server/trex_rpc_cmds_table.cpp
index 7104792e..6144d265 100644
--- a/src/rpc-server/trex_rpc_cmds_table.cpp
+++ b/src/rpc-server/trex_rpc_cmds_table.cpp
@@ -39,6 +39,7 @@ TrexRpcCommandsTable::TrexRpcCommandsTable() {
register_command(new TrexRpcCmdGetCmds());
register_command(new TrexRpcCmdGetVersion());
register_command(new TrexRpcCmdGetActivePGIds());
+ register_command(new TrexRpcCmdGetUtilization());
register_command(new TrexRpcCmdGetSysInfo());
register_command(new TrexRpcCmdGetOwner());
register_command(new TrexRpcCmdAcquire());
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_stateless.cpp b/src/stateless/cp/trex_stateless.cpp
index 698ede90..8633897e 100644
--- a/src/stateless/cp/trex_stateless.cpp
+++ b/src/stateless/cp/trex_stateless.cpp
@@ -54,7 +54,11 @@ TrexStateless::TrexStateless(const TrexStatelessCfg &cfg) {
m_publisher = cfg.m_publisher;
/* API core version */
- m_api_classes[APIClass::API_CLASS_TYPE_CORE].init(APIClass::API_CLASS_TYPE_CORE, 1, 2);
+ const int API_VER_MAJOR = 1;
+ const int API_VER_MINOR = 3;
+ m_api_classes[APIClass::API_CLASS_TYPE_CORE].init(APIClass::API_CLASS_TYPE_CORE,
+ API_VER_MAJOR,
+ API_VER_MINOR);
}
/**
diff --git a/src/stateless/rx/trex_stateless_rx_core.cpp b/src/stateless/rx/trex_stateless_rx_core.cpp
index 0bd601b6..853fc868 100644
--- a/src/stateless/rx/trex_stateless_rx_core.cpp
+++ b/src/stateless/rx/trex_stateless_rx_core.cpp
@@ -206,6 +206,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t *
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];
@@ -216,6 +217,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t *
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
@@ -224,6 +226,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t *
} else {
// garbage packet
good_packet = false;
+ m_err_cntrs.m_bad_header++;
}
}
}
@@ -278,9 +281,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t *
}
} else {
hw_id = get_hw_id(ip_id);
- if (hw_id >= MAX_FLOW_STATS) {
- // increase some error counter
- } else {
+ 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
}
@@ -444,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 209dc29f..fc66704e 100644
--- a/src/stateless/rx/trex_stateless_rx_core.h
+++ b/src/stateless/rx/trex_stateless_rx_core.h
@@ -95,6 +95,25 @@ class CRFC2544Info {
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 {
enum state_e {
STATE_IDLE,
@@ -109,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);}
@@ -146,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/trex_defs.h b/src/trex_defs.h
index 665c1edc..9abb38f5 100644
--- a/src/trex_defs.h
+++ b/src/trex_defs.h
@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include <set>
+#include <queue>
+#include <vector>
#ifndef __TREX_DEFS_H__
#define __TREX_DEFS_H__
@@ -33,8 +35,13 @@ limitations under the License.
#define UINT16_MAX 0xFFFF
#endif
+#ifndef UINT64_MAX
+ #define UINT64_MAX 0xFFFFFFFFFFFFFFFF
+#endif
typedef std::set<uint32_t> flow_stat_active_t;
typedef std::set<uint32_t>::iterator flow_stat_active_it_t;
+typedef std::vector<std::vector<uint8_t>> cpu_util_full_t;
+typedef std::vector<uint8_t> cpu_vct_t;
#endif
diff --git a/src/trex_watchdog.cpp b/src/trex_watchdog.cpp
index d099933b..88711b7a 100644
--- a/src/trex_watchdog.cpp
+++ b/src/trex_watchdog.cpp
@@ -92,7 +92,7 @@ std::string Backtrace(int skip = 1)
/* add the addr2line info */
std::stringstream addr2line;
- addr2line << "/usr/bin/addr2line -e " << get_exe_name() << " ";
+ addr2line << "/usr/bin/addr2line -s -e " << get_exe_name() << " ";
for (int i = skip; i < nFrames; i++) {
addr2line << callstack[i] << " ";
}
diff --git a/src/trex_watchdog.h b/src/trex_watchdog.h
index 1c948d56..af9a3993 100644
--- a/src/trex_watchdog.h
+++ b/src/trex_watchdog.h
@@ -117,15 +117,12 @@ private:
/* write fields are first */
volatile bool m_active;
volatile bool m_tickled;
- dsec_t m_ts;
-
int m_handle;
+ dsec_t m_ts;
double m_timeout_sec;
pthread_t m_tid;
std::string m_name;
- /* for for a full cacheline */
- uint8_t pad[15];
} __rte_cache_aligned;
@@ -203,6 +200,5 @@ private:
static bool g_signal_init;
};
-static_assert(sizeof(TrexMonitor) == RTE_CACHE_LINE_SIZE, "sizeof(TrexMonitor) != RTE_CACHE_LINE_SIZE" );
#endif /* __TREX_WATCHDOG_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_cpuu.cpp b/src/utl_cpuu.cpp
index 4d75cf6f..7786356e 100755
--- a/src/utl_cpuu.cpp
+++ b/src/utl_cpuu.cpp
@@ -25,7 +25,9 @@ limitations under the License.
void CCpuUtlCp::Create(CCpuUtlDp * cdp){
m_dpcpu=cdp;
- m_cpu_util=0.0;
+ memset(m_cpu_util, 0, sizeof(m_cpu_util));
+ m_history_latest_index = 0;
+ m_cpu_util_lpf=0.0;
m_ticks=0;
m_work=0;
}
@@ -41,17 +43,34 @@ void CCpuUtlCp::Update(){
m_work++;
}
if (m_ticks==100) {
- double window_cpu_u = ((double)m_work/(double)m_ticks);
/* LPF*/
- m_cpu_util = (m_cpu_util*0.75)+(window_cpu_u*0.25);
+ m_cpu_util_lpf = (m_cpu_util_lpf*0.75)+((double)m_work*0.25);
+ AppendHistory(m_work);
m_ticks=0;
m_work=0;
-
}
}
-/* return cpu % */
+/* return cpu % Smoothed */
double CCpuUtlCp::GetVal(){
- return (m_cpu_util*100);
+ return (m_cpu_util_lpf);
+}
+
+/* return cpu % Raw */
+uint8_t CCpuUtlCp::GetValRaw(){
+ return (m_cpu_util[m_history_latest_index]);
}
+/* get cpu % utilization history */
+void CCpuUtlCp::GetHistory(cpu_vct_t &cpu_vct){
+ cpu_vct.clear();
+ for (int i = m_history_latest_index + m_history_size; i > m_history_latest_index; i--) {
+ cpu_vct.push_back(m_cpu_util[i % m_history_size]);
+ }
+}
+
+/* save last CPU % util in history */
+void CCpuUtlCp::AppendHistory(uint8_t val){
+ m_history_latest_index = (m_history_latest_index + 1) % m_history_size;
+ m_cpu_util[m_history_latest_index] = val;
+}
diff --git a/src/utl_cpuu.h b/src/utl_cpuu.h
index e5305783..109fff4f 100755
--- a/src/utl_cpuu.h
+++ b/src/utl_cpuu.h
@@ -22,6 +22,8 @@ limitations under the License.
*/
#include <stdint.h>
+#include <cstring>
+#include "trex_defs.h"
#include "os_time.h"
#include "mbuf.h"
@@ -56,13 +58,18 @@ public:
void Update();
/* return cpu % */
double GetVal();
-
+ uint8_t GetValRaw();
+ void GetHistory(cpu_vct_t &cpu_vct);
private:
- CCpuUtlDp * m_dpcpu;
- uint16_t m_ticks;
- uint16_t m_work;
+ void AppendHistory(uint8_t);
+ CCpuUtlDp * m_dpcpu;
+ uint8_t m_ticks;
+ uint8_t m_work;
- double m_cpu_util;
+ static const int m_history_size=20;
+ uint8_t m_cpu_util[m_history_size]; // history as cyclic array
+ uint8_t m_history_latest_index;
+ double m_cpu_util_lpf;
};
#endif
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