diff options
30 files changed, 2157 insertions, 752 deletions
diff --git a/doc/AnalyticsWebReport.py b/doc/AnalyticsWebReport.py index 9bf186bd..182d8367 100755 --- a/doc/AnalyticsWebReport.py +++ b/doc/AnalyticsWebReport.py @@ -5,14 +5,20 @@ import TRexDataAnalysis as tr import time
-def main():
- analytics = ac.initialize_analyticsreporting()
- # print 'retrieving data from Google Analytics'
- current_date = time.strftime("%Y-%m-%d")
- response = ac.get_report(analytics, '2016-11-06', current_date)
- ga_all_data_dict, setups = ac.export_to_tuples(response)
- tr.create_all_data(ga_all_data_dict, setups, '2016-11-06', current_date, save_path=os.getcwd() + '/images/',
- add_stats='yes')
+def main(verbose = False):
+ if verbose:
+ print('Retrieving data from Google Analytics')
+ analytics = ac.initialize_analyticsreporting()
+ current_date = time.strftime("%Y-%m-%d")
+ response = ac.get_report(analytics, '2016-11-06', current_date)
+ ga_all_data_dict, setups = ac.export_to_tuples(response)
+ dest_path = os.path.join(os.getcwd(), 'build', 'images')
+ if verbose:
+ print('Saving data to %s' % dest_path)
+ tr.create_all_data(ga_all_data_dict, setups, '2016-11-06', current_date, save_path = dest_path,
+ add_stats='yes')
+ if verbose:
+ print('Done without errors.')
if __name__ == "__main__":
- main()
+ main()
diff --git a/doc/TRexDataAnalysis.py b/doc/TRexDataAnalysis.py index 3561b0f1..3155f16c 100755 --- a/doc/TRexDataAnalysis.py +++ b/doc/TRexDataAnalysis.py @@ -6,131 +6,90 @@ matplotlib.use('Agg') from matplotlib import pyplot as plt
import os
-PATH_FOR_GRAPHS = 'Z:/trex/trex-doc/images/'
-
-
-def convert_dict_to_dframe(data, categories, index=''):
- data_input = {}
- for category in categories:
- data_input[category] = data[category]
- if index:
- df = pd.DataFrame(data_input, index=data[index])
- else:
- df = pd.DataFrame(data_input)
- return df
-
-
-def plot_bar_by_category(data_frame, category, index='', graph_name='graph.png', show='', gtitle='', save_path=''):
- if index:
- data_frame = data_frame.sort_index(by=index)
- print data_frame[index]
- else:
- print data_frame
- data_frame = pd.DataFrame(data_frame[category], columns=category).astype(float)
- data_frame.plot(kind='bar')
- plt.xticks(rotation='horizontal')
- plt.title(gtitle)
- if save_path:
- plt.savefig(save_path + graph_name)
- if show:
- plt.show()
-
-
-def generate_csv(data_frame, file_name, save_path=(os.getcwd() + "/")):
- f = open(save_path + file_name, 'w')
- data_frame.to_csv(f)
- f.close()
-
-
-# category is an array of category names that will appear as metrics
-def plot_bar_by_test_name(data_frame, test_name, category, graph_name='graph.png', show='', gtitle='', save_path=''):
- data_frame = data_frame[data_frame['Test_name'] == test_name]
- plot_bar_by_category(data_frame, category, 'Test_name', graph_name, show, gtitle=test_name, save_path=save_path)
-
def generate_dframe_for_test(test_name, test_data):
- test_results = []
- test_mins = set()
- test_maxs = set()
- for query in test_data:
- test_results.append(float(query[3]))
- test_mins.add(float(query[4]))
- test_maxs.add(float(query[5]))
- df = pd.DataFrame({test_name: test_results})
- stats = tuple([float(df.mean()), min(test_mins), max(test_maxs)]) # stats = (avg_mpps,min,max)
- return df, stats
+ test_results = []
+ test_mins = set()
+ test_maxs = set()
+ for query in test_data:
+ test_results.append(float(query[3]))
+ test_mins.add(float(query[4]))
+ test_maxs.add(float(query[5]))
+ df = pd.DataFrame({test_name: test_results})
+ stats = tuple([float(df.mean()), min(test_mins), max(test_maxs)]) # stats = (avg_mpps,min,max)
+ return df, stats
def generate_dframe_arr_and_stats_of_tests_per_setup(date, setup_name, setup_dict):
- dframe_arr_trend = []
- stats_arr = []
- dframe_arr_latest = []
- test_names = setup_dict.keys()
- for test in test_names:
- df, stats = generate_dframe_for_test(test, setup_dict[test])
- dframe_arr_trend.append(df)
- stats_arr.append(stats)
- df_latest = float(setup_dict[test][-1][3])
- dframe_arr_latest.append(df_latest)
- dframe_arr_latest = pd.DataFrame({'Date': [date] * len(dframe_arr_latest),
- 'Setup': [setup_name],
- 'Test Name': test_names,
- 'MPPS': dframe_arr_latest},
- index=range(1, len(dframe_arr_latest) + 1))
- stats_df = pd.DataFrame(stats_arr, index=setup_dict.keys(), columns=['Avg MPPS', 'Golden Min', 'Golden Max'])
- stats_df.index.name = 'Test Name'
- return dframe_arr_trend, stats_df, dframe_arr_latest
+ dframe_arr_trend = []
+ stats_arr = []
+ dframe_arr_latest = []
+ test_names = setup_dict.keys()
+ for test in test_names:
+ df, stats = generate_dframe_for_test(test, setup_dict[test])
+ dframe_arr_trend.append(df)
+ stats_arr.append(stats)
+ df_latest = float(setup_dict[test][-1][3])
+ dframe_arr_latest.append(df_latest)
+ dframe_arr_latest = pd.DataFrame({'Date': [date] * len(dframe_arr_latest),
+ 'Setup': [setup_name],
+ 'Test Name': test_names,
+ 'MPPS': dframe_arr_latest},
+ index=range(1, len(dframe_arr_latest) + 1))
+ stats_df = pd.DataFrame(stats_arr, index=setup_dict.keys(), columns=['Avg MPPS', 'Golden Min', 'Golden Max'])
+ stats_df.index.name = 'Test Name'
+ return dframe_arr_trend, stats_df, dframe_arr_latest
def create_plot_for_dframe_arr(dframe_arr, setup_name, start_date, end_date, show='no', save_path='',
- file_name='trend_graph'):
- dframe_all = pd.concat(dframe_arr, axis=1)
- dframe_all = dframe_all.astype(float)
- dframe_all.plot()
- plt.legend(fontsize='small', loc='best')
- plt.ylabel('MPPS')
- plt.title('Setup: ' + setup_name)
- plt.tick_params(
- axis='x',
- which='both',
- bottom='off',
- top='off',
- labelbottom='off')
- plt.xlabel('Time Period: ' + start_date + ' - ' + end_date)
- if save_path:
- plt.savefig(save_path + setup_name + file_name + '.png')
- if show == 'yes':
- plt.show()
+ file_name='trend_graph'):
+ dframe_all = pd.concat(dframe_arr, axis=1)
+ dframe_all = dframe_all.astype(float)
+ dframe_all.plot()
+ plt.legend(fontsize='small', loc='best')
+ plt.ylabel('MPPS')
+ plt.title('Setup: ' + setup_name)
+ plt.tick_params(
+ axis='x',
+ which='both',
+ bottom='off',
+ top='off',
+ labelbottom='off')
+ plt.xlabel('Time Period: ' + start_date + ' - ' + end_date)
+ if save_path:
+ plt.savefig(os.path.join(save_path, setup_name + file_name + '.png'))
+ if show == 'yes':
+ plt.show()
def create_bar_plot_for_latest_runs_per_setup(dframe_all_tests_latest, setup_name, show='no', save_path=''):
- plt.figure()
- dframe_all_tests_latest['MPPS'].plot(kind='bar', legend=False)
- dframe_all_tests_latest = dframe_all_tests_latest[['Test Name', 'Setup', 'Date', 'MPPS']]
- plt.xticks(rotation='horizontal')
- plt.xlabel('Index of Tests')
- plt.ylabel('MPPS')
- plt.title("Test Runs for Setup: " + setup_name)
- if save_path:
- plt.savefig(save_path + setup_name + '_latest_test_runs.png')
- dframe_all_tests_latest = dframe_all_tests_latest.round(2)
- dframe_all_tests_latest.to_csv(save_path + setup_name + '_latest_test_runs_stats.csv')
- if show == 'yes':
- plt.show()
+ plt.figure()
+ dframe_all_tests_latest['MPPS'].plot(kind='bar', legend=False)
+ dframe_all_tests_latest = dframe_all_tests_latest[['Test Name', 'Setup', 'Date', 'MPPS']]
+ plt.xticks(rotation='horizontal')
+ plt.xlabel('Index of Tests')
+ plt.ylabel('MPPS')
+ plt.title("Test Runs for Setup: " + setup_name)
+ if save_path:
+ plt.savefig(os.path.join(save_path, setup_name + '_latest_test_runs.png'))
+ dframe_all_tests_latest = dframe_all_tests_latest.round(2)
+ dframe_all_tests_latest.to_csv(os.path.join(save_path, setup_name + '_latest_test_runs_stats.csv'))
+ if show == 'yes':
+ plt.show()
def create_all_data_per_setup(setup_dict, setup_name, start_date, end_date, show='no', save_path='', add_stats=''):
- dframe_arr, stats_arr, dframe_latest_arr = generate_dframe_arr_and_stats_of_tests_per_setup(end_date, setup_name,
- setup_dict)
- create_bar_plot_for_latest_runs_per_setup(dframe_latest_arr, setup_name, show=show, save_path=save_path)
- create_plot_for_dframe_arr(dframe_arr, setup_name, start_date, end_date, show, save_path)
- if add_stats:
- stats_arr = stats_arr.round(2)
- stats_arr.to_csv(save_path + setup_name + '_trend_stats.csv')
- plt.close('all')
+ dframe_arr, stats_arr, dframe_latest_arr = generate_dframe_arr_and_stats_of_tests_per_setup(end_date, setup_name,
+ setup_dict)
+ create_bar_plot_for_latest_runs_per_setup(dframe_latest_arr, setup_name, show=show, save_path=save_path)
+ create_plot_for_dframe_arr(dframe_arr, setup_name, start_date, end_date, show, save_path)
+ if add_stats:
+ stats_arr = stats_arr.round(2)
+ stats_arr.to_csv(os.path.join(save_path, setup_name + '_trend_stats.csv'))
+ plt.close('all')
def create_all_data(ga_data, setup_names, start_date, end_date, save_path='', add_stats=''):
- for setup_name in setup_names:
- create_all_data_per_setup(ga_data[setup_name], setup_name, start_date, end_date, show='no', save_path=save_path,
- add_stats=add_stats)
+ for setup_name in setup_names:
+ create_all_data_per_setup(ga_data[setup_name], setup_name, start_date, end_date, show='no', save_path=save_path,
+ add_stats=add_stats)
diff --git a/doc/images/topology.png b/doc/images/topology.png Binary files differnew file mode 100644 index 00000000..4e2ee65b --- /dev/null +++ b/doc/images/topology.png diff --git a/doc/trex_analytics.asciidoc b/doc/trex_analytics.asciidoc index 84946dfe..bfe0d4fc 100755 --- a/doc/trex_analytics.asciidoc +++ b/doc/trex_analytics.asciidoc @@ -22,6 +22,54 @@ endif::backend-xhtml11[] = Analytics +== Setup: TRex07 +.Setup Details +[options='header',halign='center'] +|==================================================================================================================== +|Name |OS |NICs |Routers +| trex07 | Fedora 18| 2x10G (X710) (8 interfaces), 1x10G (2 interfaces), 1x1G (4 interfaces) | Loopback on X710 + ASA 5520 + ASA 5512 + ASA 5585-ssp10 +|==================================================================================================================== + +image:images/trex07_latest_test_runs.png[title="trex07 test runs",align="left",width={p_width}, link="images/trex07_latest_test_runs.png"] + +[format="csv", options="header",halign='center'] +|=== +include::build/images/trex07_latest_test_runs_stats.csv[] +|=== + +=== Trend: Analysis Over Time + +image:images/trex07trend_graph.png[title="trex07trend_graph",align="left",width={p_width}, link="images/trex07trend_graph.png"] + +[format="csv", options="header",halign='center'] +|=== +include::build/images/trex07_trend_stats.csv[] +|=== + +== Setup: TRex08 +.Setup Details +[options='header',halign='center'] +|==================================================================================================================== +|Name |OS |NICs |Routers +| trex08 | Fedora 18| 2x10G (X710) (8 interfaces), 1x10G (2 interfaces), 1x1G (4 interfaces) | Loopback on X710 + ASA 5520 + ASA 5512 + ASA 5585-ssp10 +|==================================================================================================================== + +image:images/trex08_latest_test_runs.png[title="trex08 test runs",align="left",width={p_width}, link="images/trex08_latest_test_runs.png"] + +[format="csv", options="header",halign='center'] +|=== +include::build/images/trex08_latest_test_runs_stats.csv[] +|=== + +=== Trend: Analysis Over Time + +image:images/trex08trend_graph.png[title="trex08trend_graph",align="left",width={p_width}, link="images/trex08trend_graph.png"] + +[format="csv", options="header",halign='center'] +|=== +include::build/images/trex08_trend_stats.csv[] +|=== + == Setup: TRex09 .Setup Details @@ -35,7 +83,7 @@ image:images/trex09_latest_test_runs.png[title="trex09 test runs",align="left",w [format="csv", options="header",halign='center'] |=== -include::images/trex09_latest_test_runs_stats.csv[] +include::build/images/trex09_latest_test_runs_stats.csv[] |=== === Trend: Analysis Over Time @@ -44,7 +92,31 @@ image:images/trex09trend_graph.png[title="trex09trend_graph",align="left",width= [format="csv", options="header",halign='center'] |=== -include::images/trex09_trend_stats.csv[] +include::build/images/trex09_trend_stats.csv[] +|=== + +== Setup: TRex11 +.Setup Details +[options='header',halign='center'] +|==================================================================================================================== +|Name |OS |NICs |Routers +| trex11 | Fedora 18| 2x10G (X710) (8 interfaces), 1x10G (2 interfaces), 1x1G (4 interfaces) | Loopback on X710 + ASA 5520 + ASA 5512 + ASA 5585-ssp10 +|==================================================================================================================== + +image:images/trex11_latest_test_runs.png[title="trex11 test runs",align="left",width={p_width}, link="images/trex11_latest_test_runs.png"] + +[format="csv", options="header",halign='center'] +|=== +include::build/images/trex11_latest_test_runs_stats.csv[] +|=== + +=== Trend: Analysis Over Time + +image:images/trex11trend_graph.png[title="trex11trend_graph",align="left",width={p_width}, link="images/trex11trend_graph.png"] + +[format="csv", options="header",halign='center'] +|=== +include::build/images/trex11_trend_stats.csv[] |=== == Setup: Kiwi02 @@ -58,7 +130,7 @@ image:images/kiwi02_latest_test_runs.png[title="trex09 test runs",align="left",w [format="csv", options="header",halign='center'] |=== -include::images/kiwi02_latest_test_runs_stats.csv[] +include::build/images/kiwi02_latest_test_runs_stats.csv[] |=== === Trend: Analysis Over Time @@ -67,7 +139,7 @@ image:images/kiwi02trend_graph.png[title="kiwi02trend_graph",align="left",width= [format="csv", options="header",halign='center'] |=== -include::images/kiwi02_trend_stats.csv[] +include::build/images/kiwi02_trend_stats.csv[] |=== diff --git a/doc/trex_book.asciidoc b/doc/trex_book.asciidoc index fc69bdf4..ec661908 100755 --- a/doc/trex_book.asciidoc +++ b/doc/trex_book.asciidoc @@ -874,7 +874,8 @@ This feature allows more detailed clustering of clients. Let's look at the following topology: -image:images/client_clustering_topology.png[title="Client Clustering"] +.Topology Example +image:images/topology.png[title="Client Clustering",width=850] We would like to configure two clusters and direct traffic to them. @@ -980,15 +981,27 @@ MACs will be allocated incrementaly, with a wrap around. e.g. -* 16.0.0.1 --> 00:00:00:01:00:00 -* 16.0.0.2 --> 00:00:00:01:00:01 -* 16.0.0.3 --> 00:00:00:01:00:02 -* 16.0.0.4 --> 00:00:00:01:00:03 -* 16.0.0.5 --> 00:00:00:01:00:00 -* 16.0.0.6 --> 00:00:00:01:00:01 +*Initiator side:* + +* 16.0.0.1 --> dst_mac: 00:00:00:01:00:00 valn: 100 +* 16.0.0.2 --> dst_mac: 00:00:00:01:00:01 valn: 100 +* 16.0.0.3 --> dst_mac: 00:00:00:01:00:02 valn: 100 +* 16.0.0.4 --> dst_mac: 00:00:00:01:00:03 valn: 100 +* 16.0.0.5 --> dst_mac: 00:00:00:01:00:00 valn: 100 +* 16.0.0.6 --> dst_mac: 00:00:00:01:00:01 valn: 100 + +*responder side:* + + + +* server ->16.0.0.1 dst_mac(from responder) : "01:00:00:00:02:01" , valn:201 +* server ->16.0.0.2 dst_mac(from responder) : "01:00:00:00:02:02" , valn:201 and so on. +With this model every client (e.g. 16.0.0.1) will always have the same path, e.g. +c->s side will always have initiator VLAN and init-destination MAC and in the response side (s->c) alway responder-VLAN and responder-MAC + *Usage:* [source,bash] diff --git a/doc/trex_toc.asciidoc b/doc/trex_toc.asciidoc index a41d707f..eecb883f 100644 --- a/doc/trex_toc.asciidoc +++ b/doc/trex_toc.asciidoc @@ -24,7 +24,7 @@ ifdef::backend-xhtml11[] <!-- load the theme CSS file --> <link href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css" rel="stylesheet"/> - <link href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" rel="stylesheet" /> + <link href="https://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" rel="stylesheet" /> <!-- include the jQuery library --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.1/jquery.min.js"> diff --git a/doc/visio_drawings/topology.vsd b/doc/visio_drawings/topology.vsd Binary files differnew file mode 100644 index 00000000..d076023e --- /dev/null +++ b/doc/visio_drawings/topology.vsd diff --git a/doc/ws_main.py b/doc/ws_main.py index 9d2882c8..58f6e98f 100755 --- a/doc/ws_main.py +++ b/doc/ws_main.py @@ -292,7 +292,7 @@ TOC_HEAD = """ <!-- load the theme CSS file --> <link href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css" rel="stylesheet"/> - <link href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" rel="stylesheet" /> + <link href="https://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" rel="stylesheet" /> <!-- include the jQuery library --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.1/jquery.min.js"> @@ -891,9 +891,9 @@ def build_cp(bld,dir,root,callback): def create_analytic_report(task): try: import AnalyticsWebReport as analytics - analytics.main() - except: - raise Exception('Error importing or using AnalyticsWebReport script') + analytics.main(verbose = Logs.verbose) + except Exception as e: + raise Exception('Error importing or using AnalyticsWebReport script: %s' % e) @@ -923,6 +923,7 @@ def build(bld): if bld.options.performance: bld(rule=create_analytic_report) + bld.add_group() bld(rule=convert_to_html_toc_book, source='trex_analytics.asciidoc waf.css', target='trex_analytics.html',scan=ascii_doc_scan); return diff --git a/linux/ws_main.py b/linux/ws_main.py index b835e4ce..31d6b979 100755 --- a/linux/ws_main.py +++ b/linux/ws_main.py @@ -97,6 +97,7 @@ bp_sim_gtest = SrcGroup(dir='src', src_list=[ 'bp_gtest.cpp', 'gtest/tuple_gen_test.cpp', + 'gtest/client_cfg_test.cpp', 'gtest/nat_test.cpp', 'gtest/trex_stateless_gtest.cpp' ]) @@ -118,6 +119,7 @@ main_src = SrcGroup(dir='src', 'time_histogram.cpp', 'utl_json.cpp', 'utl_cpuu.cpp', + 'utl_ip.cpp', 'msg_manager.cpp', 'trex_port_attr.cpp', 'publisher/trex_publisher.cpp', diff --git a/linux_dpdk/ws_main.py b/linux_dpdk/ws_main.py index 7fd73f2e..eccb4089 100755 --- a/linux_dpdk/ws_main.py +++ b/linux_dpdk/ws_main.py @@ -137,6 +137,7 @@ main_src = SrcGroup(dir='src', 'time_histogram.cpp', 'os_time.cpp', 'utl_cpuu.cpp', + 'utl_ip.cpp', 'utl_json.cpp', 'utl_yaml.cpp', 'nat_check.cpp', diff --git a/scripts/automation/regression/aggregate_results.py b/scripts/automation/regression/aggregate_results.py index c7c61ea6..130e0545 100755 --- a/scripts/automation/regression/aggregate_results.py +++ b/scripts/automation/regression/aggregate_results.py @@ -8,10 +8,8 @@ import sys, os from collections import OrderedDict import copy import datetime, time -try: - import cPickle as pickle -except: - import pickle +import traceback +import yaml import subprocess, shlex from ansi2html import Ansi2HTMLConverter @@ -25,6 +23,15 @@ FUNCTIONAL_CATEGORY = 'Functional' # how to display those categories ERROR_CATEGORY = 'Error' +def try_write(file, text): + try: + file.write(text) + except: + try: + file.write(text.encode('utf-8')) + except: + file.write(text.decode('utf-8')) + def pad_tag(text, tag): return '<%s>%s</%s>' % (tag, text, tag) @@ -256,6 +263,7 @@ if __name__ == '__main__': build_url = os.environ.get('BUILD_URL') build_id = os.environ.get('BUILD_ID') trex_repo = os.environ.get('TREX_CORE_REPO') + last_commit_info_file = os.environ.get('LAST_COMMIT_INFO') python_ver = os.environ.get('PYTHON_VER') if not scenario: print('Warning: no environment variable SCENARIO, using default') @@ -283,19 +291,25 @@ if __name__ == '__main__': trex_last_commit_info = '' trex_last_commit_hash = trex_info_dict.get('Git SHA') - if trex_last_commit_hash and trex_repo: + if last_commit_info_file and os.path.exists(last_commit_info_file): + with open(last_commit_info_file) as f: + trex_last_commit_info = f.read().strip().replace('\n', '<br>\n') + elif trex_last_commit_hash and trex_repo: try: - print('Getting TRex commit with hash %s' % trex_last_commit_hash) - command = 'git --git-dir %s show %s --quiet' % (trex_repo, trex_last_commit_hash) + command = 'git show %s -s' % trex_last_commit_hash print('Executing: %s' % command) - proc = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (trex_last_commit_info, stderr) = proc.communicate() - print('Stdout:\n\t' + trex_last_commit_info.replace('\n', '\n\t')) - print('Stderr:', stderr) - print('Return code:', proc.returncode) - trex_last_commit_info = trex_last_commit_info.replace('\n', '<br>') + proc = subprocess.Popen(shlex.split(command), stdout = subprocess.PIPE, stderr = subprocess.STDOUT, cwd = trex_repo) + (stdout, stderr) = proc.communicate() + stdout = stdout.decode('utf-8', errors = 'replace') + print('Stdout:\n\t' + stdout.replace('\n', '\n\t')) + if stderr or proc.returncode: + print('Return code: %s' % proc.returncode) + trex_last_commit_info = stdout.replace('\n', '<br>\n') except Exception as e: + traceback.print_exc() print('Error getting last commit: %s' % e) + else: + print('Could not find info about commit!') ##### get xmls: report_<setup name>.xml @@ -520,7 +534,7 @@ if __name__ == '__main__': # save html with open(args.output_htmlfile, 'w') as f: print('Writing output file: %s' % args.output_htmlfile) - f.write(html_output) + try_write(f, html_output) html_output = None # mail report (only error tests, expanded) @@ -596,7 +610,7 @@ if __name__ == '__main__': else: mail_output += add_category_of_tests(ERROR_CATEGORY, error_tests, expanded=True) else: - mail_output += '<table><tr style="font-size:120;color:green;font-family:arial"><td>☺</td><td style="font-size:20">All passed.</td></tr></table>\n' + mail_output += u'<table><tr style="font-size:120;color:green;font-family:arial"><td>☺</td><td style="font-size:20">All passed.</td></tr></table>\n' mail_output += '\n</body>\n</html>' ##### save outputs @@ -605,17 +619,17 @@ if __name__ == '__main__': # mail content with open(args.output_mailfile, 'w') as f: print('Writing output file: %s' % args.output_mailfile) - f.write(mail_output) + try_write(f, mail_output) # build status category_dict_status = {} if os.path.exists(args.build_status_file): print('Reading: %s' % args.build_status_file) - with open(args.build_status_file, 'rb') as f: + with open(args.build_status_file, 'r') as f: try: - category_dict_status = pickle.load(f) + category_dict_status = yaml.safe_load(f.read()) except Exception as e: - print('Error during pickle load: %s' % e) + print('Error during YAML load: %s' % e) if type(category_dict_status) is not dict: print('%s is corrupt, truncating' % args.build_status_file) category_dict_status = {} @@ -635,15 +649,15 @@ if __name__ == '__main__': current_status = 'Fixed' category_dict_status[scenario] = current_status - with open(args.build_status_file, 'wb') as f: + with open(args.build_status_file, 'w') as f: print('Writing output file: %s' % args.build_status_file) - pickle.dump(category_dict_status, f) + yaml.dump(category_dict_status, f) # last successful commit - if (current_status in ('Successful', 'Fixed')) and trex_last_commit_hash and jobs_list > 0 and scenario == 'nightly': + if (current_status in ('Successful', 'Fixed')) and trex_last_commit_hash and len(jobs_list) > 0 and scenario == 'nightly': with open(args.last_passed_commit, 'w') as f: print('Writing output file: %s' % args.last_passed_commit) - f.write(trex_last_commit_hash) + try_write(f, trex_last_commit_hash) # mail title mailtitle_output = scenario.capitalize() @@ -653,7 +667,8 @@ if __name__ == '__main__': with open(args.output_titlefile, 'w') as f: print('Writing output file: %s' % args.output_titlefile) - f.write(mailtitle_output) + try_write(f, mailtitle_output) # exit + print('Status: %s' % current_status) sys.exit(exit_status) diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp index c1df72fc..80297c32 100755 --- a/src/bp_sim.cpp +++ b/src/bp_sim.cpp @@ -4577,6 +4577,22 @@ int CFlowGenList::load_client_config_file(std::string file_name) { return (0); } +void CFlowGenList::set_client_config_tuple_gen_info(CTupleGenYamlInfo * tg) { + m_client_config_info.set_tuple_gen_info(tg); +} + +void CFlowGenList::get_client_cfg_ip_list(std::vector<ClientCfgCompactEntry *> &ret) { + m_client_config_info.get_entry_list(ret); +} + +void CFlowGenList::set_client_config_resolved_macs(CManyIPInfo &pretest_result) { + m_client_config_info.set_resolved_macs(pretest_result); +} + +void CFlowGenList::dump_client_config(FILE *fd) { + m_client_config_info.dump(fd); +} + int CFlowGenList::load_from_yaml(std::string file_name, uint32_t num_threads){ uint8_t idx; @@ -5232,11 +5248,11 @@ void CErfIF::add_vlan(uint16_t vlan_id) { m_raw->pkt_len += 4; } -void CErfIF::apply_client_config(const ClientCfg *cfg, pkt_dir_t dir) { +void CErfIF::apply_client_config(const ClientCfgBase *cfg, pkt_dir_t dir) { assert(cfg); uint8_t *p = (uint8_t *)m_raw->raw; - const ClientCfgDir &cfg_dir = ( (dir == CLIENT_SIDE) ? cfg->m_initiator : cfg->m_responder); + const ClientCfgDirBase &cfg_dir = ( (dir == CLIENT_SIDE) ? cfg->m_initiator : cfg->m_responder); /* dst mac */ if (cfg_dir.has_dst_mac_addr()) { diff --git a/src/bp_sim.h b/src/bp_sim.h index 0cf77437..914a26d9 100755 --- a/src/bp_sim.h +++ b/src/bp_sim.h @@ -44,6 +44,7 @@ limitations under the License. #include <common/bitMan.h> #include <yaml-cpp/yaml.h> #include "trex_defs.h" +#include "utl_ip.h" #include "os_time.h" #include "pal_utl.h" #include "rx_check_header.h" @@ -72,12 +73,6 @@ class CGenNodePCAP; #define FORCE_NO_INLINE __attribute__ ((noinline)) #define FORCE_INLINE __attribute__((always_inline)) -/* IP address, last 32-bits of IPv6 remaps IPv4 */ -typedef struct { - uint16_t v6[6]; /* First 96-bits of IPv6 */ - uint32_t v4; /* Last 32-bits IPv6 overloads v4 */ -} ipaddr_t; - /* reserve both 0xFF and 0xFE , router will -1 FF */ #define TTL_RESERVE_DUPLICATE 0xff #define TOS_TTL_RESERVE_DUPLICATE 0x1 @@ -175,38 +170,6 @@ typedef enum { VM_REPLACE_IP_OFFSET =0x12, /* fix ip at offset */ /* work only on x86 littel */ #define MY_B(b) (((int)b)&0xff) -// Routine to create IPv4 address string -inline int ip_to_str(uint32_t ip,char * str){ - uint32_t ipv4 = PKT_HTONL(ip); - inet_ntop(AF_INET, (const char *)&ipv4, str, INET_ADDRSTRLEN); - 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; - uint16_t ipv6[8]; - for (uint8_t i=0; i<6; i++) { - ipv6[i] = PKT_HTONS(ip->v6[i]); - } - uint32_t ipv4 = PKT_HTONL(ip->v4); - ipv6[6] = ipv4 & 0xffff; - ipv6[7] = ipv4 >> 16; - - str[idx++] = '['; - inet_ntop(AF_INET6, (const char *)&ipv6, &str[1], INET6_ADDRSTRLEN); - idx = strlen(str); - str[idx++] = ']'; - str[idx] = 0; - return(idx); -} - class CFlowPktInfo ; class CMiniVM { @@ -694,19 +657,16 @@ class CPerPortIPCfg { uint32_t get_mask() {return m_mask;} uint32_t get_def_gw() {return m_def_gw;} uint32_t get_vlan() {return m_vlan;} - bool grat_arp_needed() {return m_grat_arp_needed;} void set_ip(uint32_t val) {m_ip = val;} void set_mask(uint32_t val) {m_mask = val;} void set_def_gw(uint32_t val) {m_def_gw = val;} void set_vlan(uint16_t val) {m_vlan = val;} - void set_grat_arp_needed(bool val) {m_grat_arp_needed = val;} private: uint32_t m_def_gw; uint32_t m_ip; uint32_t m_mask; uint16_t m_vlan; - bool m_grat_arp_needed; }; class CParserOption { @@ -1564,7 +1524,7 @@ public: 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; + const ClientCfgBase *m_client_cfg; uint32_t m_src_idx; uint32_t m_dest_idx; uint32_t m_end_of_cache_line[6]; @@ -1889,7 +1849,7 @@ typedef std::priority_queue<CGenNode *, std::vector<CGenNode *>,CGenNodeCompare> class CErfIF : public CVirtualIF { - + friend class basic_client_cfg_test1_Test; public: CErfIF(){ m_writer=NULL; @@ -1928,7 +1888,7 @@ public: protected: void add_vlan(uint16_t vlan_id); - void apply_client_config(const ClientCfg *cfg, pkt_dir_t dir); + void apply_client_config(const ClientCfgBase *cfg, pkt_dir_t dir); virtual void fill_raw_packet(rte_mbuf_t * m,CGenNode * node,pkt_dir_t dir); CFileWriterBase * m_writer; @@ -4031,6 +3991,10 @@ public: int load_from_yaml(std::string csv_file,uint32_t num_threads); int load_client_config_file(std::string file_name); + void set_client_config_tuple_gen_info(CTupleGenYamlInfo * tg); + void get_client_cfg_ip_list(std::vector<ClientCfgCompactEntry *> &ret); + void set_client_config_resolved_macs(CManyIPInfo &pretest_result); + void dump_client_config(FILE *fd); public: void Dump(FILE *fd); diff --git a/src/common/Network/Packet/MacAddress.h b/src/common/Network/Packet/MacAddress.h index 7e872fd6..5dc4a9ea 100755 --- a/src/common/Network/Packet/MacAddress.h +++ b/src/common/Network/Packet/MacAddress.h @@ -29,7 +29,7 @@ public: MacAddress() { - set(0xca, 0xfe, 0xde, 0xad, 0xbe, 0xef); + set(0xff, 0xff, 0xff, 0xff, 0xff, 0xff); }; MacAddress(uint8_t a0, @@ -47,15 +47,15 @@ public: a5); }; - MacAddress(uint8_t macAddr[ETHER_ADDR_LEN]) - { - set(macAddr[0], - macAddr[1], - macAddr[2], - macAddr[3], - macAddr[4], - macAddr[5] ); - }; + MacAddress(uint8_t macAddr[ETHER_ADDR_LEN]) + { + set(macAddr[0], + macAddr[1], + macAddr[2], + macAddr[3], + macAddr[4], + macAddr[5] ); + }; void set(uint8_t a0, uint8_t a1, @@ -81,50 +81,78 @@ public: data[5]=val; } + void set(uint64_t val) { + for (int i = 0; i < 6; i++) { + data[i] = ( val >> ((5 - i) * 8) ) & 0xFF; + } + } + + bool isDefaultAddress() const + { + static MacAddress defaultMac; + return (*this == defaultMac); + } + + bool isInvalidAddress() const + { + static MacAddress allZeros(0,0,0,0,0,0); + static MacAddress defaultMac; + return (*this == allZeros || *this == defaultMac); + } + void setIdentifierAsBogusAddr(uint32_t identifier) + { + *(uint32_t*)data = identifier; + } + + uint32_t getIdentifierFromBogusAddr() + { + return *(uint32_t*)data; + } + + MacAddress& operator+=(const int& val) { + uint64_t tmp = 0; + + for (int i = 0; i < 6; i++) { + tmp <<= 8; + tmp |= data[i]; + } - bool isInvalidAddress() const - { - static MacAddress allZeros(0,0,0,0,0,0); - static MacAddress cafeDeadBeef; - return (*this == allZeros || *this == cafeDeadBeef); - } - void setIdentifierAsBogusAddr(uint32_t identifier) - { - *(uint32_t*)data = identifier; - } - - uint32_t getIdentifierFromBogusAddr() - { - return *(uint32_t*)data; - } - - bool operator == (const MacAddress& rhs) const - { - for(int i = 0; i < ETHER_ADDR_LEN; i++) - { - if(data[i] != rhs.data[i]) - return false; - } - - return true; - } - - uint8_t* GetBuffer() - { - return data; - } - - const uint8_t* GetConstBuffer() const - { - return data; - } + tmp += val; + + for (int i = 0; i < 6; i++) { + data[i] = ( tmp >> ((5 - i) * 8) ) & 0xFF; + } + + return *this; + } + + bool operator == (const MacAddress& rhs) const + { + for(int i = 0; i < ETHER_ADDR_LEN; i++) + { + if(data[i] != rhs.data[i]) + return false; + } + + return true; + } + + uint8_t* GetBuffer() + { + return data; + } + + const uint8_t* GetConstBuffer() const + { + return data; + } void dump(FILE *fd) const; - void copyToArray(uint8_t *arrayToFill) const - { + void copyToArray(uint8_t *arrayToFill) const + { ((uint32_t*)arrayToFill)[0] = ((uint32_t*)data)[0];//Copy first 32bit - ((uint16_t*)arrayToFill)[2] = ((uint16_t*)data)[2];//Copy last 16bit - } + ((uint16_t*)arrayToFill)[2] = ((uint16_t*)data)[2];//Copy last 16bit + } public: uint8_t data[ETHER_ADDR_LEN]; diff --git a/src/common/basic_utils.cpp b/src/common/basic_utils.cpp index dfd3b183..d8a95b53 100755 --- a/src/common/basic_utils.cpp +++ b/src/common/basic_utils.cpp @@ -178,6 +178,13 @@ void TestDump(void){ utl_DumpBuffer2(stdout,buffer,31,1,4,SHOW_BUFFER_ADDR_EN |SHOW_BUFFER_CHAR); } +std::string +utl_macaddr_to_str(const uint8_t *mac) { + std::string tmp; + utl_macaddr_to_str(mac, tmp); + return tmp; +} + void utl_macaddr_to_str(const uint8_t *macaddr, std::string &output) { for (int i = 0; i < 6; i++) { diff --git a/src/common/basic_utils.h b/src/common/basic_utils.h index ab0ff1ec..ce2da691 100755 --- a/src/common/basic_utils.h +++ b/src/common/basic_utils.h @@ -85,6 +85,7 @@ inline void utl_swap(T& a, T& b) { bool utl_is_file_exists (const std::string& name) ; void utl_macaddr_to_str(const uint8_t *macaddr, std::string &output); +std::string utl_macaddr_to_str(const uint8_t *macaddr); std::string utl_macaddr_to_str(const uint8_t *macaddr); bool utl_str_to_macaddr(const std::string &s, uint8_t *mac); diff --git a/src/gtest/client_cfg_test.cpp b/src/gtest/client_cfg_test.cpp new file mode 100644 index 00000000..1e84e1d5 --- /dev/null +++ b/src/gtest/client_cfg_test.cpp @@ -0,0 +1,186 @@ +/* + 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 <stdio.h> +#include "../bp_sim.h" +#include <common/gtest.h> +#include <common/basic_utils.h> + +class basic_client_cfg : public testing::Test { + protected: + virtual void SetUp() { + } + virtual void TearDown() { + } + public: +}; + +TEST_F(basic_client_cfg, test1) { + uint32_t ip_start = 0x10010101; + uint32_t ip_end = 0x100101ff; + uint32_t next_hop_init = 0x01010101; + uint32_t next_hop_resp = 0x02020202; + uint16_t vlan_init = 5; + uint16_t vlan_resp = 7; + uint32_t dual_if_mask = 0x01000000; + uint32_t test_count = 2; + ClientCfgDB cfg_db; + ClientCfgDB test_db; + ClientCfgEntry cfg_ent; + ClientCfgExt cfg_ext; + std::vector<ClientCfgCompactEntry *> ent_list; + struct CTupleGenPoolYaml c_pool; + + // Create tuple gen, so we can have ip->port translation + CTupleGenYamlInfo tg_yam_info; + struct CTupleGenPoolYaml s_pool; + s_pool.m_ip_start = ip_start; + s_pool.m_ip_end = ip_end; + s_pool.m_dual_interface_mask = dual_if_mask; + tg_yam_info.m_client_pool.push_back(s_pool); + + CGlobalInfo::m_options.m_expected_portd = 4; + printf("Expected ports %d\n", CGlobalInfo::m_options.m_expected_portd); + + std::string tmp_file_name = "/tmp/client_cfg_gtest.yaml"; + FILE *fd = fopen(tmp_file_name.c_str(), "w"); + + if (fd == NULL) { + fprintf(stderr, "Failed opening %s file for write\n", tmp_file_name.c_str()); + } + + // We create config file with 3 groups (Should match 6 ports). + cfg_ext.m_initiator.set_next_hop(next_hop_init); + cfg_ext.m_responder.set_next_hop(next_hop_resp); + cfg_ext.m_initiator.set_vlan(vlan_init); + cfg_ext.m_responder.set_vlan(vlan_resp); + + cfg_ent.set_params(ip_start, ip_end, test_count); + cfg_ent.set_cfg(cfg_ext); + + // first group + cfg_db.set_vlan(true); + cfg_db.add_group(ip_start, cfg_ent); + + //second group + cfg_ent.set_params(ip_start + dual_if_mask, ip_end + dual_if_mask + , test_count); + cfg_db.add_group(ip_start + dual_if_mask, cfg_ent); + + // third group + cfg_ent.set_params(ip_start + 2 * dual_if_mask, ip_end + 2 * dual_if_mask + , test_count); + cfg_db.add_group(ip_start + dual_if_mask * 2, cfg_ent); + + cfg_db.dump(fd); + fclose(fd); + test_db.load_yaml_file(tmp_file_name); + test_db.set_tuple_gen_info(&tg_yam_info); + test_db.get_entry_list(ent_list); + + + // We expect ports for first two groups to be found. + // This group addresses should not appear in the list, since + // we simulate system with only 4 ports + int i = 0; + for (std::vector<ClientCfgCompactEntry *>::iterator + it = ent_list.begin(); it != ent_list.end(); it++) { + uint8_t port = (*it)->get_port(); + uint16_t vlan = (*it)->get_vlan(); + uint32_t count = (*it)->get_count(); + uint32_t dst_ip = (*it)->get_dst_ip(); + + assert(count == test_count); + switch(i) { + case 0: + case 2: + assert(port == i); + assert(vlan == vlan_init); + assert(dst_ip == next_hop_init); + break; + case 1: + case 3: + assert(port == i); + assert(vlan == vlan_resp); + assert(dst_ip == next_hop_resp); + break; + default: + fprintf(stderr, "Test failed. Too many entries returned\n"); + exit(1); + } + i++; + delete *it; + } + + // Simulate the pre test phase, and hand results to client config + CManyIPInfo many_ip; + MacAddress mac0, mac1, mac2, mac3; + mac0.set(0x0, 0x1, 0x2, 0x3, 0x4, 0); + mac1.set(0x0, 0x1, 0x2, 0x3, 0x4, 0x1); + mac2.set(0x0, 0x1, 0x2, 0x3, 0x4, 0x2); + mac3.set(0x0, 0x1, 0x2, 0x3, 0x4, 0x3); + COneIPv4Info ip0_1(next_hop_init, vlan_init, mac0, 0); + COneIPv4Info ip0_2(next_hop_init + 1, vlan_init, mac1, 0); + COneIPv4Info ip1_1(next_hop_resp, vlan_resp, mac2, 1); + COneIPv4Info ip1_2(next_hop_resp + 1, vlan_resp, mac3, 1); + + many_ip.insert(ip0_1); + many_ip.insert(ip0_2); + many_ip.insert(ip1_1); + many_ip.insert(ip1_2); + + test_db.set_resolved_macs(many_ip); + + ClientCfgBase cfg0; + + ClientCfgEntry *ent0 = test_db.lookup(ip_start); + ClientCfgEntry *ent1 = test_db.lookup(ip_start + dual_if_mask); + + assert (ent0 != NULL); + ent0->assign(cfg0); + assert (!memcmp(cfg0.m_initiator.get_dst_mac_addr() + , mac0.GetConstBuffer(), ETHER_ADDR_LEN)); + ent0->assign(cfg0); + assert (!memcmp(cfg0.m_initiator.get_dst_mac_addr() + , mac1.GetConstBuffer(), ETHER_ADDR_LEN)); + ent0->assign(cfg0); + assert (!memcmp(cfg0.m_responder.get_dst_mac_addr() + , mac2.GetConstBuffer(), ETHER_ADDR_LEN)); + ent0->assign(cfg0); + assert (!memcmp(cfg0.m_responder.get_dst_mac_addr() + , mac3.GetConstBuffer(), ETHER_ADDR_LEN)); + + assert(ent1 != NULL); + ent1->assign(cfg0); + assert (!memcmp(cfg0.m_initiator.get_dst_mac_addr() + , mac0.GetConstBuffer(), ETHER_ADDR_LEN)); + ent1->assign(cfg0); + assert (!memcmp(cfg0.m_initiator.get_dst_mac_addr() + , mac1.GetConstBuffer(), ETHER_ADDR_LEN)); + ent1->assign(cfg0); + assert (!memcmp(cfg0.m_responder.get_dst_mac_addr() + , mac2.GetConstBuffer(), ETHER_ADDR_LEN)); + ent1->assign(cfg0); + assert (!memcmp(cfg0.m_responder.get_dst_mac_addr() + , mac3.GetConstBuffer(), ETHER_ADDR_LEN)); + +} + diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index e799a5bd..af6d8366 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -486,11 +486,11 @@ public: private: - virtual void add_del_rules(enum rte_filter_op op, uint8_t port_id, - uint16_t type, uint8_t ttl, - uint16_t ip_id, + virtual void add_del_rules(enum rte_filter_op op, uint8_t port_id, + uint16_t type, uint8_t ttl, + uint16_t ip_id, uint8_t l4_proto, - int queue, + int queue, uint16_t stat_idx); virtual int configure_rx_filter_rules_statfull(CPhyEthIF * _if); @@ -1123,6 +1123,9 @@ static int parse_options(int argc, char *argv[], CParserOption* po, bool first_t if ( opt_vlan_was_set ) { po->preview.set_vlan_mode_enable(true); } + if (CGlobalInfo::m_options.client_cfg_file != "") { + parse_err("Client config file is not supported with interactive (stateless) mode "); + } if ( po->m_duration ) { parse_err("Duration is not supported with interactive (stateless) mode "); } @@ -1970,7 +1973,7 @@ public: __attribute__ ((noinline)) void handle_rx_queue(); __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); + void apply_client_cfg(const ClientCfgBase *cfg, rte_mbuf_t *m, pkt_dir_t dir, uint8_t *p); bool process_rx_pkt(pkt_dir_t dir,rte_mbuf_t * m); @@ -2361,12 +2364,12 @@ 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) { +void CCoreEthIF::apply_client_cfg(const ClientCfgBase *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); + const ClientCfgDirBase &cfg_dir = ( (dir == CLIENT_SIDE) ? cfg->m_initiator : cfg->m_responder); /* dst mac */ if (cfg_dir.has_dst_mac_addr()) { @@ -3181,74 +3184,162 @@ void CGlobalTRex::pre_test() { CPretest pretest(m_max_ports); bool resolve_needed = false; uint8_t empty_mac[ETHER_ADDR_LEN] = {0,0,0,0,0,0}; + bool need_grat_arp[TREX_MAX_PORTS]; + + if (CGlobalInfo::m_options.preview.get_is_client_cfg_enable()) { + std::vector<ClientCfgCompactEntry *> conf; + m_fl.get_client_cfg_ip_list(conf); + + // If we got src MAC for port in global config, take it, otherwise use src MAC from DPDK + uint8_t port_macs[m_max_ports][ETHER_ADDR_LEN]; + for (int port_id = 0; port_id < m_max_ports; port_id++) { + uint8_t empty_mac[ETHER_ADDR_LEN] = {0,0,0,0,0,0}; + if (! memcmp( CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src, empty_mac, ETHER_ADDR_LEN)) { + rte_eth_macaddr_get(port_id, + (struct ether_addr *)&CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src); + } + memcpy(port_macs[port_id], CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src, ETHER_ADDR_LEN); + } + + for (std::vector<ClientCfgCompactEntry *>::iterator it = conf.begin(); it != conf.end(); it++) { + uint8_t port = (*it)->get_port(); + uint16_t vlan = (*it)->get_vlan(); + uint32_t count = (*it)->get_count(); + uint32_t dst_ip = (*it)->get_dst_ip(); + uint32_t src_ip = (*it)->get_src_ip(); + + for (int i = 0; i < count; i++) { + //??? handle ipv6; + if ((*it)->is_ipv4()) { + pretest.add_next_hop(port, dst_ip + i, vlan); + } + } + if (!src_ip) { + src_ip = CGlobalInfo::m_options.m_ip_cfg[port].get_ip(); + if (!src_ip) { + fprintf(stderr, "No matching src ip for port: %d ip:%s vlan: %d\n" + , port, ip_to_str(dst_ip).c_str(), vlan); + fprintf(stderr, "You must specify src_ip in client config file or in TRex config file\n"); + exit(1); + } + } + pretest.add_ip(port, src_ip, vlan, port_macs[port]); + COneIPv4Info ipv4(src_ip, vlan, port_macs[port], port); + m_mg.add_grat_arp_src(ipv4); + + delete *it; + } + if ( CGlobalInfo::m_options.preview.getVMode() > 1) { + fprintf(stdout, "*******Pretest for client cfg********\n"); + pretest.dump(stdout); + } + } else { + for (int port_id = 0; port_id < m_max_ports; port_id++) { + if (! memcmp( CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, empty_mac, ETHER_ADDR_LEN)) { + resolve_needed = true; + } else { + resolve_needed = false; + } + if (! memcmp( CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src, empty_mac, ETHER_ADDR_LEN)) { + rte_eth_macaddr_get(port_id, + (struct ether_addr *)&CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src); + need_grat_arp[port_id] = true; + } else { + // If we got src MAC from config file, do not send gratuitous ARP for it + // (for compatibility with old behaviour) + need_grat_arp[port_id] = false; + } + + pretest.add_ip(port_id, CGlobalInfo::m_options.m_ip_cfg[port_id].get_ip() + , CGlobalInfo::m_options.m_ip_cfg[port_id].get_vlan() + , CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src); + + if (resolve_needed) { + pretest.add_next_hop(port_id, CGlobalInfo::m_options.m_ip_cfg[port_id].get_def_gw() + , CGlobalInfo::m_options.m_ip_cfg[port_id].get_vlan()); + } + } + } for (int port_id = 0; port_id < m_max_ports; port_id++) { CPhyEthIF *pif = &m_ports[port_id]; // Configure port to send all packets to software CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(pif, true); - if (! memcmp( CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, empty_mac, ETHER_ADDR_LEN)) { - resolve_needed = true; - } else { - resolve_needed = false; - } - if (! memcmp( CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src, empty_mac, ETHER_ADDR_LEN)) { - rte_eth_macaddr_get(port_id, - (struct ether_addr *)&CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src); - CGlobalInfo::m_options.m_ip_cfg[port_id].set_grat_arp_needed(true); - } else { - // If we got src MAC from config file, do not send gratuitous ARP for it (for compatibility with old behaviour) - CGlobalInfo::m_options.m_ip_cfg[port_id].set_grat_arp_needed(false); - } - pretest.set_port_params(port_id, CGlobalInfo::m_options.m_ip_cfg[port_id] - , CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src - , resolve_needed); } + pretest.send_grat_arp_all(); bool ret; int count = 0; + bool resolve_failed = false; do { ret = pretest.resolve_all(); count++; } while ((ret != true) && (count < 10)); + if (ret != true) { + resolve_failed = true; + } - if ( CGlobalInfo::m_options.preview.getVMode() > 0) { + if ( CGlobalInfo::m_options.preview.getVMode() > 1) { + fprintf(stdout, "*******Pretest after resolving ********\n"); pretest.dump(stdout); } - uint8_t mac[ETHER_ADDR_LEN]; - for (int port_id = 0; port_id < m_max_ports; port_id++) { - if (! memcmp(CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, empty_mac, ETHER_ADDR_LEN)) { - // we don't have dest MAC. Get it from what we resolved. - uint32_t ip = CGlobalInfo::m_options.m_ip_cfg[port_id].get_def_gw(); - if (! pretest.get_mac(port_id, ip, mac)) { - fprintf(stderr, "Failed resolving dest MAC for default gateway:%d.%d.%d.%d on port %d\n" - , (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF, port_id); - exit(1); + + if (CGlobalInfo::m_options.preview.get_is_client_cfg_enable()) { + CManyIPInfo pretest_result; + pretest.get_results(pretest_result); + if (resolve_failed) { + fprintf(stderr, "Resolution of following IPs failed. Exiting.\n"); + for (const COneIPInfo *ip=pretest_result.get_next(); ip != NULL; + ip = pretest_result.get_next()) { + ip->dump(stderr); } - memcpy(CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, mac, ETHER_ADDR_LEN); - - // if port is connected in loopback, no need to send gratuitous ARP. It will only confuse our ingress counters. - if (pretest.is_loopback(port_id)) - CGlobalInfo::m_options.m_ip_cfg[port_id].set_grat_arp_needed(false); + exit(-1); + } + m_fl.set_client_config_resolved_macs(pretest_result); + if ( CGlobalInfo::m_options.preview.getVMode() > 1) { + m_fl.dump_client_config(stdout); } - // update statistics baseline, so we can ignore what happened in pre test phase - CPhyEthIF *pif = &m_ports[port_id]; - CPreTestStats pre_stats = pretest.get_stats(port_id); - pif->set_ignore_stats_base(pre_stats); - - // Configure port back to normal mode. Only relevant packets handled by software. - CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(pif, false); - + } else { + uint8_t mac[ETHER_ADDR_LEN]; + for (int port_id = 0; port_id < m_max_ports; port_id++) { + if (! memcmp(CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, empty_mac, ETHER_ADDR_LEN)) { + // we don't have dest MAC. Get it from what we resolved. + uint32_t ip = CGlobalInfo::m_options.m_ip_cfg[port_id].get_def_gw(); + uint16_t vlan = CGlobalInfo::m_options.m_ip_cfg[port_id].get_vlan(); + if (! pretest.get_mac(port_id, ip, vlan, mac)) { + fprintf(stderr, "Failed resolving dest MAC for default gateway:%d.%d.%d.%d on port %d\n" + , (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF, port_id); + exit(1); + } + memcpy(CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest, mac, ETHER_ADDR_LEN); + // if port is connected in loopback, no need to send gratuitous ARP. It will only confuse our ingress counters. + if (need_grat_arp[port_id] && (! pretest.is_loopback(port_id))) { + COneIPv4Info ipv4(CGlobalInfo::m_options.m_ip_cfg[port_id].get_ip() + , CGlobalInfo::m_options.m_ip_cfg[port_id].get_vlan() + , CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src + , port_id); + m_mg.add_grat_arp_src(ipv4); + } + } - /* set resolved IPv4 */ - uint32_t dg = CGlobalInfo::m_options.m_ip_cfg[port_id].get_def_gw(); - const uint8_t *dst_mac = CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest; - if (dg) { - m_ports[port_id].get_port_attr()->get_dest().set_dest_ipv4(dg, dst_mac); - } else { - m_ports[port_id].get_port_attr()->get_dest().set_dest_mac(dst_mac); - } + // update statistics baseline, so we can ignore what happened in pre test phase + CPhyEthIF *pif = &m_ports[port_id]; + CPreTestStats pre_stats = pretest.get_stats(port_id); + pif->set_ignore_stats_base(pre_stats); + + // Configure port back to normal mode. Only relevant packets handled by software. + CTRexExtendedDriverDb::Ins()->get_drv()->set_rcv_all(pif, false); + + /* set resolved IPv4 */ + uint32_t dg = CGlobalInfo::m_options.m_ip_cfg[port_id].get_def_gw(); + const uint8_t *dst_mac = CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.dest; + if (dg) { + m_ports[port_id].get_port_attr()->get_dest().set_dest_ipv4(dg, dst_mac); + } else { + m_ports[port_id].get_port_attr()->get_dest().set_dest_mac(dst_mac); + } } @@ -4744,6 +4835,8 @@ int CGlobalTRex::start_master_statefull() { exit(-1); } CGlobalInfo::m_options.preview.set_client_cfg_enable(true); + m_fl.set_client_config_tuple_gen_info(&m_fl.m_yaml_info.m_tuple_gen); + pre_test(); } /* verify options */ @@ -5485,7 +5578,10 @@ int main_test(int argc , char * argv[]){ } } - g_trex.pre_test(); + // in case of client config, we already run pretest + if (! CGlobalInfo::m_options.preview.get_is_client_cfg_enable()) { + g_trex.pre_test(); + } // after doing all needed ARP resolution, we need to flush queues, and stop our drop queue g_trex.ixgbe_rx_queue_flush(); @@ -6442,15 +6538,15 @@ void CTRexExtendedDriverBaseMlnx5G::update_configuration(port_cfg_t * cfg){ cfg->m_port_conf.fdir_conf.mask.ipv4_mask.tos=0x01; cfg->m_port_conf.fdir_conf.mask.ipv6_mask.proto=0xff; cfg->m_port_conf.fdir_conf.mask.ipv6_mask.tc=0x01; - + } -void CTRexExtendedDriverBaseMlnx5G::add_del_rules(enum rte_filter_op op, uint8_t port_id, uint16_t type, - uint8_t ttl, - uint16_t ip_id, - uint8_t l4_proto, +void CTRexExtendedDriverBaseMlnx5G::add_del_rules(enum rte_filter_op op, uint8_t port_id, uint16_t type, + uint8_t ttl, + uint16_t ip_id, + uint8_t l4_proto, int queue, uint16_t stat_idx) { - /* Mellanox card does not have TTL support, + /* Mellanox card does not have TTL support, so we will replace it in low level with TOS */ int ret=rte_eth_dev_filter_supported(port_id, RTE_ETH_FILTER_FDIR); @@ -6520,8 +6616,8 @@ int CTRexExtendedDriverBaseMlnx5G::add_del_rx_flow_stat_rule(uint8_t port_id, en int CTRexExtendedDriverBaseMlnx5G::configure_rx_filter_rules_statfull(CPhyEthIF * _if) { uint32_t port_id = _if->get_port_id(); /* TTL==TOS */ - - /*PID=1 ==> MASK TOS=0x1/0x1*/ + + /*PID=1 ==> MASK TOS=0x1/0x1*/ add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_UDP, 0x1, 1, 17, MAIN_DPDK_RX_Q, 0); /*TCP/UDP */ add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_TCP, 0x1, 1, 6, MAIN_DPDK_RX_Q, 0); add_del_rules(RTE_ETH_FILTER_ADD, port_id, RTE_ETH_FLOW_NONFRAG_IPV4_OTHER, 0x1, 1, 132, MAIN_DPDK_RX_Q, 0); /*SCTP*/ @@ -6586,7 +6682,7 @@ void CTRexExtendedDriverBaseMlnx5G::get_extended_stats(CPhyEthIF * _if,CPhyEthIF rte_eth_stats_get(_if->get_port_id(), &stats1); stats->ipackets += stats1.ipackets - prev_stats->ipackets; - stats->ibytes += stats1.ibytes - prev_stats->ibytes + + stats->ibytes += stats1.ibytes - prev_stats->ibytes + + (stats1.ipackets << 2) - (prev_stats->ipackets << 2); stats->opackets += stats1.opackets - prev_stats->opackets; stats->obytes += stats1.obytes - prev_stats->obytes @@ -6760,7 +6856,7 @@ int CTRexExtendedDriverBaseVIC::configure_rx_filter_rules_statefull(CPhyEthIF * return 0; } -extern "C" int enicpmd_dev_get_fw_support(int port_id, +extern "C" int enicpmd_dev_get_fw_support(int port_id, uint32_t *ver); @@ -6771,7 +6867,7 @@ int CTRexExtendedDriverBaseVIC::verify_fw_ver(int port_id) { if (ret==0) { if (CGlobalInfo::m_options.preview.getVMode() >= 1) { - printf("VIC port %d: FW support advanced filtering \n", port_id); + printf("VIC port %d: FW support advanced filtering \n", port_id); } }else{ printf("Error: VIC firmware should upgrade to support advanced filtering \n"); @@ -6807,7 +6903,7 @@ int CTRexExtendedDriverBaseVIC::get_rx_stats(CPhyEthIF * _if, uint32_t *pkts, ui int CTRexExtendedDriverBaseVIC::dump_fdir_global_stats(CPhyEthIF * _if, FILE *fd) { //printf(" NOT supported yet \n"); - return (0); + return (0); } diff --git a/src/pre_test.cpp b/src/pre_test.cpp index 76fa9a26..583427eb 100644 --- a/src/pre_test.cpp +++ b/src/pre_test.cpp @@ -29,80 +29,356 @@ #include "pkt_gen.h" #include "pre_test.h" -void CPretestPortInfo::set_params(CPerPortIPCfg port_cfg, const uint8_t *src_mac, bool resolve_needed) { - m_ip = port_cfg.get_ip(); - m_def_gw = port_cfg.get_def_gw(); - m_vlan = port_cfg.get_vlan(); - memcpy(&m_src_mac, src_mac, sizeof(m_src_mac)); - if (resolve_needed) { - m_state = CPretestPortInfo::RESOLVE_NEEDED; - } else { - m_state = CPretestPortInfo::RESOLVE_NOT_NEEDED; - } +CPretestOnePortInfo::CPretestOnePortInfo() { + m_state = RESOLVE_NOT_NEEDED; + m_is_loopback = false; + m_stats.clear(); } -void CPretestPortInfo::set_dst_mac(const uint8_t *dst_mac) { - memcpy(&m_dst_mac, dst_mac, sizeof(m_dst_mac)); - m_state = CPretestPortInfo::RESOLVE_DONE; +void CPretestOnePortInfo::add_src(uint32_t ip, uint16_t vlan, MacAddress mac) { + COneIPv4Info *one_ip = new COneIPv4Info(ip, vlan, mac); + assert(one_ip); + m_src_info.push_back(one_ip); } -void CPretestPortInfo::dump(FILE *fd) { - if (m_state == INIT_NEEDED) { - return; - } +void CPretestOnePortInfo::add_dst(uint32_t ip, uint16_t vlan) { + MacAddress default_mac; + COneIPv4Info *one_ip = new COneIPv4Info(ip, vlan, default_mac); + assert(one_ip); + m_dst_info.push_back(one_ip); + m_state = RESOLVE_NEEDED; +} - uint32_t ip = htonl(m_ip); +void CPretestOnePortInfo::add_src(uint16_t ip[8], uint16_t vlan, MacAddress mac) { + COneIPv6Info *one_ip = new COneIPv6Info(ip, vlan, mac); + assert(one_ip); + m_src_info.push_back(one_ip); +} + +void CPretestOnePortInfo::add_dst(uint16_t ip[8], uint16_t vlan) { + MacAddress default_mac; + COneIPv6Info *one_ip = new COneIPv6Info(ip, vlan, default_mac); + assert(one_ip); + m_dst_info.push_back(one_ip); + m_state = RESOLVE_NEEDED; +} - fprintf(fd, " ip:%d.%d.%d.%d", ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF); - ip = htonl(m_def_gw); - fprintf(fd, " default gw:%d.%d.%d.%d\n", ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF); +void CPretestOnePortInfo::dump(FILE *fd, char *offset) { + std::string new_offset = std::string(offset) + " "; - printf(" src MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", m_src_mac[0], m_src_mac[1], m_src_mac[2], m_src_mac[3] - , m_src_mac[4], m_src_mac[5]); - printf( " dst MAC: "); - if (m_state == RESOLVE_DONE) { - printf("%02x:%02x:%02x:%02x:%02x:%02x\n", m_dst_mac[0], m_dst_mac[1], m_dst_mac[2], m_dst_mac[3] - , m_dst_mac[4], m_dst_mac[5]); - } else { - printf("Not resolved\n"); + if (m_is_loopback) { + fprintf(fd, "%sPort connected in loopback\n", offset); + } + fprintf(fd, "%sSources:\n", offset); + for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) { + (*it)->dump(fd, new_offset.c_str()); + } + fprintf(fd, "%sDestinations:\n", offset); + for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) { + (*it)->dump(fd, new_offset.c_str()); } } /* - put in mac relevant dest MAC for port/ip pair. - return false if no relevant info exists, true otherwise. + * Get appropriate source for given vlan and ip version. */ -bool CPretest::get_mac(uint16_t port_id, uint32_t ip, uint8_t *mac) { - assert(port_id < TREX_MAX_PORTS); +COneIPInfo *CPretestOnePortInfo::get_src(uint16_t vlan, uint8_t ip_ver) { + for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) { + if ((ip_ver == (*it)->ip_ver()) && (vlan == (*it)->get_vlan())) + return (*it); + } + + return NULL; +} + +COneIPv4Info *CPretestOnePortInfo::find_ip(uint32_t ip, uint16_t vlan) { + for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) { + if (((*it)->ip_ver() == COneIPInfo::IP4_VER) && ((*it)->get_vlan() == vlan) && (((COneIPv4Info *)(*it))->get_ip() == ip)) + return (COneIPv4Info *) *it; + } + + return NULL; +} + +COneIPv4Info *CPretestOnePortInfo::find_next_hop(uint32_t ip, uint16_t vlan) { + + for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) { + if (((*it)->ip_ver() == COneIPInfo::IP4_VER) && ((*it)->get_vlan() == vlan) && (((COneIPv4Info *)(*it))->get_ip() == ip)) + return (COneIPv4Info *) *it; + } + + return NULL; +} + +COneIPv6Info *CPretestOnePortInfo::find_ipv6(uint16_t ip[8], uint16_t vlan) { + for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) { + if (((*it)->ip_ver() == COneIPInfo::IP6_VER) && ((*it)->get_vlan() == vlan) + && (! memcmp((uint8_t *) ((COneIPv6Info *) (*it))->get_ipv6(), (uint8_t *)ip, 2*8 /* ???*/ ) ) ) + return (COneIPv6Info *) *it; + } + + return NULL; +} + +bool CPretestOnePortInfo::get_mac(COneIPInfo *ip, uint8_t *mac) { + for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) { + if (ip->ip_ver() != (*it)->ip_ver()) + continue; + + switch(ip->ip_ver()) { + case 4: + if (*((COneIPv4Info *) (*it)) != *((COneIPv4Info *) ip)) + continue; + break; + case 6: + if (*((COneIPv6Info *) (*it)) != *((COneIPv6Info *) ip)) + continue; + break; + default: + assert(0); + } + + (*it)->get_mac(mac); + return true; + } - if (m_port_info[port_id].m_state != CPretestPortInfo::RESOLVE_DONE) { + return false; +} + +bool CPretestOnePortInfo::get_mac(uint32_t ip, uint16_t vlan, uint8_t *mac) { + COneIPv4Info one_ip(ip, vlan); + + return get_mac(&one_ip, mac); +} + +bool CPretestOnePortInfo::get_mac(uint16_t ip[8], uint16_t vlan, uint8_t *mac) { + COneIPv6Info one_ip(ip, vlan); + + return get_mac(&one_ip, mac); +} + +// return true if there are still any addresses to resolve on this port +bool CPretestOnePortInfo::resolve_needed() { + if (m_state == RESOLVE_NOT_NEEDED) return false; + + for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) { + if ((*it)->resolve_needed()) + return true; } - memcpy(mac, &m_port_info[port_id].m_dst_mac, sizeof(m_port_info[port_id].m_dst_mac)); + m_state = RESOLVE_NOT_NEEDED; + return false; +} - return true; +void CPretestOnePortInfo::send_arp_req_all() { + for (std::vector<COneIPInfo *>::iterator it = m_dst_info.begin(); it != m_dst_info.end(); ++it) { + rte_mbuf_t *m[1]; + int num_sent; + int verbose = CGlobalInfo::m_options.preview.getVMode(); + + m[0] = CGlobalInfo::pktmbuf_alloc_small_by_port(m_port_id); + if ( unlikely(m[0] == 0) ) { + fprintf(stderr, "ERROR: Could not allocate mbuf for sending ARP to port:%d\n", m_port_id); + exit(1); + } + + uint8_t *p = (uint8_t *)rte_pktmbuf_append(m[0], (*it)->get_arp_req_len()); + // We need source on the same VLAN of the dest in order to send + COneIPInfo *sip = get_src((*it)->get_vlan(), (*it)->ip_ver()); + if (sip == NULL) { + fprintf(stderr, "Failed finding matching source for - "); + (*it)->dump(stderr); + exit(1); + } + (*it)->fill_arp_req_buf(p, m_port_id, sip); + + if (verbose >= 3) { + fprintf(stdout, "TX ARP request on port %d - " , m_port_id); + (*it)->dump(stdout, ""); + } + + num_sent = rte_eth_tx_burst(m_port_id, 0, m, 1); + if (num_sent < 1) { + fprintf(stderr, "Failed sending ARP to port:%d\n", m_port_id); + exit(1); + } else { + m_stats.m_tx_arp++; + } + } +} + +void CPretestOnePortInfo::send_grat_arp_all() { + for (std::vector<COneIPInfo *>::iterator it = m_src_info.begin(); it != m_src_info.end(); ++it) { + rte_mbuf_t *m[1]; + int num_sent; + int verbose = CGlobalInfo::m_options.preview.getVMode(); + + m[0] = CGlobalInfo::pktmbuf_alloc_small_by_port(m_port_id); + if ( unlikely(m[0] == 0) ) { + fprintf(stderr, "ERROR: Could not allocate mbuf for sending grat ARP on port:%d\n", m_port_id); + exit(1); + } + + uint8_t *p = (uint8_t *)rte_pktmbuf_append(m[0], (*it)->get_grat_arp_len()); + (*it)->fill_grat_arp_buf(p); + + + if (verbose >= 3) { + fprintf(stdout, "TX grat ARP on port %d - " , m_port_id); + (*it)->dump(stdout, ""); + } + + num_sent = rte_eth_tx_burst(m_port_id, 0, m, 1); + if (num_sent < 1) { + fprintf(stderr, "Failed sending grat ARP on port:%d\n", m_port_id); + exit(1); + } else { + m_stats.m_tx_arp++; + } + } +} + +// IPv4 functions +void CPretest::add_ip(uint16_t port, uint32_t ip, uint16_t vlan, MacAddress src_mac) { + assert(port < m_max_ports); + m_port_info[port].add_src(ip, vlan, src_mac); +} + +void CPretest::add_ip(uint16_t port, uint32_t ip, MacAddress src_mac) { + assert(port < m_max_ports); + add_ip(port, ip, 0, src_mac); +} + +void CPretest::add_next_hop(uint16_t port, uint32_t ip, uint16_t vlan) { + assert(port < m_max_ports); + m_port_info[port].add_dst(ip, vlan); +} + +void CPretest::add_next_hop(uint16_t port, uint32_t ip) { + assert(port < m_max_ports); + add_next_hop(port, ip, 0); +} + +// IPv6 functions +void CPretest::add_ip(uint16_t port, uint16_t ip[8], uint16_t vlan, MacAddress src_mac) { + assert(port < m_max_ports); + m_port_info[port].add_src(ip, vlan, src_mac); +} + +void CPretest::add_ip(uint16_t port, uint16_t ip[8], MacAddress src_mac) { + assert(port < m_max_ports); + add_ip(port, ip, 0, src_mac); +} + +void CPretest::add_next_hop(uint16_t port, uint16_t ip[8], uint16_t vlan) { + assert(port < m_max_ports); + m_port_info[port].add_dst(ip, vlan); +} + +void CPretest::add_next_hop(uint16_t port, uint16_t ip[8]) { + assert(port < m_max_ports); + add_next_hop(port, ip, 0); +} + +// put in mac, the relevant mac address for the tupple port_id, ip, vlan +bool CPretest::get_mac(uint16_t port_id, uint32_t ip, uint16_t vlan, uint8_t *mac) { + assert(port_id < m_max_ports); + + return m_port_info[port_id].get_mac(ip, vlan, mac); +} + +// IPv6 version of above +bool CPretest::get_mac(uint16_t port_id, uint16_t ip[8], uint16_t vlan, uint8_t *mac) { + assert(port_id < m_max_ports); + + return m_port_info[port_id].get_mac(ip, vlan, mac); } CPreTestStats CPretest::get_stats(uint16_t port_id) { - assert(port_id < TREX_MAX_PORTS); + assert(port_id < m_max_ports); - return m_port_info[port_id].m_stats; + return m_port_info[port_id].get_stats(); } bool CPretest::is_loopback(uint16_t port) { - assert(port < TREX_MAX_PORTS); + assert(port < m_max_ports); - return m_port_info[port].m_is_loopback; + return m_port_info[port].is_loopback(); } -void CPretest::set_port_params(uint16_t port_id, const CPerPortIPCfg &port_cfg, const uint8_t *src_mac, bool resolve_needed) { - if (port_id >= m_max_ports) - return; +bool CPretest::resolve_all() { + uint16_t port; + + // send ARP request on all ports + for (port = 0; port < m_max_ports; port++) { + m_port_info[port].send_arp_req_all(); + } - m_port_info[port_id].set_params(port_cfg, src_mac, resolve_needed); + int max_tries = 1000; + int i; + for (i = 0; i < max_tries; i++) { + bool all_resolved = true; + for (port = 0; port < m_max_ports; port++) { + if (m_port_info[port].resolve_needed()) { + // We need to stop reading packets only if all ports are resolved. + // If we are on loopback, We might get requests on port even after it is in RESOLVE_DONE state + all_resolved = false; + } + handle_rx(port, MAIN_DPDK_DATA_Q); + if (! CGlobalInfo::m_options.preview.get_vm_one_queue_enable()) + handle_rx(port, MAIN_DPDK_RX_Q); + } + if (all_resolved) { + break; + } else { + delay(1); + } + } + + if (i == max_tries) { + return false; + } else { + return true; + } + + return true; +} + +void CPretest::send_arp_req_all() { + for (uint16_t port = 0; port < m_max_ports; port++) { + m_port_info[port].send_arp_req_all(); + } } +void CPretest::send_grat_arp_all() { + for (uint16_t port = 0; port < m_max_ports; port++) { + m_port_info[port].send_grat_arp_all(); + } +} + +bool CPretest::is_arp(const uint8_t *p, uint16_t pkt_size, ArpHdr *&arp, uint16_t &vlan_tag) { + EthernetHeader *m_ether = (EthernetHeader *)p; + vlan_tag = 0; + + if ((pkt_size < 60) || + ((m_ether->getNextProtocol() != EthernetHeader::Protocol::ARP) + && (m_ether->getNextProtocol() != EthernetHeader::Protocol::VLAN))) + return false; + + if (m_ether->getNextProtocol() == EthernetHeader::Protocol::ARP) { + arp = (ArpHdr *)(p + 14); + } else { + if (m_ether->getVlanProtocol() != EthernetHeader::Protocol::ARP) { + return false; + } else { + vlan_tag = m_ether->getVlanTag(); + arp = (ArpHdr *)(p + 18); + } + } + + return true; +} int CPretest::handle_rx(int port_id, int queue_id) { rte_mbuf_t * rx_pkts[32]; @@ -121,72 +397,89 @@ int CPretest::handle_rx(int port_id, int queue_id) { int pkt_size = rte_pktmbuf_pkt_len(m); uint8_t *p = rte_pktmbuf_mtod(m, uint8_t *); ArpHdr *arp; - CPretestPortInfo *port = &m_port_info[port_id]; - if (is_arp(p, pkt_size, arp)) { - m_port_info[port_id].m_stats.m_rx_arp++; + uint16_t vlan_tag; + CPretestOnePortInfo *port = &m_port_info[port_id]; + if (is_arp(p, pkt_size, arp, vlan_tag)) { + port->m_stats.m_rx_arp++; if (arp->m_arp_op == htons(ArpHdr::ARP_HDR_OP_REQUEST)) { if (verbose >= 3) { - fprintf(stdout, "RX ARP request on port %d queue %d sip:0x%08x tip:0x%08x\n" - , port_id, queue_id, ntohl(arp->m_arp_sip), ntohl(arp->m_arp_tip)); + bool is_grat = false; + if (arp->m_arp_sip == arp->m_arp_tip) { + is_grat = true; + } + fprintf(stdout, "RX %s on port %d queue %d sip:%s tip:%s vlan:%d\n" + , is_grat ? "grat ARP" : "ARP request" + , port_id, queue_id + , ip_to_str(ntohl(arp->m_arp_sip)).c_str() + , ip_to_str(ntohl(arp->m_arp_tip)).c_str() + , vlan_tag); } // is this request for our IP? - if (ntohl(arp->m_arp_tip) == port->m_ip) { + COneIPv4Info *src_addr; + COneIPv4Info *rcv_addr; + if ((src_addr = port->find_ip(ntohl(arp->m_arp_tip), vlan_tag))) { // If our request(i.e. we are connected in loopback) // , do a shortcut, and write info directly to asking port uint8_t magic[5] = {0x1, 0x3, 0x5, 0x7, 0x9}; if (! memcmp((uint8_t *)&arp->m_arp_tha.data, magic, 5)) { uint8_t sent_port_id = arp->m_arp_tha.data[5]; if ((sent_port_id < m_max_ports) && - (m_port_info[sent_port_id].m_def_gw == port->m_ip)) { - memcpy(m_port_info[sent_port_id].m_dst_mac, port->m_src_mac, ETHER_ADDR_LEN); - m_port_info[sent_port_id].m_state = CPretestPortInfo::RESOLVE_DONE; + (rcv_addr = m_port_info[sent_port_id].find_next_hop(ntohl(arp->m_arp_tip), vlan_tag))) { + uint8_t mac[ETHER_ADDR_LEN]; + src_addr->get_mac(mac); + rcv_addr->set_mac(mac); + port->m_is_loopback = true; m_port_info[sent_port_id].m_is_loopback = true; } } else { // Not our request. Answer. + uint8_t src_mac[ETHER_ADDR_LEN]; free_pkt = false; // We use the same mbuf to send response. Don't free it twice. arp->m_arp_op = htons(ArpHdr::ARP_HDR_OP_REPLY); uint32_t tmp_ip = arp->m_arp_sip; arp->m_arp_sip = arp->m_arp_tip; arp->m_arp_tip = tmp_ip; memcpy((uint8_t *)&arp->m_arp_tha, (uint8_t *)&arp->m_arp_sha, ETHER_ADDR_LEN); - memcpy((uint8_t *)&arp->m_arp_sha, port->m_src_mac, ETHER_ADDR_LEN); + src_addr->get_mac(src_mac); + memcpy((uint8_t *)&arp->m_arp_sha, src_mac, ETHER_ADDR_LEN); EthernetHeader *m_ether = (EthernetHeader *)p; memcpy((uint8_t *)&m_ether->myDestination, (uint8_t *)&m_ether->mySource, ETHER_ADDR_LEN); - memcpy((uint8_t *)&m_ether->mySource, (uint8_t *)port->m_src_mac, ETHER_ADDR_LEN); + memcpy((uint8_t *)&m_ether->mySource, src_mac, ETHER_ADDR_LEN); int num_sent = rte_eth_tx_burst(port_id, 0, &m, 1); if (num_sent < 1) { fprintf(stderr, "Failed sending ARP reply to port:%d\n", port_id); rte_pktmbuf_free(m); } else { - fprintf(stdout, "TX ARP reply on port:%d sip:0x%08x, tip:0x%08x\n" - , port_id ,htonl(arp->m_arp_sip), htonl(arp->m_arp_tip)); + if (verbose >= 3) { + fprintf(stdout, "TX ARP reply on port:%d sip:%s, tip:%s\n" + , port_id + , ip_to_str(ntohl(arp->m_arp_sip)).c_str() + , ip_to_str(ntohl(arp->m_arp_tip)).c_str()); + + } m_port_info[port_id].m_stats.m_tx_arp++; } } } else { - // ARP request not to our IP. - if ((ntohl(arp->m_arp_tip) == port->m_def_gw) && (ntohl(arp->m_arp_sip) == port->m_def_gw)) { - // sip and tip equals def_gw, meaning we got gratitues ARP. - port->set_dst_mac((uint8_t *)&arp->m_arp_sha); + // ARP request not to our IP. Check if this is gratitues ARP for something we need. + if ((arp->m_arp_tip == arp->m_arp_sip) + && (rcv_addr = port->find_next_hop(ntohl(arp->m_arp_tip), vlan_tag))) { + rcv_addr->set_mac((uint8_t *)&arp->m_arp_sha); } } } else { if (arp->m_arp_op == htons(ArpHdr::ARP_HDR_OP_REPLY)) { if (verbose >= 3) { - bool is_grat = false; - if (arp->m_arp_sip == arp->m_arp_tip) { - is_grat = true; - } - fprintf(stdout, "RX %s on port %d queue %d sip:0x%08x tip:0x%08x\n" - , is_grat ? "grat ARP" : "ARP reply" + fprintf(stdout, "RX ARP reply on port %d queue %d sip:%s tip:%s\n" , port_id, queue_id - , ntohl(arp->m_arp_sip) - , ntohl(arp->m_arp_tip)); + , ip_to_str(ntohl(arp->m_arp_sip)).c_str() + , ip_to_str(ntohl(arp->m_arp_tip)).c_str()); } + // If this is response to our request, update our tables - if (port->m_def_gw == ntohl(arp->m_arp_sip)) { - port->set_dst_mac((uint8_t *)&arp->m_arp_sha); + COneIPv4Info *addr; + if ((addr = port->find_next_hop(ntohl(arp->m_arp_sip), vlan_tag))) { + addr->set_mac((uint8_t *)&arp->m_arp_sha); } } } @@ -199,160 +492,86 @@ int CPretest::handle_rx(int port_id, int queue_id) { return 0; } -/* - Try to resolve def_gw address on all ports marked as needed. - Return false if failed to resolve on one of the ports - */ -bool CPretest::resolve_all() { - uint16_t port; - - // send ARP request on all ports - for (port = 0; port < m_max_ports; port++) { - if (m_port_info[port].m_state == CPretestPortInfo::RESOLVE_NEEDED) { - send_arp_req(port, false); - } - } - - int max_tries = 1000; - int i; - for (i = 0; i < max_tries; i++) { - bool all_resolved = true; - for (port = 0; port < m_max_ports; port++) { - if (m_port_info[port].m_state == CPretestPortInfo::RESOLVE_NEEDED) { - // We need to stop reading packets only if all ports are resolved. - // If we are on loopback, We might get requests on port even after it is in RESOLVE_DONE state - all_resolved = false; +void CPretest::get_results(CManyIPInfo &resolved_ips) { + for (int port = 0; port < m_max_ports; port++) { + for (std::vector<COneIPInfo *>::iterator it = m_port_info[port].m_dst_info.begin() + ; it != m_port_info[port].m_dst_info.end(); ++it) { + uint8_t ip_type = (*it)->ip_ver(); + switch(ip_type) { + case COneIPInfo::IP4_VER: + resolved_ips.insert(*(COneIPv4Info *)(*it)); + break; +#if 0 + //??? fix for ipv6 + case COneIPInfo::IP6_VER: + ipv6_tmp = (uint8_t *)((COneIPv6Info *)(*it))->get_ipv6(); + memcpy((uint8_t *)ipv6, (uint8_t *)ipv6_tmp, 16); + v6_list.insert(std::pair<std::pair<uint16_t[8], uint16_t>, COneIPv6Info> + (std::pair<uint16_t[8], uint16_t>(ipv6, vlan), *(COneIPv6Info *)(*it))); + break; +#endif + default: + break; } - handle_rx(port, MAIN_DPDK_DATA_Q); - if (! CGlobalInfo::m_options.preview.get_vm_one_queue_enable()) - handle_rx(port, MAIN_DPDK_RX_Q); - } - if (all_resolved) { - break; - } else { - delay(1); } } - - if (i == max_tries) { - return false; - } else { - return true; - } } void CPretest::dump(FILE *fd) { + fprintf(fd, "Pre test info start ===================\n"); for (int port = 0; port < m_max_ports; port++) { - if (m_port_info[port].m_state != CPretestPortInfo::INIT_NEEDED) { - fprintf(fd, "port %d:\n", port); - m_port_info[port].dump(fd); - } - } -} - -// Send ARP request for our default gateway on port -// If is_grat is true - send gratuitous ARP. -void CPretest::send_arp_req(uint16_t port_id, bool is_grat) { - rte_mbuf_t *m[1]; - int num_sent; - int verbose = CGlobalInfo::m_options.preview.getVMode(); - - m[0] = CGlobalInfo::pktmbuf_alloc_small_by_port(port_id); - if ( unlikely(m[0] == 0) ) { - fprintf(stderr, "ERROR: Could not allocate mbuf for sending ARP to port:%d\n", port_id); - exit(1); - } - - uint32_t tip; - uint8_t *p = (uint8_t *)rte_pktmbuf_append(m[0], 60); // ARP packet is shorter than 60 - uint32_t sip = m_port_info[port_id].m_ip; - uint8_t *src_mac = m_port_info[port_id].m_src_mac; - uint16_t vlan = m_port_info[port_id].m_vlan; - if (is_grat) { - tip = sip; - } else { - tip = m_port_info[port_id].m_def_gw; - } - - if (verbose >= 3) { - fprintf(stdout, "TX %s port:%d sip:0x%08x, tip:0x%08x\n" - , is_grat ? "grat ARP": "ARP request", port_id ,sip, tip); - } - - CTestPktGen::create_arp_req(p, sip, tip, src_mac, vlan, port_id); - num_sent = rte_eth_tx_burst(port_id, 0, m, 1); - if (num_sent < 1) { - fprintf(stderr, "Failed sending ARP to port:%d\n", port_id); - exit(1); - } else { - m_port_info[port_id].m_stats.m_tx_arp++; + fprintf(fd, "Port %d:\n", port); + m_port_info[port].dump(fd, (char *)" "); } + fprintf(fd, "Pre test info end ===================\n"); } -/* - Send gratuitous ARP on all ports - */ -void CPretest::send_grat_arp_all() { - for (uint16_t port = 0; port < m_max_ports; port++) { - if (m_port_info[port].m_state == CPretestPortInfo::RESOLVE_NEEDED) { - send_arp_req(port, true); - } - } -} - -bool CPretest::is_arp(const uint8_t *p, uint16_t pkt_size, ArpHdr *&arp) { - EthernetHeader *m_ether = (EthernetHeader *)p; - - if ((pkt_size < 60) || - ((m_ether->getNextProtocol() != EthernetHeader::Protocol::ARP) - && (m_ether->getNextProtocol() != EthernetHeader::Protocol::VLAN))) - return false; - - if (m_ether->getNextProtocol() == EthernetHeader::Protocol::ARP) { - arp = (ArpHdr *)(p + 14); - } else { - if (m_ether->getVlanProtocol() != EthernetHeader::Protocol::ARP) { - return false; - } else { - arp = (ArpHdr *)(p + 18); - } - } - - return true; -} - -// Should be run on setup with two interfaces connected by loopback. -// Before running, should put ports on receive all mode. void CPretest::test() { uint8_t found_mac[ETHER_ADDR_LEN]; uint8_t mac0[ETHER_ADDR_LEN] = {0x90, 0xe2, 0xba, 0xae, 0x87, 0xd0}; uint8_t mac1[ETHER_ADDR_LEN] = {0x90, 0xe2, 0xba, 0xae, 0x87, 0xd1}; - uint32_t ip0 = 0x0f000003; - uint32_t ip1 = 0x0f000001; - - CPerPortIPCfg port_cfg0; - CPerPortIPCfg port_cfg1; - port_cfg0.set_ip(ip0); - port_cfg0.set_def_gw(ip1); - port_cfg0.set_vlan(0); - port_cfg1.set_ip(ip1); - port_cfg1.set_def_gw(ip0); - port_cfg1.set_vlan(0); - - set_port_params(0, port_cfg0, mac0, true); - set_port_params(1, port_cfg1, mac1, true); + uint8_t mac2[ETHER_ADDR_LEN] = {0x90, 0xe2, 0xba, 0xae, 0x87, 0xd2}; + uint32_t ip0 = 0x0f000002; + uint32_t ip01 = 0x0f000003; + uint32_t ip1 = 0x0f000001; + uint16_t ipv6_0[8] = {0x1234, 0x5678, 0xabcd, 0x0, 0x0, 0x0, 0x1111, 0x2220}; + uint16_t ipv6_1[8] = {0x1234, 0x5678, 0xabcd, 0x0, 0x0, 0x0, 0x1111, 0x2221}; + uint16_t vlan=1; + uint8_t port_0 = 0; + uint8_t port_1 = 3; + + add_ip(port_0, ip0, vlan, mac0); + add_ip(port_0, ip01, vlan, mac1); + add_ip(port_0, ipv6_0, vlan, mac1); + add_next_hop(port_0, ip1, vlan); + add_next_hop(port_0, ipv6_1, vlan); + + add_ip(port_1, ip1, vlan, mac2); + add_ip(port_1, ipv6_1, vlan, mac2); + add_next_hop(port_1, ip0, vlan); + add_next_hop(port_1, ip01, vlan); + add_next_hop(port_1, ipv6_0, vlan); + dump(stdout); + send_grat_arp_all(); resolve_all(); dump(stdout); - get_mac(0, ip1, found_mac); - if (memcmp(found_mac, mac1, ETHER_ADDR_LEN)) { - fprintf(stderr, "Test failed: Could not resolve def gw on port 0\n"); + + if (!get_mac(port_0, ip1, vlan, found_mac)) { + fprintf(stderr, "Test failed: Could not find %x on port %d\n", ip1, port_0); + exit(1); + } + if (memcmp(found_mac, mac2, ETHER_ADDR_LEN)) { + fprintf(stderr, "Test failed: dest %x on port %d badly resolved\n", ip1, port_0); exit(1); } - get_mac(1, ip0, found_mac); + if (!get_mac(port_1, ip0, vlan, found_mac)) { + fprintf(stderr, "Test failed: Could not find %x on port %d\n", ip0, port_1); + exit(1); + } if (memcmp(found_mac, mac0, ETHER_ADDR_LEN)) { - fprintf(stderr, "Test failed: Could not resolve def gw on port 1\n"); + fprintf(stderr, "Test failed: dest %x on port %d badly resolved\n", ip0, port_1); exit(1); } diff --git a/src/pre_test.h b/src/pre_test.h index ad7608a6..9573ff02 100644 --- a/src/pre_test.h +++ b/src/pre_test.h @@ -24,6 +24,7 @@ #include <iostream> #include <common/Network/Packet/Arp.h> +#include <common/Network/Packet/MacAddress.h> #include "bp_sim.h" #include "trex_defs.h" @@ -39,60 +40,79 @@ class CPreTestStats { } }; -class CPretestPortInfo { +class CPretestOnePortInfo { friend class CPretest; - - private: - enum CPretestPortInfoStates { - INIT_NEEDED, + enum CPretestOnePortInfoStates { RESOLVE_NEEDED, - RESOLVE_DONE, RESOLVE_NOT_NEEDED, }; - CPretestPortInfo() { - m_state = INIT_NEEDED; - m_is_loopback = false; - m_stats.clear(); - } - void dump(FILE *fd); - uint8_t *create_arp_req(uint16_t &pkt_size, uint8_t port, bool is_grat); - void set_params(CPerPortIPCfg port_cfg, const uint8_t *src_mac, bool resolve_needed); - void set_dst_mac(const uint8_t *dst_mac); - + public: + CPretestOnePortInfo(); + void add_src(uint32_t ip, uint16_t vlan, MacAddress mac); + void add_dst(uint32_t ip, uint16_t vlan); + void add_src(uint16_t ip[8], uint16_t vlan, MacAddress mac); + void add_dst(uint16_t ip[8], uint16_t vlan); + bool get_mac(uint32_t ip, uint16_t vlan, uint8_t *mac); + bool get_mac(uint16_t ip[8], uint16_t vlan, uint8_t *mac); + bool get_mac(COneIPInfo *ip, uint8_t *mac); + COneIPInfo *get_src(uint16_t vlan, uint8_t ip_ver); + void set_port_id(uint16_t port_id) {m_port_id = port_id;} + void dump(FILE *fd, char *offset); + bool is_loopback() {return m_is_loopback;} + CPreTestStats get_stats() {return m_stats;} + bool resolve_needed(); + void send_grat_arp_all(); + void send_arp_req_all(); + + private: + COneIPv4Info *find_ip(uint32_t ip, uint16_t vlan); + COneIPv4Info *find_next_hop(uint32_t ip, uint16_t vlan); + COneIPv6Info *find_ipv6(uint16_t *ip, uint16_t vlan); + bool get_mac(COneIPInfo *ip, uint16_t vlan, uint8_t *mac, uint8_t ip_ver); + private: - uint32_t m_ip; - uint32_t m_def_gw; - uint16_t m_vlan; - uint8_t m_src_mac[6]; - uint8_t m_dst_mac[6]; - enum CPretestPortInfoStates m_state; bool m_is_loopback; + CPretestOnePortInfoStates m_state; CPreTestStats m_stats; + uint16_t m_port_id; + std::vector<COneIPInfo *> m_src_info; + std::vector<COneIPInfo *> m_dst_info; }; - class CPretest { public: CPretest(uint16_t max_ports) { m_max_ports = max_ports; + for (int i =0; i < max_ports; i++) { + m_port_info[i].set_port_id(i); + } } - bool get_mac(uint16_t port, uint32_t ip, uint8_t *mac); + void add_ip(uint16_t port, uint32_t ip, uint16_t vlan, MacAddress src_mac); + void add_ip(uint16_t port, uint32_t ip, MacAddress src_mac); + void add_next_hop(uint16_t port, uint32_t ip, uint16_t vlan); + void add_next_hop(uint16_t port, uint32_t ip); + void add_ip(uint16_t port, uint16_t ip[8], uint16_t vlan, MacAddress src_mac); + void add_ip(uint16_t port, uint16_t ip[8], MacAddress src_mac); + void add_next_hop(uint16_t port, uint16_t ip[8], uint16_t vlan); + void add_next_hop(uint16_t port, uint16_t ip[8]); + bool get_mac(uint16_t port, uint32_t ip, uint16_t vlan, uint8_t *mac); + bool get_mac(uint16_t port, uint16_t ip[8], uint16_t vlan, uint8_t *mac); CPreTestStats get_stats(uint16_t port_id); bool is_loopback(uint16_t port); - void set_port_params(uint16_t port_id, const CPerPortIPCfg &port_cfg, const uint8_t *src_mac, bool resolve_needed); bool resolve_all(); - void send_arp_req(uint16_t port, bool is_grat); + void send_arp_req_all(); void send_grat_arp_all(); - bool is_arp(const uint8_t *p, uint16_t pkt_size, ArpHdr *&arp); + bool is_arp(const uint8_t *p, uint16_t pkt_size, ArpHdr *&arp, uint16_t &vlan_tag); + void get_results(CManyIPInfo &resolved_ips); void dump(FILE *fd); void test(); - + private: int handle_rx(int port, int queue_id); private: - CPretestPortInfo m_port_info[TREX_MAX_PORTS]; + CPretestOnePortInfo m_port_info[TREX_MAX_PORTS]; uint16_t m_max_ports; }; diff --git a/src/stateful_rx_core.cpp b/src/stateful_rx_core.cpp index cbf62a17..7ee802df 100644 --- a/src/stateful_rx_core.cpp +++ b/src/stateful_rx_core.cpp @@ -588,32 +588,56 @@ void CLatencyManager::send_pkt_all_ports(){ } } -void CLatencyManager::send_grat_arp_all_ports() { - for (int port_id = 0; port_id < m_max_ports; port_id++) { - if (! CGlobalInfo::m_options.m_ip_cfg[port_id].grat_arp_needed()) - continue; - - CLatencyManagerPerPort * lp = &m_ports[port_id]; - rte_mbuf_t *m = CGlobalInfo::pktmbuf_alloc_small(CGlobalInfo::m_socket.port_to_socket(port_id)); - assert(m); - uint8_t *p = (uint8_t *)rte_pktmbuf_append(m, 60); // ARP packet is shorter than 60 - uint32_t sip = CGlobalInfo::m_options.m_ip_cfg[port_id].get_ip(); - uint8_t *src_mac = CGlobalInfo::m_options.m_mac_addr[port_id].u.m_mac.src; - uint16_t vlan = CGlobalInfo::m_options.m_ip_cfg[port_id].get_vlan(); - // gratuitous ARP. Requested IP is our source. - CTestPktGen::create_arp_req(p, sip, sip, src_mac, vlan, port_id); +double CLatencyManager::grat_arp_timeout() { + return (double)CGlobalInfo::m_options.m_arp_ref_per / m_arp_info.size(); +} + +void CLatencyManager::add_grat_arp_src(COneIPv4Info &ip) { + m_arp_info.insert(ip); +} + +void CLatencyManager::send_one_grat_arp() { + const COneIPInfo *ip_info; + uint16_t port_id; + CLatencyManagerPerPort * lp; + rte_mbuf_t *m; + uint8_t src_mac[ETHER_ADDR_LEN]; + uint16_t vlan; + uint32_t sip; + + ip_info = m_arp_info.get_next(); + if (!ip_info) + ip_info = m_arp_info.get_next(); + // Two times NULL means there are no addresses + if (!ip_info) + return; + port_id = ip_info->get_port(); + lp = &m_ports[port_id]; + m = CGlobalInfo::pktmbuf_alloc_small(CGlobalInfo::m_socket.port_to_socket(port_id)); + assert(m); + uint8_t *p = (uint8_t *)rte_pktmbuf_append(m, ip_info->get_grat_arp_len()); + ip_info->get_mac(src_mac); + vlan = ip_info->get_vlan(); + switch(ip_info->ip_ver()) { + case COneIPInfo::IP4_VER: + sip = ((COneIPv4Info *)ip_info)->get_ip(); + CTestPktGen::create_arp_req(p, sip, sip, src_mac, vlan, port_id); if (CGlobalInfo::m_options.preview.getVMode() >= 3) { - printf("Sending gratuitous ARP on port %d vlan:%d, sip:0x%08x\n", port_id, vlan, sip); + printf("Sending gratuitous ARP on port %d vlan:%d, sip:%s\n", port_id, vlan + , ip_to_str(sip).c_str()); utl_DumpBuffer(stdout, p, 60, 0); } - if ( lp->m_io->tx(m) == 0 ) { lp->m_port.m_ign_stats.m_tx_arp++; lp->m_port.m_ign_stats.m_tot_bytes += 64; // mbuf size is smaller, but 64 bytes will be sent } else { lp->m_port.m_tx_pkt_err++; } + break; + case COneIPInfo::IP6_VER: + //??? implement ipv6 + break; } } @@ -815,9 +839,9 @@ void CLatencyManager::start(int iter, bool activate_watchdog) { case CGenNode::GRAT_ARP: m_cpu_dp_u.start_work1(); - send_grat_arp_all_ports(); + send_one_grat_arp(); m_p_queue.pop(); - node->m_time += (double)CGlobalInfo::m_options.m_arp_ref_per; + node->m_time += grat_arp_timeout(); m_p_queue.push(node); m_cpu_dp_u.commit1(); break; diff --git a/src/stateful_rx_core.h b/src/stateful_rx_core.h index 3fa5892f..48fbeb97 100644 --- a/src/stateful_rx_core.h +++ b/src/stateful_rx_core.h @@ -24,6 +24,7 @@ limitations under the License. #include "bp_sim.h" #include "flow_stat.h" +#include "utl_ip.h" #define L_PKT_SUBMODE_NO_REPLY 1 #define L_PKT_SUBMODE_REPLY 2 @@ -346,11 +347,13 @@ public: return ( &m_nat_check_manager ); } CLatencyPktMode *c_l_pkt_mode; + void add_grat_arp_src(COneIPv4Info &ip); private: void tickle(); void send_pkt_all_ports(); - void send_grat_arp_all_ports(); + double grat_arp_timeout(); + void send_one_grat_arp(); void try_rx(); void try_rx_queues(); void run_rx_queue_msgs(uint8_t thread_id, CNodeRing * r); @@ -374,7 +377,7 @@ private: CCpuUtlDp m_cpu_dp_u; CCpuUtlCp m_cpu_cp_u; TrexMonitor m_monitor; - + CManyIPInfo m_arp_info; // for grat ARP volatile bool m_do_stop __rte_cache_aligned ; }; diff --git a/src/trex_client_config.cpp b/src/trex_client_config.cpp index ad71d38e..548d097b 100644 --- a/src/trex_client_config.cpp +++ b/src/trex_client_config.cpp @@ -30,38 +30,200 @@ limitations under the License. #include "common/basic_utils.h" #include "bp_sim.h" +void ClientCfgDirBase::dump(FILE *fd) const { + if (has_src_mac_addr()) { + fprintf(fd, " src_mac: %s\n", utl_macaddr_to_str(m_src_mac.GetConstBuffer()).c_str()); + } else { + fprintf(fd, "# No src MAC\n"); + } + if (has_dst_mac_addr()) { + fprintf(fd, " dst_mac: %s\n", utl_macaddr_to_str(m_dst_mac.GetConstBuffer()).c_str()); + } else { + fprintf(fd, "# No dst MAC\n"); + } + if (has_vlan()) { + fprintf(fd, " vlan: %d\n", m_vlan); + } else { + fprintf(fd, "# No vlan\n"); + } +} + +void ClientCfgDirBase::update(uint32_t index, const ClientCfgDirExt &cfg) { + if (has_src_mac_addr()) { + m_src_mac += index; + } + + if (has_dst_mac_addr() || cfg.has_next_hop() || cfg.has_ipv6_next_hop()) { + m_dst_mac = cfg.get_resolved_mac(index); + m_bitfield |= HAS_DST_MAC; + } +} + +bool ClientCfgDirExt::need_resolve() const { + if (has_next_hop() || has_ipv6_next_hop()) + return true; + else + return false; +} + +void ClientCfgDirExt::set_no_resolve_needed() { + m_bitfield &= ~(HAS_DST_MAC | HAS_IPV6_NEXT_HOP | HAS_NEXT_HOP); +} + +void ClientCfgDirExt::dump(FILE *fd) const { + ClientCfgDirBase::dump(fd); + + if (has_next_hop()) { + fprintf(fd, " next_hop: %s\n", ip_to_str(m_next_hop).c_str()); + } else { + fprintf(fd, "# No next hop\n"); + } + if (has_ipv6_next_hop()) { + fprintf(fd, " ipv6_next_hop: %s\n", ip_to_str((unsigned char *)m_ipv6_next_hop).c_str()); + } else { + fprintf(fd, "# No IPv6 next hop\n"); + } + + if (m_resolved_macs.size() > 0) { + fprintf(fd, "# Resolved MAC list:\n"); + for (int i = 0; i < m_resolved_macs.size(); i++) { + fprintf(fd, "# %s\n", utl_macaddr_to_str(m_resolved_macs[i].GetConstBuffer()).c_str()); + } + } +} + +void ClientCfgDirExt::set_resolved_macs(CManyIPInfo &pretest_result, uint16_t count) { + uint16_t vlan = has_vlan() ? m_vlan : 0; + MacAddress base_mac = m_dst_mac; + m_resolved_macs.resize(count); + + for (int i = 0; i < count; i++) { + if (need_resolve()) { + if (has_next_hop()) { + if (!pretest_result.lookup(m_next_hop + i, vlan, m_resolved_macs[i])) { + fprintf(stderr, "Failed resolving ip:%x, vlan:%d - exiting\n", m_next_hop+i, vlan); + exit(1); + } + } else { + //??? handle ipv6 + } + } else { + m_resolved_macs[i] = base_mac; + base_mac += 1; + } + } +} + +void ClientCfgBase::update(uint32_t index, const ClientCfgExt *cfg) { + m_initiator.update(index, cfg->m_initiator); + m_responder.update(index, cfg->m_responder); +} + void -ClientCfgEntry::dump() const { +ClientCfgEntry::dump(FILE *fd) const { - std::cout << "IP start: " << ip_to_str(m_ip_start) << "\n"; - std::cout << "IP end: " << ip_to_str(m_ip_end) << "\n"; + fprintf(fd, "- ip_start : %s\n", ip_to_str(m_ip_start).c_str()); + fprintf(fd, " ip_end : %s\n", ip_to_str(m_ip_end).c_str()); + m_cfg.dump(fd); + fprintf(fd, " count : %d\n", m_count); +} - //m_cfg.dump(); +void ClientCfgEntry::set_resolved_macs(CManyIPInfo &pretest_result) { + m_cfg.m_initiator.set_resolved_macs(pretest_result, m_count); + m_cfg.m_responder.set_resolved_macs(pretest_result, m_count); +} - #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 ) ); +void ClientCfgCompactEntry::fill_from_dir(ClientCfgDirExt cfg, uint8_t port_id) { + m_port = port_id; + if (cfg.has_next_hop()) { + m_next_hop_base.ip = cfg.m_next_hop; + if (cfg.has_src_ip()) { + m_src_ip.ip = cfg.m_src_ip; + } else { + m_src_ip.ip = 0; + } + m_is_ipv4 = true; + } else if (cfg.has_ipv6_next_hop()) { + memcpy(m_next_hop_base.ipv6, cfg.m_ipv6_next_hop, sizeof(m_next_hop_base.ipv6)); + if (cfg.has_src_ipv6()) { + memcpy(m_src_ip.ipv6, cfg.m_src_ipv6, sizeof(m_src_ip.ipv6)); + } else { + memset(m_src_ip.ipv6, 0, sizeof(m_src_ip.ipv6)); + } + m_is_ipv4 = false; } - std::cout << "\n"; - std::cout << "Init. VLAN: " << m_initiator.m_vlan << "\n"; + if (cfg.has_vlan()) { + m_vlan = cfg.m_vlan; + } else { + m_vlan = 0; + } +} - std::cout << "Res. MAC addr: "; - for (int i = 0; i < 6; i++) { - printf("%lx:", ( (m_responder.m_dst_mac >> ( (6-i) * 8)) & 0xFF ) ); +void +ClientCfgDB::dump(FILE *fd) { + //fprintf(fd, "#**********Client config file start*********\n"); + fprintf(fd, "vlan: %s\n", m_under_vlan ? "true" : "false"); + fprintf(fd, "groups:\n"); + + for (std::map<uint32_t, ClientCfgEntry>::iterator it = m_groups.begin(); it != m_groups.end(); ++it) { + fprintf(fd, "# ****%s:****\n", ip_to_str(it->first).c_str()); + ((ClientCfgEntry)it->second).dump(fd); } - std::cout << "\n"; + //fprintf(fd, "#**********Client config end*********\n"); +} - std::cout << "Res. VLAN: " << m_responder.m_vlan << "\n"; - #endif +void ClientCfgDB::set_resolved_macs(CManyIPInfo &pretest_result) { + std::map<uint32_t, ClientCfgEntry>::iterator it; + for (it = m_groups.begin(); it != m_groups.end(); it++) { + ClientCfgEntry &cfg = it->second; + cfg.set_resolved_macs(pretest_result); + } } +void ClientCfgDB::get_entry_list(std::vector<ClientCfgCompactEntry *> &ret) { + uint8_t port; + bool result; + + assert(m_tg != NULL); + for (std::map<uint32_t, ClientCfgEntry>::iterator it = m_groups.begin(); it != m_groups.end(); ++it) { + ClientCfgEntry &cfg = it->second; + if (cfg.m_cfg.m_initiator.need_resolve() || cfg.m_cfg.m_initiator.need_resolve()) { + result = m_tg->find_port(cfg.m_ip_start, cfg.m_ip_end, port); + if (! result) { + fprintf(stderr, "Error in clinet config range %s - %s.\n" + , ip_to_str(cfg.m_ip_start).c_str(), ip_to_str(cfg.m_ip_end).c_str()); + exit(-1); + } + if (port == UINT8_MAX) { + // if port not found, it means this adderss is not needed. Don't try to resolve. + cfg.m_cfg.m_initiator.set_no_resolve_needed(); + cfg.m_cfg.m_responder.set_no_resolve_needed(); + } else { + if (cfg.m_cfg.m_initiator.need_resolve()) { + ClientCfgCompactEntry *init_entry = new ClientCfgCompactEntry(); + assert(init_entry); + init_entry->m_count = cfg.m_count; + init_entry->fill_from_dir(cfg.m_cfg.m_initiator, port); + ret.push_back(init_entry); + } + + if (cfg.m_cfg.m_responder.need_resolve()) { + ClientCfgCompactEntry *resp_entry = new ClientCfgCompactEntry(); + assert(resp_entry); + resp_entry->m_count = cfg.m_count; + resp_entry->fill_from_dir(cfg.m_cfg.m_responder, port + 1); + ret.push_back(resp_entry); + } + } + } + } +} /** - * loads a YAML file containing - * the client groups configuration - * + * loads a YAML file containing + * the client groups configuration + * */ void ClientCfgDB::load_yaml_file(const std::string &filename) { @@ -93,7 +255,7 @@ ClientCfgDB::load_yaml_file(const std::string &filename) { /** * reads a single group of clients from YAML - * + * */ void ClientCfgDB::parse_single_group(YAMLParserWrapper &parser, const YAML::Node &node) { @@ -116,7 +278,7 @@ ClientCfgDB::parse_single_group(YAMLParserWrapper &parser, const YAML::Node &nod 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 */ @@ -124,12 +286,32 @@ ClientCfgDB::parse_single_group(YAMLParserWrapper &parser, const YAML::Node &nod } -void -ClientCfgDB::parse_dir(YAMLParserWrapper &parser, const YAML::Node &node, ClientCfgDir &dir) { +void +ClientCfgDB::parse_dir(YAMLParserWrapper &parser, const YAML::Node &node, ClientCfgDirExt &dir) { + if (node.FindValue("src_ip")) { + dir.set_src_ip(parser.parse_ip(node, "src_ip")); + } + + if (node.FindValue("src_ipv6")) { + uint16_t ip_num[8]; + parser.parse_ipv6(node, "src_ipv6", (unsigned char *)&ip_num); + dir.set_src_ipv6(ip_num); + } + + if (node.FindValue("next_hop")) { + dir.set_next_hop(parser.parse_ip(node, "next_hop")); + } + + if (node.FindValue("ipv6_next_hop")) { + uint16_t ip_num[8]; + parser.parse_ipv6(node, "ipv6_next_hop", (unsigned char *)&ip_num); + dir.set_ipv6_next_hop(ip_num); + } + if (node.FindValue("src_mac")) { dir.set_src_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")); } @@ -141,11 +323,20 @@ ClientCfgDB::parse_dir(YAMLParserWrapper &parser, const YAML::Node &node, Client parser.parse_err("VLAN config was disabled", node["vlan"]); } } + + if ((dir.has_next_hop() || dir.has_ipv6_next_hop()) && (dir.has_dst_mac_addr() || dir.has_src_mac_addr())) { + parser.parse_err("Should not configure both next_hop/ipv6_next_hop and dst_mac or src_mac", node); + } + + if (dir.has_next_hop() && dir.has_ipv6_next_hop()) { + parser.parse_err("Should not configure both next_hop and ipv6_next_hop", node); + } + } /** * sanity checks - * + * * @author imarom (28-Jun-16) */ void @@ -154,7 +345,7 @@ ClientCfgDB::verify(const YAMLParserWrapper &parser) const { 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; @@ -169,13 +360,13 @@ ClientCfgDB::verify(const YAMLParserWrapper &parser) const { } /** - * lookup function - * should be fast - * + * 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; @@ -185,7 +376,7 @@ ClientCfgDB::lookup(uint32_t ip) { m_cache_group = NULL; std::map<uint32_t ,ClientCfgEntry>::iterator it; - + /* upper bound fetchs the first greater element */ it = m_groups.upper_bound(ip); @@ -217,12 +408,12 @@ ClientCfgDB::lookup(uint32_t ip) { /** * for convenience - search by IP as string - * + * * @author imarom (28-Jun-16) - * - * @param ip - * - * @return ClientCfgEntry* + * + * @param ip + * + * @return ClientCfgEntry* */ ClientCfgEntry * ClientCfgDB::lookup(const std::string &ip) { @@ -231,5 +422,3 @@ ClientCfgDB::lookup(const std::string &ip) { return lookup(addr); } - - diff --git a/src/trex_client_config.h b/src/trex_client_config.h index a5bb83b3..257d354f 100644 --- a/src/trex_client_config.h +++ b/src/trex_client_config.h @@ -24,57 +24,56 @@ limitations under the License. #include <stdint.h> #include <string> #include <map> +#include "utl_ip.h" +#include "common/Network/Packet/MacAddress.h" class YAMLParserWrapper; +struct CTupleGenYamlInfo; +class ClientCfgDirExt; - +// To save memory, we use here the ClientCfgExt and ClientCfgDirExt, +// and in tuple_gen the ClientCfgBase and ClientCfgDirBase /** * client configuration per direction - * + * * @author imarom (29-Jun-16) */ -class ClientCfgDir { +class ClientCfgDirBase { + friend class ClientCfgCompactEntry; -private: + protected: enum { - HAS_SRC_MAC = 0x1, - HAS_DST_MAC = 0x2, - HAS_VLAN = 0x4, + HAS_SRC_MAC = 0x1, + HAS_DST_MAC = 0x2, + HAS_VLAN = 0x4, + HAS_NEXT_HOP = 0x8, + HAS_IPV6_NEXT_HOP = 0x10, + HAS_SRC_IP = 0x20, + HAS_SRC_IPV6 = 0x40, }; - uint8_t m_src_mac[6]; - uint8_t m_dst_mac[6]; + MacAddress m_src_mac; + MacAddress m_dst_mac; uint16_t m_vlan; uint8_t m_bitfield; - -public: - ClientCfgDir() { + public: + ClientCfgDirBase() { m_bitfield = 0; } + virtual void dump(FILE *fd) const; 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_src_mac.set(mac_addr); 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_dst_mac.set(mac_addr); m_bitfield |= HAS_DST_MAC; } @@ -83,26 +82,21 @@ public: 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); - } + bool has_dst_mac_addr() const { + return (m_bitfield & HAS_DST_MAC); + } + bool has_vlan() const { + return (m_bitfield & HAS_VLAN); } const uint8_t *get_src_mac_addr() const { assert(has_src_mac_addr()); - return m_src_mac; + return m_src_mac.GetConstBuffer(); } const uint8_t *get_dst_mac_addr() const { assert(has_dst_mac_addr()); - return m_dst_mac; + return m_dst_mac.GetConstBuffer(); } uint16_t get_vlan() const { @@ -110,64 +104,165 @@ public: return m_vlan; } + /* updates a configuration with a group index member */ + void update(uint32_t index, const ClientCfgDirExt &cfg); +}; + +class ClientCfgDirExt : public ClientCfgDirBase { + friend class ClientCfgCompactEntry; + 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; + enum { + HAS_SRC_MAC = 0x1, + HAS_DST_MAC = 0x2, + HAS_VLAN = 0x4, + HAS_NEXT_HOP = 0x8, + HAS_IPV6_NEXT_HOP = 0x10, + HAS_SRC_IP = 0x20, + HAS_SRC_IPV6 = 0x40, + }; - for (int i = 0; i < 6; i++) { - tmp <<= 8; - tmp |= mac[i]; + uint32_t m_next_hop; + uint32_t m_src_ip; + uint16_t m_src_ipv6[8]; + uint16_t m_ipv6_next_hop[8]; + std::vector <MacAddress> m_resolved_macs; + +public: + void dump(FILE *fd) const; + void set_resolved_macs(CManyIPInfo &pretest_result, uint16_t count); + bool need_resolve() const; + void set_no_resolve_needed(); + + bool has_ipv6_next_hop() const { + return (m_bitfield & HAS_IPV6_NEXT_HOP); + } + + bool has_next_hop() const { + return (m_bitfield & HAS_NEXT_HOP); + } + + bool has_src_ip() const { + return (m_bitfield & HAS_SRC_IP); + } + + bool has_src_ipv6() const { + return (m_bitfield & HAS_SRC_IPV6); + } + + void set_src_ip(uint32_t src_ip) { + m_src_ip = src_ip; + m_bitfield |= HAS_SRC_IP; + } + + void set_src_ipv6(const uint16_t src_ipv6[8]) { + for (int i = 0; i < 8; i++) { + m_src_ipv6[i] = src_ipv6[i]; } + m_bitfield |= HAS_SRC_IPV6; + } - tmp += i; + void set_next_hop(uint32_t next_hop) { + m_next_hop = next_hop; + m_bitfield |= HAS_NEXT_HOP; + } - for (int i = 0; i < 6; i++) { - mac[i] = ( tmp >> ((5 - i) * 8) ) & 0xFF; + void set_ipv6_next_hop(const uint16_t next_hop[8]) { + for (int i = 0; i < 8; i++) { + m_ipv6_next_hop[i] = next_hop[i]; } + m_bitfield |= HAS_IPV6_NEXT_HOP; + } + virtual MacAddress get_resolved_mac(uint16_t index) const { + return m_resolved_macs[index]; } + }; +class ClientCfgExt; + /** * single client config - * + * */ -class ClientCfg { +class ClientCfgBase { public: + virtual void dump (FILE *fd) const { + fprintf(fd, " initiator :\n"); + m_initiator.dump(fd); + fprintf(fd, " responder :\n"); + m_responder.dump(fd); + } + virtual void update(uint32_t index, const ClientCfgExt *cfg); - void update(uint32_t index) { - m_initiator.update(index); - m_responder.update(index); + public: + ClientCfgDirBase m_initiator; + ClientCfgDirBase m_responder; +}; + +class ClientCfgExt : public ClientCfgBase { +public: + virtual void dump (FILE *fd) const { + fprintf(fd, " initiator:\n"); + m_initiator.dump(fd); + fprintf(fd, " responder:\n"); + m_responder.dump(fd); } - ClientCfgDir m_initiator; - ClientCfgDir m_responder; + ClientCfgDirExt m_initiator; + ClientCfgDirExt m_responder; +}; + +class ClientCfgCompactEntry { + friend class ClientCfgDB; + public: + uint16_t get_count() {return m_count;} + uint16_t get_vlan() {return m_vlan;} + uint16_t get_port() {return m_port;} + bool is_ipv4() {return m_is_ipv4;} + uint32_t get_dst_ip() {return m_next_hop_base.ip;} + uint16_t *get_dst_ipv6() {return m_next_hop_base.ipv6;} + uint32_t get_src_ip() {return m_src_ip.ip;} + uint16_t *get_src_ipv6() {return m_src_ip.ipv6;} + + public: + void fill_from_dir(ClientCfgDirExt cfg, uint8_t port_id); + + private: + uint16_t m_count; + uint16_t m_vlan; + uint8_t m_port; + bool m_is_ipv4; + union { + uint32_t ip; + uint16_t ipv6[8]; + } m_next_hop_base; + union { + uint32_t ip; + uint16_t ipv6[8]; + } m_src_ip; + }; /******************************** internal section ********************************/ /** - * describes a single client config + * describes a single client config * entry loaded from the config file - * + * */ class ClientCfgEntry { - + friend class basic_client_cfg_test1_Test; public: ClientCfgEntry() { reset(); } - - void dump() const; - + void dump(FILE *fd) const; + void set_resolved_macs(CManyIPInfo &pretest_result); bool contains(uint32_t ip) const { return ( (ip >= m_ip_start) && (ip <= m_ip_end) ); } @@ -176,19 +271,19 @@ public: m_iterator = 0; } - + /** - * assings a client config from the group - * it will advance MAC addresses andf etc. - * + * assings a client config from the group + * it will advance MAC addresses andf etc. + * * @author imarom (27-Jun-16) - * - * @param info + * + * @param info */ - void assign(ClientCfg &info) { + void assign(ClientCfgBase &info) { info = m_cfg; - info.update(m_iterator); - + info.update(m_iterator, &m_cfg); + /* advance for the next assign */ m_iterator = (m_iterator + 1) % m_count; } @@ -197,69 +292,92 @@ public: uint32_t m_ip_start; uint32_t m_ip_end; - ClientCfg m_cfg; + ClientCfgExt m_cfg; uint32_t m_count; private: + void set_params(uint32_t start, uint32_t end, uint32_t count) { // for tests + m_ip_start = start; + m_ip_end = end; + m_count = count; + } + void set_cfg(const ClientCfgExt &cfg) { + m_cfg = cfg; + } uint32_t m_iterator; }; /** * holds all the configured clients groups - * + * */ class ClientCfgDB { -public: + friend class basic_client_cfg_test1_Test; + public: ClientCfgDB() { m_is_empty = true; m_cache_group = NULL; m_under_vlan = false; + m_tg = NULL; } + ~ClientCfgDB() { + m_groups.clear(); + } + + void dump(FILE *fd) ; + /** - * if no config file was loaded - * this should return true - * + * if no config file was loaded + * this should return true + * */ bool is_empty() { return m_is_empty; } + void set_resolved_macs(CManyIPInfo &pretest_result); + void get_entry_list(std::vector<ClientCfgCompactEntry *> &ret); + + /** - * loads a YAML file - * configuration will be built - * according to the YAML config - * + * 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 - * + * lookup for a specific IP address for + * a group that contains this IP + * */ ClientCfgEntry * lookup(uint32_t ip); ClientCfgEntry * lookup(const std::string &ip); + void set_tuple_gen_info(CTupleGenYamlInfo *tg) {m_tg = tg;} private: void parse_single_group(YAMLParserWrapper &parser, const YAML::Node &node); - void parse_dir(YAMLParserWrapper &parser, const YAML::Node &node, ClientCfgDir &dir); - + void parse_dir(YAMLParserWrapper &parser, const YAML::Node &node, ClientCfgDirExt &dir); + void set_vlan(bool val) {m_under_vlan = val;} // for tests + void add_group(uint32_t ip, ClientCfgEntry cfg) { // for tests + m_groups.insert(std::make_pair(ip, cfg)); + } /** * 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; + bool m_under_vlan; + CTupleGenYamlInfo * m_tg; + ClientCfgEntry * m_cache_group; + bool m_is_empty; }; #endif /* __TREX_CLIENT_CONFIG_H__ */ - diff --git a/src/tuple_gen.cpp b/src/tuple_gen.cpp index 6861b73f..a4509580 100755 --- a/src/tuple_gen.cpp +++ b/src/tuple_gen.cpp @@ -6,7 +6,7 @@ */ /* -Copyright (c) 2015-2015 Cisco Systems, Inc. +Copyright (c) 2015-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. @@ -21,11 +21,10 @@ See the License for the specific language governing permissions and limitations under the License. */ - -#include "tuple_gen.h" #include <string.h> #include "utl_yaml.h" #include "bp_sim.h" +#include "tuple_gen.h" void CServerPool::Create(IP_DIST_t dist_value, uint32_t min_ip, @@ -134,7 +133,7 @@ void CClientPool::allocate_configured_clients(uint32_t min_ip, throw std::runtime_error(ss.str()); } - ClientCfg info; + ClientCfgBase info; group->assign(info); if (is_long_range) { @@ -216,13 +215,84 @@ void CTupleGeneratorSmart::Delete(){ m_server_pool.clear(); } +void CTupleGenYamlInfo::dump(FILE *fd) { + fprintf(fd, "Client pools:\n"); + for (int i=0; i < m_client_pool.size(); i++) { + m_client_pool[i].Dump(fd); + } + fprintf(fd, "Server pools:\n"); + for (int i=0; i < m_server_pool.size(); i++) { + m_server_pool[i].Dump(fd); + } +} + +// Find out matching port for given ip range. +// If found, port is returned in port, otherwise port is set to UINT8_MAX +// Return false in case of error. True otherwise. Port not found is not considered error. +bool CTupleGenYamlInfo::find_port(uint32_t ip_start, uint32_t ip_end, uint8_t &port) { + uint8_t num_ports = CGlobalInfo::m_options.get_expected_ports(); + + for (int i=0; i < m_client_pool.size(); i++) { + CTupleGenPoolYaml &pool = m_client_pool[i]; + uint32_t pool_start = pool.get_ip_start(); + uint32_t pool_end = pool.get_ip_end(); + uint32_t pool_offset = pool.getDualMask(); + for (uint8_t port_id = 0; port_id < num_ports; port_id += 2) { + uint32_t pool_port_start = pool_start + pool_offset * port_id / 2; + uint32_t pool_port_end = pool_end + pool_offset * port_id / 2; + if ((ip_start >= pool_port_start) && (ip_start <= pool_port_end)) { + if ((ip_end >= pool_port_start) && (ip_end <= pool_port_end)) { + port = port_id; + return true; + } else { + // ip_start in range, ip_end not + fprintf(stderr, "Error for range %s - %s. Start is inside range %s - %s, but end is outside\n" + , ip_to_str(ip_start).c_str(), ip_to_str(ip_end).c_str() + , ip_to_str(pool_port_start).c_str(), ip_to_str(pool_port_end).c_str()); + port = UINT8_MAX; + return false; + } + } + } + } + + for (int i=0; i < m_server_pool.size(); i++) { + CTupleGenPoolYaml &pool = m_server_pool[i]; + uint32_t pool_start = pool.get_ip_start(); + uint32_t pool_end = pool.get_ip_end(); + uint32_t pool_offset = pool.getDualMask(); + for (uint8_t port_id = 1; port_id < num_ports; port_id += 2) { + uint32_t pool_port_start = pool_start + pool_offset * (port_id - 1) / 2; + uint32_t pool_port_end = pool_end + pool_offset * (port_id - 1)/ 2; + if ((ip_start >= pool_port_start) && (ip_start <= pool_port_end)) { + if ((ip_end >= pool_port_start) && (ip_end <= pool_port_end)) { + port = port_id; + return true; + } else { + fprintf(stderr, "Error for range %s - %s. Start is inside range %s - %s, but end is outside\n" + , ip_to_str(ip_start).c_str(), ip_to_str(ip_end).c_str() + , ip_to_str(pool_port_start).c_str(), ip_to_str(pool_port_end).c_str()); + // ip_start in range, ip_end not + port = UINT8_MAX; + return false; + } + } + } + } + + port = UINT8_MAX; + return true; +} + void CTupleGenPoolYaml::Dump(FILE *fd){ - fprintf(fd," dist : %d \n",m_dist); - fprintf(fd," IPs : %08x -%08x \n",m_ip_start,m_ip_end); - fprintf(fd," clients per gb : %d \n",m_number_of_clients_per_gb); - fprintf(fd," min clients : %d \n",m_min_clients); - fprintf(fd," tcp aging : %d sec \n",m_tcp_aging_sec); - fprintf(fd," udp aging : %d sec \n",m_udp_aging_sec); + fprintf(fd," Pool %s:\n", (m_name.size() == 0) ? "default":m_name.c_str()); + fprintf(fd," dist : %d \n",m_dist); + fprintf(fd," IPs : %s - %s \n",ip_to_str(m_ip_start).c_str(), ip_to_str(m_ip_end).c_str()); + fprintf(fd," dual_port_mask : %s \n",ip_to_str(m_dual_interface_mask).c_str()); + fprintf(fd," clients per gb : %d \n",m_number_of_clients_per_gb); + fprintf(fd," min clients : %d \n",m_min_clients); + fprintf(fd," tcp aging : %d sec \n",m_tcp_aging_sec); + fprintf(fd," udp aging : %d sec \n",m_udp_aging_sec); } bool CTupleGenPoolYaml::is_valid(uint32_t num_threads,bool is_plugins){ @@ -244,10 +314,6 @@ bool CTupleGenPoolYaml::is_valid(uint32_t num_threads,bool is_plugins){ return (true); } - - - - #define UTL_YAML_READ(type, field, target) if (node.FindValue(#field)) { \ utl_yaml_read_ ## type (node, #field , target); \ } else { printf("generator definition mising " #field "\n"); } diff --git a/src/tuple_gen.h b/src/tuple_gen.h index 2491f489..e9dc8d4e 100755 --- a/src/tuple_gen.h +++ b/src/tuple_gen.h @@ -85,15 +85,15 @@ public: void setClientPort(uint16_t port) { m_client_port = port; } - void setClientCfg(ClientCfg *cfg) { + void setClientCfg(ClientCfgBase *cfg) { m_client_cfg = cfg; } - ClientCfg *getClientCfg() { + ClientCfgBase *getClientCfg() { return m_client_cfg; } - void setClientTuple(uint32_t ip, ClientCfg *cfg, uint16_t port) { + void setClientTuple(uint32_t ip, ClientCfgBase *cfg, uint16_t port) { setClient(ip); setClientPort(port); setClientCfg(cfg); @@ -125,7 +125,7 @@ private: uint32_t m_server_ip; uint32_t m_server_idx; - ClientCfg *m_client_cfg; + ClientCfgBase *m_client_cfg; uint16_t m_client_port; uint16_t m_server_port; @@ -337,7 +337,7 @@ template <typename T> class CConfiguredClientInfo : public T { public: - CConfiguredClientInfo(uint32_t ip, const ClientCfg &cfg) : m_cfg(cfg) { + CConfiguredClientInfo(uint32_t ip, const ClientCfgBase &cfg) : m_cfg(cfg) { T::set_ip(ip); } @@ -348,7 +348,7 @@ public: } private: - ClientCfg m_cfg; + ClientCfgBase m_cfg; }; @@ -852,6 +852,9 @@ struct CTupleGenPoolYaml { uint32_t get_ip_start() { return m_ip_start; } + uint32_t get_ip_end() { + return m_ip_end; + } bool is_valid(uint32_t num_threads,bool is_plugins); void Dump(FILE *fd); }; @@ -888,6 +891,9 @@ public: exit(-1); return 0; } + + bool find_port(uint32_t ip_start, uint32_t ip_end, uint8_t &port); + void dump(FILE *fd); }; diff --git a/src/utl_ip.cpp b/src/utl_ip.cpp new file mode 100644 index 00000000..5bd83f95 --- /dev/null +++ b/src/utl_ip.cpp @@ -0,0 +1,121 @@ +/* + 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 <string> +#include <iostream> +#include <pkt_gen.h> +#include "utl_ip.h" + +void COneIPInfo::dump(FILE *fd, const char *offset) const { + uint8_t mac[ETHER_ADDR_LEN]; + m_mac.copyToArray(mac); + char ip_str[100]; + get_ip_str(ip_str); + std::string mac_str; + utl_macaddr_to_str(mac, mac_str); + const char *mac_char = resolve_needed() ? "Not resolved" : mac_str.c_str(); + fprintf(fd, "%sip: %s vlan: %d port: %d mac: %s\n", offset, ip_str, m_vlan, m_port, mac_char); +} + +bool COneIPInfo::resolve_needed() const { + return m_mac.isDefaultAddress(); +} + +/* + * Fill buffer p with arp request. + * port_id - port id we intend to send on + * sip - source IP/MAC information + */ +void COneIPv4Info::fill_arp_req_buf(uint8_t *p, uint16_t port_id, COneIPInfo *sip) { + uint8_t src_mac[ETHER_ADDR_LEN]; + sip->get_mac(src_mac); + + CTestPktGen::create_arp_req(p, ((COneIPv4Info *)sip)->get_ip(), m_ip, src_mac, m_vlan, port_id); +} + +void COneIPv4Info::fill_grat_arp_buf(uint8_t *p) { + uint8_t src_mac[ETHER_ADDR_LEN]; + get_mac(src_mac); + + CTestPktGen::create_arp_req(p, m_ip, m_ip, src_mac, m_vlan, 0); +} + +void COneIPv6Info::fill_arp_req_buf(uint8_t *p, uint16_t port_id, COneIPInfo *sip) { + //??? implement ipv6 +} + +void COneIPv6Info::fill_grat_arp_buf(uint8_t *p) { + //??? implement ipv6 +} + +const COneIPInfo *CManyIPInfo::get_next() { + COneIPInfo *ret; + + if (!m_iter_initiated) { + m_ipv4_iter = m_ipv4_resolve.begin(); + m_iter_initiated = true; + } + + if (m_ipv4_iter == m_ipv4_resolve.end()) { + m_ipv4_iter = m_ipv4_resolve.begin(); + return NULL; + } + + ret = &(m_ipv4_iter->second); + m_ipv4_iter++; + return ret; +} + +void CManyIPInfo::dump(FILE *fd) { + ip_vlan_to_many_ip_iter_t it; + for (it = m_ipv4_resolve.begin(); it != m_ipv4_resolve.end(); it++) { + fprintf(fd, "IPv4 resolved list:\n"); + uint8_t mac[ETHER_ADDR_LEN]; + it->second.get_mac(mac); + fprintf(fd, "ip:%s vlan: %d resolved to mac %s\n", ip_to_str(it->first.get_ip()).c_str(), it->first.get_vlan() + , utl_macaddr_to_str(mac).c_str()); + } +} + +void CManyIPInfo::insert(COneIPv4Info &ip_info) { + CIpVlan ip_vlan(ip_info.get_ip(), ip_info.get_vlan()); + + m_ipv4_resolve.insert(std::make_pair(ip_vlan, ip_info)); +} + +bool CManyIPInfo::lookup(uint32_t ip, uint16_t vlan, MacAddress &ret_mac) { + ip_vlan_to_many_ip_iter_t it = m_ipv4_resolve.find(CIpVlan(ip, vlan)); + if (it != m_ipv4_resolve.end()) { + uint8_t mac[ETHER_ADDR_LEN]; + (*it).second.get_mac(mac); + ret_mac.set(mac); + return true; + } else { + return false; + } +} + +const COneIPInfo *CManyIPInfo::get_first() { + if (m_ipv4_resolve.size() == 0) { + return NULL; + } else { + m_ipv4_iter = m_ipv4_resolve.begin(); + return &(m_ipv4_iter->second); + } +} diff --git a/src/utl_ip.h b/src/utl_ip.h new file mode 100644 index 00000000..3a133a15 --- /dev/null +++ b/src/utl_ip.h @@ -0,0 +1,246 @@ +#ifndef UTL_IP_H +#define UTL_IP_H +/* + 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 <map> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include "common/basic_utils.h" +#include "common/Network/Packet/CPktCmn.h" +#include "common/Network/Packet/MacAddress.h" + +/* IP address, last 32-bits of IPv6 remaps IPv4 */ +typedef struct { + uint16_t v6[6]; /* First 96-bits of IPv6 */ + uint32_t v4; /* Last 32-bits IPv6 overloads v4 */ +} ipaddr_t; + +// Routine to create IPv4 address string +inline int ip_to_str(uint32_t ip,char * str) { + uint32_t ipv4 = PKT_HTONL(ip); + inet_ntop(AF_INET, (const char *)&ipv4, str, INET_ADDRSTRLEN); + 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; + uint16_t ipv6[8]; + for (uint8_t i=0; i<6; i++) { + ipv6[i] = PKT_HTONS(ip->v6[i]); + } + uint32_t ipv4 = PKT_HTONL(ip->v4); + ipv6[6] = ipv4 & 0xffff; + ipv6[7] = ipv4 >> 16; + + str[idx++] = '['; + inet_ntop(AF_INET6, (const char *)&ipv6, &str[1], INET6_ADDRSTRLEN); + idx = strlen(str); + str[idx++] = ']'; + str[idx] = 0; + return(idx); +} + +inline std::string ip_to_str(uint8_t *ip) { + char tmp[INET6_ADDRSTRLEN]; + ipv6_to_str((ipaddr_t *)ip, tmp); + return tmp; +} + +class CIpVlan { + // to be able to use this in map + friend bool operator<(const CIpVlan& l, const CIpVlan& r) { + if (l.get_ip() == r.get_ip()) { + return l.get_vlan() < r.get_vlan(); + } else { + return l.get_ip() < r.get_ip(); + } + } + + public: + CIpVlan(uint32_t ip, uint16_t vlan) { + m_ip = ip; + m_vlan = vlan; + } + uint16_t get_vlan() const {return m_vlan;} + void set_vlan(uint16_t vlan) {m_vlan = vlan;} + uint16_t get_ip() const {return m_ip;} + void set_ip(uint32_t ip) {m_ip = ip;} + + private: + uint32_t m_ip; + uint16_t m_vlan; +}; + + +class COneIPInfo { + public: + enum { + IP4_VER=4, + IP6_VER=6, + } COneIPInfo_ip_types; + + public: + virtual void get_mac(uint8_t *mac) const { + m_mac.copyToArray(mac); + } + virtual void set_mac(uint8_t *mac) { + m_mac.set(mac); + } + uint16_t get_vlan() const {return m_vlan;} + uint16_t get_port() const {return m_port;} + virtual void dump(FILE *fd) const { + dump(fd, ""); + } + virtual void dump(FILE *fd, const char *offset) const; + virtual uint8_t ip_ver() const {return 0;} + virtual uint32_t get_arp_req_len() const=0; + virtual uint32_t get_grat_arp_len() const=0; + virtual void fill_arp_req_buf(uint8_t *p, uint16_t port_id, COneIPInfo *sip)=0; + virtual void fill_grat_arp_buf(uint8_t *p)=0; + virtual bool resolve_needed() const; + + protected: + COneIPInfo(uint16_t vlan, MacAddress mac, uint8_t port) : m_mac(mac) { + m_vlan = vlan; + m_port = port; + } + COneIPInfo(uint16_t vlan, MacAddress mac) : COneIPInfo(vlan, mac, UINT8_MAX) { + } + virtual const void get_ip_str(char str[100]) const { + snprintf(str, 4, "Bad"); + } + + protected: + uint8_t m_port; + uint16_t m_vlan; + MacAddress m_mac; +}; + +class COneIPv4Info : public COneIPInfo { + friend bool operator== (const COneIPv4Info& lhs, const COneIPv4Info& rhs); + + public: + COneIPv4Info(uint32_t ip, uint16_t vlan, MacAddress mac) : COneIPInfo(vlan, mac) { + m_ip = ip; + } + COneIPv4Info(uint32_t ip, uint16_t vlan) : COneIPv4Info (ip, vlan, MacAddress()) { + } + COneIPv4Info(uint32_t ip, uint16_t vlan, MacAddress mac, uint8_t port) : COneIPInfo(vlan, mac, port) { + m_ip = ip; + } + uint32_t get_ip() {return m_ip;} + virtual uint8_t ip_ver() const {return IP4_VER;} + virtual uint32_t get_arp_req_len() const {return 60;} + virtual uint32_t get_grat_arp_len() const {return 60;} + virtual void fill_arp_req_buf(uint8_t *p, uint16_t port_id, COneIPInfo *sip); + virtual void fill_grat_arp_buf(uint8_t *p); + + private: + virtual const void get_ip_str(char str[100]) const { + ip_to_str(m_ip, str); + }; + uint32_t m_ip; +}; + +inline bool operator== (const COneIPv4Info& lhs, const COneIPv4Info& rhs) { + if (lhs.m_vlan != rhs.m_vlan) + return false; + + if (lhs.m_ip != rhs.m_ip) + return false; + + return true; +} + +inline bool operator!= (const COneIPv4Info& lhs, const COneIPv4Info& rhs){ return !(lhs == rhs); } + +class COneIPv6Info : public COneIPInfo { + friend bool operator== (const COneIPv6Info& lhs, const COneIPv6Info& rhs); + + public: + COneIPv6Info(uint16_t ip[8], uint16_t vlan, MacAddress mac) : COneIPInfo(vlan, mac) { + memcpy(m_ip, ip, sizeof(m_ip)); + } + + COneIPv6Info(uint16_t ip[8], uint16_t vlan) : COneIPv6Info(ip, vlan, MacAddress()){ + } + + COneIPv6Info(uint16_t ip[8], uint16_t vlan, MacAddress mac, uint8_t port) : COneIPInfo(vlan, mac, port) { + memcpy(m_ip, ip, sizeof(m_ip)); + } + + const uint8_t *get_ipv6() {return (uint8_t *)m_ip;} + virtual uint8_t ip_ver() const {return IP6_VER;} + virtual uint32_t get_arp_req_len() const {return 100; /* ??? put correct number for ipv6*/} + virtual uint32_t get_grat_arp_len() const {return 100; /* ??? put correct number for ipv6*/} + virtual void fill_arp_req_buf(uint8_t *p, uint16_t port_id, COneIPInfo *sip); + virtual void fill_grat_arp_buf(uint8_t *p); + + private: + virtual const void get_ip_str(char str[100]) { + ipv6_to_str((ipaddr_t *)m_ip, str); + } + uint16_t m_ip[8]; +}; + +inline bool operator== (const COneIPv6Info& lhs, const COneIPv6Info& rhs) { + if (lhs.m_vlan != rhs.m_vlan) + return false; + + if (memcmp(&lhs.m_ip, &rhs.m_ip, sizeof(rhs.m_ip))) + return false; + + return true; +} + +inline bool operator!= (const COneIPv6Info& lhs, const COneIPv6Info& rhs){ return !(lhs == rhs); } + +typedef std::map<CIpVlan, COneIPv4Info> ip_vlan_to_many_ip_t; +typedef std::map<CIpVlan, COneIPv4Info>::iterator ip_vlan_to_many_ip_iter_t; +typedef std::map<std::pair<uint16_t[8], uint16_t>, COneIPv6Info> ipv6_vlan_to_many_ipv6_t; + +class CManyIPInfo { + public: + CManyIPInfo () { + m_iter_initiated = false; + } + void insert(COneIPv4Info &ip_info); + bool lookup(uint32_t ip, uint16_t vlan, MacAddress &ret_mac); + void dump(FILE *fd); + uint32_t size() { return m_ipv4_resolve.size() + m_ipv6_resolve.size();} + const COneIPInfo *get_first(); + const COneIPInfo *get_next(); + private: + ip_vlan_to_many_ip_t m_ipv4_resolve; + ip_vlan_to_many_ip_iter_t m_ipv4_iter; + ipv6_vlan_to_many_ipv6_t m_ipv6_resolve; + bool m_iter_initiated; + +}; + +#endif diff --git a/src/utl_yaml.cpp b/src/utl_yaml.cpp index 094a3de8..a4fd6404 100755 --- a/src/utl_yaml.cpp +++ b/src/utl_yaml.cpp @@ -1,12 +1,10 @@ -#include "utl_yaml.h" -#include <common/Network/Packet/CPktCmn.h> /* Hanoh Haim Cisco Systems, Inc. */ /* -Copyright (c) 2015-2015 Cisco Systems, Inc. +Copyright (c) 2015-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. @@ -24,13 +22,15 @@ limitations under the License. #include <istream> #include <fstream> #include "common/basic_utils.h" +#include <common/Network/Packet/CPktCmn.h> +#include "utl_yaml.h" #define INADDRSZ 4 extern int my_inet_pton4(const char *src, unsigned char *dst); extern int my_inet_pton6(const char *src, unsigned char *dst); -bool utl_yaml_read_ip_addr(const YAML::Node& node, +bool utl_yaml_read_ip_addr(const YAML::Node& node, const std::string &name, uint32_t & val){ std::string tmp; @@ -42,16 +42,14 @@ bool utl_yaml_read_ip_addr(const YAML::Node& node, val=PKT_NTOHL(ip); res=true; }else{ - printf(" ERROR not a valid ip %s \n",(char *)tmp.c_str()); + printf(" Error: non valid ip %s \n",(char *)tmp.c_str()); exit(-1); } } return (res); } - - -bool utl_yaml_read_uint32(const YAML::Node& node, +bool utl_yaml_read_uint32(const YAML::Node& node, const std::string &name, uint32_t & val){ bool res=false; @@ -62,7 +60,7 @@ bool utl_yaml_read_uint32(const YAML::Node& node, return (res); } -bool utl_yaml_read_uint16(const YAML::Node& node, +bool utl_yaml_read_uint16(const YAML::Node& node, const std::string &name, uint16_t & val){ uint32_t val_tmp; @@ -183,10 +181,10 @@ YAMLParserWrapper::parse_bool(const YAML::Node &node, const std::string &name) { return (val); } catch (const YAML::InvalidScalar &ex) { - parse_err("expecting true/false for field '" + name + "'", node[name]); + parse_err("Expecting true/false for field '" + name + "'", node[name]); } catch (const YAML::KeyNotFound &ex) { - parse_err("cannot locate mandatory field '" + name + "'", node); + parse_err("Can not locate mandatory field '" + name + "'", node); } assert(0); @@ -203,10 +201,10 @@ YAMLParserWrapper::parse_list(const YAML::Node &node, const std::string &name) { return val; } catch (const YAML::InvalidScalar &ex) { - parse_err("expecting sequence/list for field '" + name + "'", node[name]); + parse_err("Expecting sequence or list for field '" + name + "'", node[name]); } catch (const YAML::KeyNotFound &ex) { - parse_err("cannot locate mandatory field '" + name + "'", node); + parse_err("Can not locate mandatory field '" + name + "'", node); } assert(0); @@ -223,10 +221,10 @@ YAMLParserWrapper::parse_map(const YAML::Node &node, const std::string &name) { return val; } catch (const YAML::InvalidScalar &ex) { - parse_err("expecting map for field '" + name + "'", node[name]); + parse_err("Expecting map for field '" + name + "'", node[name]); } catch (const YAML::KeyNotFound &ex) { - parse_err("cannot locate mandatory field '" + name + "'", node); + parse_err("Can not locate mandatory field '" + name + "'", node); } assert(0); @@ -241,16 +239,43 @@ YAMLParserWrapper::parse_ip(const YAML::Node &node, const std::string &name) { 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]); + 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]); + parse_err("Expecting valid IP address for field '" + name + "'", node[name]); + + } catch (const YAML::KeyNotFound &ex) { + parse_err("Can not locate mandatory field '" + name + "'", node); + } + + assert(0); +} + +void +YAMLParserWrapper::parse_ipv6(const YAML::Node &node, const std::string &name, unsigned char *ip_num) { + try { + std::string ip_str; + + node[name] >> ip_str; + int rc = my_inet_pton6((char *)ip_str.c_str(), ip_num); + if (!rc) { + parse_err("Invalid IPv6 address: " + ip_str, node[name]); + } + + // we want host order + for (int i = 0; i < 8; i++) { + ((uint16_t *) ip_num)[i] = PKT_NTOHS(((uint16_t *) ip_num)[i]); + } + return; + + } catch (const YAML::InvalidScalar &ex) { + parse_err("Expecting valid IPv6 address for field '" + name + "'", node[name]); } catch (const YAML::KeyNotFound &ex) { - parse_err("cannot locate mandatory field '" + name + "'", node); + parse_err("Can not locate mandatory field '" + name + "'", node); } assert(0); @@ -276,21 +301,21 @@ YAMLParserWrapper::parse_mac_addr(const YAML::Node &node, const std::string &nam node[name] >> mac_str; bool rc = mac2uint64(mac_str, mac_num); if (!rc) { - parse_err("invalid MAC address: " + mac_str, node[name]); + 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]); + parse_err("Expecting true/false for field '" + name + "'", node[name]); } catch (const YAML::KeyNotFound &ex) { - parse_err("cannot locate mandatory field '" + name + "'", node); + parse_err("Can not locate mandatory field '" + name + "'", node); } assert(0); } -uint64_t +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; @@ -299,7 +324,7 @@ YAMLParserWrapper::parse_uint(const YAML::Node &node, const std::string &name, u return parse_uint(node, name, low, high); } -uint64_t +uint64_t YAMLParserWrapper::parse_uint(const YAML::Node &node, const std::string &name, uint64_t low, uint64_t high) { try { @@ -316,10 +341,10 @@ YAMLParserWrapper::parse_uint(const YAML::Node &node, const std::string &name, u return (val); } catch (const YAML::InvalidScalar &ex) { - parse_err("expecting true/false for field '" + name + "'", node[name]); + parse_err("Expecting true/false for field '" + name + "'", node[name]); } catch (const YAML::KeyNotFound &ex) { - parse_err("cannot locate mandatory field '" + name + "'", node); + parse_err("Can not locate mandatory field '" + name + "'", node); } assert(0); diff --git a/src/utl_yaml.h b/src/utl_yaml.h index ed7d66d6..004e82db 100755 --- a/src/utl_yaml.h +++ b/src/utl_yaml.h @@ -63,6 +63,7 @@ public: 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); + void parse_ipv6(const YAML::Node &node, const std::string &name, unsigned char *ip); 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); |