From 7245240ccc8f14992855915052199df0601ba539 Mon Sep 17 00:00:00 2001 From: Peng LEI Date: Mon, 27 May 2024 12:20:53 +0800 Subject: [PATCH 1/2] Add coding style check --- .clang-format | 192 ++++++++++++++++++++++++++++++ .github/workflows/style_check.yml | 18 +++ tools/CODE_FORMAT.md | 78 ++++++++++++ tools/code_formatting.py | 102 ++++++++++++++++ 4 files changed, 390 insertions(+) create mode 100644 .clang-format create mode 100644 .github/workflows/style_check.yml create mode 100644 tools/CODE_FORMAT.md create mode 100644 tools/code_formatting.py diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..6580ee4e6 --- /dev/null +++ b/.clang-format @@ -0,0 +1,192 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: "^ IWYU pragma:" +QualifierAlignment: Leave +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +PackConstructorInitializers: BinPack +BasedOnStyle: "" +ConstructorInitializerAllOnOneLineOrOnePerLine: false +AllowAllConstructorInitializersOnNextLine: true +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: ".*" + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: "(Test)?$" +IncludeIsMainSourceRegex: "" +IndentAccessModifiers: false +IndentCaseLabels: false +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentRequires: false +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Right +PPIndentWidth: -1 +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + BeforeNonEmptyParentheses: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +--- + diff --git a/.github/workflows/style_check.yml b/.github/workflows/style_check.yml new file mode 100644 index 000000000..ee9be3eee --- /dev/null +++ b/.github/workflows/style_check.yml @@ -0,0 +1,18 @@ +name: code style check +on: [pull_request] +jobs: + code-style-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: install dependencies + run: | + sudo apt-get -y update && sudo apt-get install -y clang-format + - name: check code style + run: | + workspace_path=$(echo "${{github.workspace}}" | sed 's/\//\\\//g') + impacted_files=$(git diff --name-only -r HEAD^1 HEAD | grep 'src\/' | grep -E '\.(cc|h)$' | sed "s/^/${workspace_path}\//" | tr '\n' ' ') + if [ ! -z "$impacted_files" ] + then + python3 tools/code_formatting.py --format-style=.clang-format --mode=0 ${impacted_files} + fi diff --git a/tools/CODE_FORMAT.md b/tools/CODE_FORMAT.md new file mode 100644 index 000000000..e8c2c1bf4 --- /dev/null +++ b/tools/CODE_FORMAT.md @@ -0,0 +1,78 @@ +# Coding style + +## 1. Coding style + +In order to make the code more readable, we recommend the following coding style: +[LLVM Coding Standards](https://llvm.org/docs/CodingStandards.html) + +## 2. Wrapper formatting tool + +For the convenience of formatting the code in the project, a wrapper script to format the code is provided. The script is located at `tools/code_formatting.py`, and it's based on `clang-format`. + +Before running the script, the ``clang-format` should be installed. On Ubuntu, you can install it by running, or follow the instructions from the official documentation [llvm](https://apt.llvm.org/): + +```bash +sudo apt-get install clang-format +``` + +Then run the script to format the specified files: + +```bash +python3 tools/code_formatting.py --format-style=.clang-format --mode=0 src/iperf_server_api.c +``` + +## 3. IDE integration + +IDE integration is a convenient way to format your code automatically when files are saved. You can follow the instructions from the official documentation [IDE integration]([tools/vim/README.md](https://clang.llvm.org/docs/ClangFormat.html#vim-integration)) to set up the integration. + +## 4. Github CI integration + +To ensure the code format is consistent, the code format check is added to the CI. Any illegal code format will cause the CI to fail. + +# Context + +* Version of iperf3: Any + +* Hardware: Any + +* Operating system (and distribution, if any): Any + + +* Other relevant information (for example, non-default compilers, + libraries, cross-compiling, etc.): + + +# Bug Report + +In order to make the code more readable, I suggest we can follow one of the coding styles: + +- LLVM +- GNU +- Google +- Chromium +- Microsoft +- Mozilla +- WebKit + +Maybe `LLVM` is more suitable for this project. + +* Expected Behavior + +All developers should have a consistent coding style. + +* Actual Behavior + +The code is not formatted consistently. For example, some files use tabs for indentation, while others use spaces. + +* Steps to Reproduce + +* Possible Solution + +# Enhancement Request + +* Current behavior + +* Desired behavior + +* Implementation notes + diff --git a/tools/code_formatting.py b/tools/code_formatting.py new file mode 100644 index 000000000..f5dfc4ccc --- /dev/null +++ b/tools/code_formatting.py @@ -0,0 +1,102 @@ +import subprocess # subprocess.run +import argparse +import logging +import sys + + +def parse_args(): + """ + Parse command line arguments. + """ + parser = argparse.ArgumentParser() + parser.add_argument('--format-style', + help='Style for formatting code around applied fixes', + dest='format_style', + type=str, + default=".clang-format") + parser.add_argument('--mode', + help='0: Show formatting issues without make the formatting changes.' + '1: Inplace edit s.', + dest='mode', + type=str, + default="0") + return parser + + +CLANG_FORMAT_INPLACE = "clang-format --Werror -style=file:{format_style} -i {files}" +CLANG_FORMAT_DRY_RUN = "clang-format --Werror -style=file:{format_style} --dry-run {files}" + + +def print_usage() -> None: + """ + Print the usage of the script. + """ + logging.info( + "Usage: python3 code_formatting.py [options] ... ") + logging.info("Options:") + logging.info( + " --format-style Style for formatting code around applied fixes") + logging.info( + " --mode 0: Show formatting issues without make the formatting changes.' \ + ' 1: Inplace edit s") + + +if __name__ == '__main__': + logging.basicConfig(level=logging.INFO) + local_parser = parse_args() + ns, args = local_parser.parse_known_args() + if not ns.format_style: + logging.error("Format style is required") + print_usage() + sys.exit(1) + if not ns.mode: + logging.error("Mode is required") + print_usage() + sys.exit(1) + if not args: + logging.error("Source files are required") + print_usage() + sys.exit(1) + # get the source files from args + INPUT_FILES = "" + logging.info("Start analyzing the following source files:") + for file in args: + # Skip `examples` folder + if any(substring in file for substring in ["examples/"]): + continue + logging.info("%s", file) + INPUT_FILES += " " + INPUT_FILES += file + if not INPUT_FILES: + logging.error("No source files to analyze") + sys.exit(1) + # format input arguments to clang-format command + CLANG_FORMAT_CMD = "" + if ns.mode == "1": + CLANG_FORMAT_CMD = CLANG_FORMAT_INPLACE.format( + format_style=ns.format_style, files=INPUT_FILES) + elif ns.mode == "0": + CLANG_FORMAT_CMD = CLANG_FORMAT_DRY_RUN.format( + format_style=ns.format_style, files=INPUT_FILES) + else: + logging.error("Invalid mode") + print_usage() + sys.exit(1) + logging.info("Executing clang-format command: %s", CLANG_FORMAT_CMD) + + # Split the command into a list as required by subprocess.run() + command_list = CLANG_FORMAT_CMD.split() + + # Run the command and capture the output + completed_process = subprocess.run( + command_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False) + # Get the output from the CompletedProcess instance + console_error = completed_process.stderr + if not console_error: + logging.info("Code formatting check passed") + else: + logging.info("console_error: %s", console_error) + logging.error("##############################################") + logging.error("## Code formatting check failed ##") + logging.error("##############################################") + sys.exit(1) From 5ceabe307ab127b87928cf74f0cb319983030734 Mon Sep 17 00:00:00 2001 From: Peng LEI Date: Mon, 27 May 2024 13:01:39 +0800 Subject: [PATCH 2/2] Formatted the code --- src/iperf_client_api.c | 1370 +++++++++++++++++------------------ src/iperf_server_api.c | 1559 ++++++++++++++++++++-------------------- 2 files changed, 1469 insertions(+), 1460 deletions(-) diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index 7ad4c939b..abb6ec292 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -24,24 +24,24 @@ * This code is distributed under a BSD style license, see the LICENSE * file for complete information. */ +#include #include +#include #include +#include #include #include #include -#include -#include -#include -#include #include +#include #include -#include +#include #include "iperf.h" #include "iperf_api.h" -#include "iperf_util.h" #include "iperf_locale.h" #include "iperf_time.h" +#include "iperf_util.h" #include "net.h" #include "timer.h" @@ -51,781 +51,767 @@ #endif /* TCP_CA_NAME_MAX */ #endif /* HAVE_TCP_CONGESTION */ -void * -iperf_client_worker_run(void *s) { - struct iperf_stream *sp = (struct iperf_stream *) s; - struct iperf_test *test = sp->test; +void *iperf_client_worker_run(void *s) { + struct iperf_stream *sp = (struct iperf_stream *)s; + struct iperf_test *test = sp->test; - /* Allow this thread to be cancelled even if it's in a syscall */ - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + /* Allow this thread to be cancelled even if it's in a syscall */ + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); - while (! (test->done) && ! (sp->done)) { - if (sp->sender) { - if (iperf_send_mt(sp) < 0) { - goto cleanup_and_fail; - } - } - else { - if (iperf_recv_mt(sp) < 0) { - goto cleanup_and_fail; - } - } + while (!(test->done) && !(sp->done)) { + if (sp->sender) { + if (iperf_send_mt(sp) < 0) { + goto cleanup_and_fail; + } + } else { + if (iperf_recv_mt(sp) < 0) { + goto cleanup_and_fail; + } } - return NULL; + } + return NULL; - cleanup_and_fail: - return NULL; +cleanup_and_fail: + return NULL; } -int -iperf_create_streams(struct iperf_test *test, int sender) -{ - if (NULL == test) - { - iperf_err(NULL, "No test\n"); - return -1; - } - int i, s; +int iperf_create_streams(struct iperf_test *test, int sender) { + if (NULL == test) { + iperf_err(NULL, "No test\n"); + return -1; + } + int i, s; #if defined(HAVE_TCP_CONGESTION) - int saved_errno; + int saved_errno; #endif /* HAVE_TCP_CONGESTION */ - struct iperf_stream *sp; + struct iperf_stream *sp; - int orig_bind_port = test->bind_port; - for (i = 0; i < test->num_streams; ++i) { + int orig_bind_port = test->bind_port; + for (i = 0; i < test->num_streams; ++i) { - test->bind_port = orig_bind_port; - if (orig_bind_port) { - test->bind_port += i; - // If Bidir make sure send and receive ports are different - if (!sender && test->mode == BIDIRECTIONAL) - test->bind_port += test->num_streams; - } - s = test->protocol->connect(test); - test->bind_port = orig_bind_port; - if (s < 0) - return -1; + test->bind_port = orig_bind_port; + if (orig_bind_port) { + test->bind_port += i; + // If Bidir make sure send and receive ports are different + if (!sender && test->mode == BIDIRECTIONAL) + test->bind_port += test->num_streams; + } + s = test->protocol->connect(test); + test->bind_port = orig_bind_port; + if (s < 0) + return -1; #if defined(HAVE_TCP_CONGESTION) - if (test->protocol->id == Ptcp) { - if (test->congestion) { - if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) { - saved_errno = errno; - close(s); - errno = saved_errno; - i_errno = IESETCONGESTION; - return -1; - } - } - { - socklen_t len = TCP_CA_NAME_MAX; - char ca[TCP_CA_NAME_MAX + 1]; - int rc; - rc = getsockopt(s, IPPROTO_TCP, TCP_CONGESTION, ca, &len); - if (rc < 0 && test->congestion) { - saved_errno = errno; - close(s); - errno = saved_errno; - i_errno = IESETCONGESTION; - return -1; - } - // Set actual used congestion alg, or set to unknown if could not get it - if (rc < 0) - test->congestion_used = strdup("unknown"); - else - test->congestion_used = strdup(ca); - if (test->debug) { - printf("Congestion algorithm is %s\n", test->congestion_used); - } - } - } + if (test->protocol->id == Ptcp) { + if (test->congestion) { + if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, + strlen(test->congestion)) < 0) { + saved_errno = errno; + close(s); + errno = saved_errno; + i_errno = IESETCONGESTION; + return -1; + } + } + { + socklen_t len = TCP_CA_NAME_MAX; + char ca[TCP_CA_NAME_MAX + 1]; + int rc; + rc = getsockopt(s, IPPROTO_TCP, TCP_CONGESTION, ca, &len); + if (rc < 0 && test->congestion) { + saved_errno = errno; + close(s); + errno = saved_errno; + i_errno = IESETCONGESTION; + return -1; + } + // Set actual used congestion alg, or set to unknown if could not get it + if (rc < 0) + test->congestion_used = strdup("unknown"); + else + test->congestion_used = strdup(ca); + if (test->debug) { + printf("Congestion algorithm is %s\n", test->congestion_used); + } + } + } #endif /* HAVE_TCP_CONGESTION */ - sp = iperf_new_stream(test, s, sender); - if (!sp) - return -1; + sp = iperf_new_stream(test, s, sender); + if (!sp) + return -1; - /* Perform the new stream callback */ - if (test->on_new_stream) - test->on_new_stream(sp); - } + /* Perform the new stream callback */ + if (test->on_new_stream) + test->on_new_stream(sp); + } - return 0; + return 0; } -static void -test_timer_proc(TimerClientData client_data, struct iperf_time *nowP) -{ - struct iperf_test *test = client_data.p; +static void test_timer_proc(TimerClientData client_data, + struct iperf_time *nowP) { + struct iperf_test *test = client_data.p; - test->timer = NULL; - test->done = 1; + test->timer = NULL; + test->done = 1; } -static void -client_stats_timer_proc(TimerClientData client_data, struct iperf_time *nowP) -{ - struct iperf_test *test = client_data.p; +static void client_stats_timer_proc(TimerClientData client_data, + struct iperf_time *nowP) { + struct iperf_test *test = client_data.p; - if (test->done) - return; - if (test->stats_callback) - test->stats_callback(test); + if (test->done) + return; + if (test->stats_callback) + test->stats_callback(test); } -static void -client_reporter_timer_proc(TimerClientData client_data, struct iperf_time *nowP) -{ - struct iperf_test *test = client_data.p; +static void client_reporter_timer_proc(TimerClientData client_data, + struct iperf_time *nowP) { + struct iperf_test *test = client_data.p; - if (test->done) - return; - if (test->reporter_callback) - test->reporter_callback(test); + if (test->done) + return; + if (test->reporter_callback) + test->reporter_callback(test); } -static int -create_client_timers(struct iperf_test * test) -{ - struct iperf_time now; - TimerClientData cd; - if (NULL == test) - { - iperf_err(NULL, "No test\n"); - i_errno = IEINITTEST; - return -1; - } +static int create_client_timers(struct iperf_test *test) { + struct iperf_time now; + TimerClientData cd; + if (NULL == test) { + iperf_err(NULL, "No test\n"); + i_errno = IEINITTEST; + return -1; + } - if (iperf_time_now(&now) < 0) { - i_errno = IEINITTEST; - return -1; - } - cd.p = test; - test->timer = test->stats_timer = test->reporter_timer = NULL; - if (test->duration != 0) { - test->done = 0; - test->timer = tmr_create(&now, test_timer_proc, cd, ( test->duration + test->omit ) * SEC_TO_US, 0); - if (test->timer == NULL) { - i_errno = IEINITTEST; - return -1; - } - } - if (test->stats_interval != 0) { - test->stats_timer = tmr_create(&now, client_stats_timer_proc, cd, test->stats_interval * SEC_TO_US, 1); - if (test->stats_timer == NULL) { - i_errno = IEINITTEST; - return -1; - } - } - if (test->reporter_interval != 0) { - test->reporter_timer = tmr_create(&now, client_reporter_timer_proc, cd, test->reporter_interval * SEC_TO_US, 1); - if (test->reporter_timer == NULL) { - i_errno = IEINITTEST; - return -1; - } - } - return 0; + if (iperf_time_now(&now) < 0) { + i_errno = IEINITTEST; + return -1; + } + cd.p = test; + test->timer = test->stats_timer = test->reporter_timer = NULL; + if (test->duration != 0) { + test->done = 0; + test->timer = tmr_create(&now, test_timer_proc, cd, + (test->duration + test->omit) * SEC_TO_US, 0); + if (test->timer == NULL) { + i_errno = IEINITTEST; + return -1; + } + } + if (test->stats_interval != 0) { + test->stats_timer = tmr_create(&now, client_stats_timer_proc, cd, + test->stats_interval * SEC_TO_US, 1); + if (test->stats_timer == NULL) { + i_errno = IEINITTEST; + return -1; + } + } + if (test->reporter_interval != 0) { + test->reporter_timer = tmr_create(&now, client_reporter_timer_proc, cd, + test->reporter_interval * SEC_TO_US, 1); + if (test->reporter_timer == NULL) { + i_errno = IEINITTEST; + return -1; + } + } + return 0; } -static void -client_omit_timer_proc(TimerClientData client_data, struct iperf_time *nowP) -{ - struct iperf_test *test = client_data.p; - - test->omit_timer = NULL; - test->omitting = 0; - iperf_reset_stats(test); - if (test->verbose && !test->json_output && test->reporter_interval == 0) - iperf_printf(test, "%s", report_omit_done); - - /* Reset the timers. */ - if (test->stats_timer != NULL) - tmr_reset(nowP, test->stats_timer); - if (test->reporter_timer != NULL) - tmr_reset(nowP, test->reporter_timer); +static void client_omit_timer_proc(TimerClientData client_data, + struct iperf_time *nowP) { + struct iperf_test *test = client_data.p; + + test->omit_timer = NULL; + test->omitting = 0; + iperf_reset_stats(test); + if (test->verbose && !test->json_output && test->reporter_interval == 0) + iperf_printf(test, "%s", report_omit_done); + + /* Reset the timers. */ + if (test->stats_timer != NULL) + tmr_reset(nowP, test->stats_timer); + if (test->reporter_timer != NULL) + tmr_reset(nowP, test->reporter_timer); } -static int -create_client_omit_timer(struct iperf_test * test) -{ - struct iperf_time now; - TimerClientData cd; - if (NULL == test) - { - iperf_err(NULL, "No test\n"); - return -1; - } +static int create_client_omit_timer(struct iperf_test *test) { + struct iperf_time now; + TimerClientData cd; + if (NULL == test) { + iperf_err(NULL, "No test\n"); + return -1; + } - if (test->omit == 0) { - test->omit_timer = NULL; - test->omitting = 0; - } else { - if (iperf_time_now(&now) < 0) { - i_errno = IEINITTEST; - return -1; - } - test->omitting = 1; - cd.p = test; - test->omit_timer = tmr_create(&now, client_omit_timer_proc, cd, test->omit * SEC_TO_US, 0); - if (test->omit_timer == NULL) { - i_errno = IEINITTEST; - return -1; - } + if (test->omit == 0) { + test->omit_timer = NULL; + test->omitting = 0; + } else { + if (iperf_time_now(&now) < 0) { + i_errno = IEINITTEST; + return -1; } - return 0; + test->omitting = 1; + cd.p = test; + test->omit_timer = + tmr_create(&now, client_omit_timer_proc, cd, test->omit * SEC_TO_US, 0); + if (test->omit_timer == NULL) { + i_errno = IEINITTEST; + return -1; + } + } + return 0; } -int -iperf_handle_message_client(struct iperf_test *test) -{ - int rval; - int32_t err; +int iperf_handle_message_client(struct iperf_test *test) { + int rval; + int32_t err; - if (NULL == test) - { - iperf_err(NULL, "No test\n"); - i_errno = IEINITTEST; + if (NULL == test) { + iperf_err(NULL, "No test\n"); + i_errno = IEINITTEST; + return -1; + } + /*!!! Why is this read() and not Nread()? */ + if ((rval = read(test->ctrl_sck, (char *)&test->state, + sizeof(signed char))) <= 0) { + if (rval == 0) { + i_errno = IECTRLCLOSE; + return -1; + } else { + i_errno = IERECVMESSAGE; + return -1; + } + } + + switch (test->state) { + case PARAM_EXCHANGE: + if (iperf_exchange_parameters(test) < 0) + return -1; + if (test->on_connect) + test->on_connect(test); + break; + case CREATE_STREAMS: + if (test->mode == BIDIRECTIONAL) { + if (iperf_create_streams(test, 1) < 0) return -1; - } - /*!!! Why is this read() and not Nread()? */ - if ((rval = read(test->ctrl_sck, (char*) &test->state, sizeof(signed char))) <= 0) { - if (rval == 0) { - i_errno = IECTRLCLOSE; - return -1; - } else { - i_errno = IERECVMESSAGE; - return -1; - } - } + if (iperf_create_streams(test, 0) < 0) + return -1; + } else if (iperf_create_streams(test, test->mode) < 0) + return -1; + break; + case TEST_START: + if (iperf_init_test(test) < 0) + return -1; + if (create_client_timers(test) < 0) + return -1; + if (create_client_omit_timer(test) < 0) + return -1; + if (test->mode) + if (iperf_create_send_timers(test) < 0) + return -1; + break; + case TEST_RUNNING: + break; + case EXCHANGE_RESULTS: + if (iperf_exchange_results(test) < 0) + return -1; + break; + case DISPLAY_RESULTS: + if (test->on_test_finish) + test->on_test_finish(test); + iperf_client_end(test); + break; + case IPERF_DONE: + break; + case SERVER_TERMINATE: + i_errno = IESERVERTERM; - switch (test->state) { - case PARAM_EXCHANGE: - if (iperf_exchange_parameters(test) < 0) - return -1; - if (test->on_connect) - test->on_connect(test); - break; - case CREATE_STREAMS: - if (test->mode == BIDIRECTIONAL) - { - if (iperf_create_streams(test, 1) < 0) - return -1; - if (iperf_create_streams(test, 0) < 0) - return -1; - } - else if (iperf_create_streams(test, test->mode) < 0) - return -1; - break; - case TEST_START: - if (iperf_init_test(test) < 0) - return -1; - if (create_client_timers(test) < 0) - return -1; - if (create_client_omit_timer(test) < 0) - return -1; - if (test->mode) - if (iperf_create_send_timers(test) < 0) - return -1; - break; - case TEST_RUNNING: - break; - case EXCHANGE_RESULTS: - if (iperf_exchange_results(test) < 0) - return -1; - break; - case DISPLAY_RESULTS: - if (test->on_test_finish) - test->on_test_finish(test); - iperf_client_end(test); - break; - case IPERF_DONE: - break; - case SERVER_TERMINATE: - i_errno = IESERVERTERM; - - /* - * Temporarily be in DISPLAY_RESULTS phase so we can get - * ending summary statistics. - */ - signed char oldstate = test->state; - cpu_util(test->cpu_util); - test->state = DISPLAY_RESULTS; - test->reporter_callback(test); - test->state = oldstate; - return -1; - case ACCESS_DENIED: - i_errno = IEACCESSDENIED; - return -1; - case SERVER_ERROR: - if (Nread(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) { - i_errno = IECTRLREAD; - return -1; - } - i_errno = ntohl(err); - if (Nread(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) { - i_errno = IECTRLREAD; - return -1; - } - errno = ntohl(err); - return -1; - default: - i_errno = IEMESSAGE; - return -1; + /* + * Temporarily be in DISPLAY_RESULTS phase so we can get + * ending summary statistics. + */ + signed char oldstate = test->state; + cpu_util(test->cpu_util); + test->state = DISPLAY_RESULTS; + test->reporter_callback(test); + test->state = oldstate; + return -1; + case ACCESS_DENIED: + i_errno = IEACCESSDENIED; + return -1; + case SERVER_ERROR: + if (Nread(test->ctrl_sck, (char *)&err, sizeof(err), Ptcp) < 0) { + i_errno = IECTRLREAD; + return -1; + } + i_errno = ntohl(err); + if (Nread(test->ctrl_sck, (char *)&err, sizeof(err), Ptcp) < 0) { + i_errno = IECTRLREAD; + return -1; } + errno = ntohl(err); + return -1; + default: + i_errno = IEMESSAGE; + return -1; + } - return 0; + return 0; } - - /* iperf_connect -- client to server connection function */ -int -iperf_connect(struct iperf_test *test) -{ - int opt; - socklen_t len; - - if (NULL == test) - { - iperf_err(NULL, "No test\n"); - return -1; - } - FD_ZERO(&test->read_set); - FD_ZERO(&test->write_set); - - make_cookie(test->cookie); +int iperf_connect(struct iperf_test *test) { + int opt; + socklen_t len; - /* Create and connect the control channel */ - if (test->ctrl_sck < 0) - // Create the control channel using an ephemeral port - test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, 0, test->server_hostname, test->server_port, test->settings->connect_timeout); - if (test->ctrl_sck < 0) { - i_errno = IECONNECT; - return -1; - } + if (NULL == test) { + iperf_err(NULL, "No test\n"); + return -1; + } + FD_ZERO(&test->read_set); + FD_ZERO(&test->write_set); + + make_cookie(test->cookie); + + /* Create and connect the control channel */ + if (test->ctrl_sck < 0) + // Create the control channel using an ephemeral port + test->ctrl_sck = + netdial(test->settings->domain, Ptcp, test->bind_address, + test->bind_dev, 0, test->server_hostname, test->server_port, + test->settings->connect_timeout); + if (test->ctrl_sck < 0) { + i_errno = IECONNECT; + return -1; + } - // set TCP_NODELAY for lower latency on control messages - int flag = 1; - if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int))) { - i_errno = IESETNODELAY; - return -1; - } + // set TCP_NODELAY for lower latency on control messages + int flag = 1; + if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, + sizeof(int))) { + i_errno = IESETNODELAY; + return -1; + } #if defined(HAVE_TCP_USER_TIMEOUT) - if ((opt = test->settings->snd_timeout)) { - if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) { - i_errno = IESETUSERTIMEOUT; - return -1; - } + if ((opt = test->settings->snd_timeout)) { + if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, + sizeof(opt)) < 0) { + i_errno = IESETUSERTIMEOUT; + return -1; } + } #endif /* HAVE_TCP_USER_TIMEOUT */ - if (Nwrite(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) < 0) { - i_errno = IESENDCOOKIE; - return -1; - } - - FD_SET(test->ctrl_sck, &test->read_set); - if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck; - - len = sizeof(opt); - if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_MAXSEG, &opt, &len) < 0) { - test->ctrl_sck_mss = 0; - } - else { - if (opt > 0 && opt <= MAX_UDP_BLOCKSIZE) { - test->ctrl_sck_mss = opt; - } - else { - char str[WARN_STR_LEN]; - snprintf(str, sizeof(str), - "Ignoring nonsense TCP MSS %d", opt); - warning(str); - - test->ctrl_sck_mss = 0; - } - } - - if (test->verbose) { - printf("Control connection MSS %d\n", test->ctrl_sck_mss); + if (Nwrite(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) < 0) { + i_errno = IESENDCOOKIE; + return -1; + } + + FD_SET(test->ctrl_sck, &test->read_set); + if (test->ctrl_sck > test->max_fd) + test->max_fd = test->ctrl_sck; + + len = sizeof(opt); + if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_MAXSEG, &opt, &len) < 0) { + test->ctrl_sck_mss = 0; + } else { + if (opt > 0 && opt <= MAX_UDP_BLOCKSIZE) { + test->ctrl_sck_mss = opt; + } else { + char str[WARN_STR_LEN]; + snprintf(str, sizeof(str), "Ignoring nonsense TCP MSS %d", opt); + warning(str); + + test->ctrl_sck_mss = 0; + } + } + + if (test->verbose) { + printf("Control connection MSS %d\n", test->ctrl_sck_mss); + } + + /* + * If we're doing a UDP test and the block size wasn't explicitly + * set, then use the known MSS of the control connection to pick + * an appropriate default. If we weren't able to get the + * MSS for some reason, then default to something that should + * work on non-jumbo-frame Ethernet networks. The goal is to + * pick a reasonable default that is large but should get from + * sender to receiver without any IP fragmentation. + * + * We assume that the control connection is routed the same as the + * data packets (thus has the same PMTU). Also in the case of + * --reverse tests, we assume that the MTU is the same in both + * directions. Note that even if the algorithm guesses wrong, + * the user always has the option to override. + */ + if (test->protocol->id == Pudp) { + if (test->settings->blksize == 0) { + if (test->ctrl_sck_mss) { + test->settings->blksize = test->ctrl_sck_mss; + } else { + test->settings->blksize = DEFAULT_UDP_BLKSIZE; + } + if (test->verbose) { + printf("Setting UDP block size to %d\n", test->settings->blksize); + } } /* - * If we're doing a UDP test and the block size wasn't explicitly - * set, then use the known MSS of the control connection to pick - * an appropriate default. If we weren't able to get the - * MSS for some reason, then default to something that should - * work on non-jumbo-frame Ethernet networks. The goal is to - * pick a reasonable default that is large but should get from - * sender to receiver without any IP fragmentation. - * - * We assume that the control connection is routed the same as the - * data packets (thus has the same PMTU). Also in the case of - * --reverse tests, we assume that the MTU is the same in both - * directions. Note that even if the algorithm guesses wrong, - * the user always has the option to override. + * Regardless of whether explicitly or implicitly set, if the + * block size is larger than the MSS, print a warning. */ - if (test->protocol->id == Pudp) { - if (test->settings->blksize == 0) { - if (test->ctrl_sck_mss) { - test->settings->blksize = test->ctrl_sck_mss; - } - else { - test->settings->blksize = DEFAULT_UDP_BLKSIZE; - } - if (test->verbose) { - printf("Setting UDP block size to %d\n", test->settings->blksize); - } - } - - /* - * Regardless of whether explicitly or implicitly set, if the - * block size is larger than the MSS, print a warning. - */ - if (test->ctrl_sck_mss > 0 && - test->settings->blksize > test->ctrl_sck_mss) { - char str[WARN_STR_LEN]; - snprintf(str, sizeof(str), - "UDP block size %d exceeds TCP MSS %d, may result in fragmentation / drops", test->settings->blksize, test->ctrl_sck_mss); - warning(str); - } - } - - return 0; + if (test->ctrl_sck_mss > 0 && + test->settings->blksize > test->ctrl_sck_mss) { + char str[WARN_STR_LEN]; + snprintf(str, sizeof(str), + "UDP block size %d exceeds TCP MSS %d, may result in " + "fragmentation / drops", + test->settings->blksize, test->ctrl_sck_mss); + warning(str); + } + } + + return 0; } +int iperf_client_end(struct iperf_test *test) { + if (NULL == test) { + iperf_err(NULL, "No test\n"); + return -1; + } + struct iperf_stream *sp; -int -iperf_client_end(struct iperf_test *test) -{ - if (NULL == test) - { - iperf_err(NULL, "No test\n"); - return -1; - } - struct iperf_stream *sp; - - /* Close all stream sockets */ - SLIST_FOREACH(sp, &test->streams, streams) { - close(sp->socket); - } + /* Close all stream sockets */ + SLIST_FOREACH(sp, &test->streams, streams) { close(sp->socket); } - /* show final summary */ - test->reporter_callback(test); + /* show final summary */ + test->reporter_callback(test); - /* Send response only if no error in server */ - if (test->state > 0) { - if (iperf_set_send_state(test, IPERF_DONE) != 0) - return -1; - } + /* Send response only if no error in server */ + if (test->state > 0) { + if (iperf_set_send_state(test, IPERF_DONE) != 0) + return -1; + } - /* Close control socket */ - if (test->ctrl_sck >= 0) - close(test->ctrl_sck); + /* Close control socket */ + if (test->ctrl_sck >= 0) + close(test->ctrl_sck); - return 0; + return 0; } +int iperf_run_client(struct iperf_test *test) { + int startup; + int result = 0; + fd_set read_set, write_set; + struct iperf_time now; + struct timeval *timeout = NULL; + struct iperf_stream *sp; + struct iperf_time last_receive_time; + struct iperf_time diff_time; + struct timeval used_timeout; + iperf_size_t last_receive_blocks; + int64_t t_usecs; + int64_t timeout_us; + int64_t rcv_timeout_us; + int i_errno_save; + + if (NULL == test) { + iperf_err(NULL, "No test\n"); + return -1; + } + + if (test->logfile) + if (iperf_open_logfile(test) < 0) + return -1; + + if (test->affinity != -1) + if (iperf_setaffinity(test, test->affinity) != 0) + return -1; + + if (test->json_output) + if (iperf_json_start(test) < 0) + return -1; + + if (test->json_output) { + cJSON_AddItemToObject(test->json_start, "version", + cJSON_CreateString(version)); + cJSON_AddItemToObject(test->json_start, "system_info", + cJSON_CreateString(get_system_info())); + } else if (test->verbose) { + iperf_printf(test, "%s\n", version); + iperf_printf(test, "%s", ""); + iperf_printf(test, "%s\n", get_system_info()); + iflush(test); + } + + /* Start the client and connect to the server */ + if (iperf_connect(test) < 0) + goto cleanup_and_fail; + + /* Begin calculating CPU utilization */ + cpu_util(NULL); + if (test->mode != SENDER) + rcv_timeout_us = (test->settings->rcv_timeout.secs * SEC_TO_US) + + test->settings->rcv_timeout.usecs; + else + rcv_timeout_us = 0; + + iperf_time_now( + &last_receive_time); // Initialize last time something was received + last_receive_blocks = 0; + + startup = 1; + while (test->state != IPERF_DONE) { + memcpy(&read_set, &test->read_set, sizeof(fd_set)); + memcpy(&write_set, &test->write_set, sizeof(fd_set)); + iperf_time_now(&now); + timeout = tmr_timeout(&now); + + // In reverse active mode client ensures data is received + if (test->state == TEST_RUNNING && rcv_timeout_us > 0) { + timeout_us = -1; + if (timeout != NULL) { + used_timeout.tv_sec = timeout->tv_sec; + used_timeout.tv_usec = timeout->tv_usec; + timeout_us = (timeout->tv_sec * SEC_TO_US) + timeout->tv_usec; + } + /* Cap the maximum select timeout at 1 second */ + if (timeout_us > SEC_TO_US) { + timeout_us = SEC_TO_US; + } + if (timeout_us < 0 || timeout_us > rcv_timeout_us) { + used_timeout.tv_sec = test->settings->rcv_timeout.secs; + used_timeout.tv_usec = test->settings->rcv_timeout.usecs; + } + timeout = &used_timeout; + } -int -iperf_run_client(struct iperf_test * test) -{ - int startup; - int result = 0; - fd_set read_set, write_set; - struct iperf_time now; - struct timeval* timeout = NULL; - struct iperf_stream *sp; - struct iperf_time last_receive_time; - struct iperf_time diff_time; - struct timeval used_timeout; - iperf_size_t last_receive_blocks; - int64_t t_usecs; - int64_t timeout_us; - int64_t rcv_timeout_us; - int i_errno_save; - - if (NULL == test) - { - iperf_err(NULL, "No test\n"); - return -1; +#if (defined(__vxworks)) || (defined(__VXWORKS__)) + if (timeout != NULL && timeout->tv_sec == 0 && timeout->tv_usec == 0) { + taskDelay(1); } - if (test->logfile) - if (iperf_open_logfile(test) < 0) - return -1; - - if (test->affinity != -1) - if (iperf_setaffinity(test, test->affinity) != 0) - return -1; - - if (test->json_output) - if (iperf_json_start(test) < 0) - return -1; - - if (test->json_output) { - cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version)); - cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info())); - } else if (test->verbose) { - iperf_printf(test, "%s\n", version); - iperf_printf(test, "%s", ""); - iperf_printf(test, "%s\n", get_system_info()); - iflush(test); + result = select(test->max_fd + 1, &read_set, + (test->state == TEST_RUNNING && !test->reverse) ? &write_set + : NULL, + NULL, timeout); +#else + result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout); +#endif // __vxworks or __VXWORKS__ + if (result < 0 && errno != EINTR) { + i_errno = IESELECT; + goto cleanup_and_fail; + } else if (result == 0 && test->state == TEST_RUNNING && + rcv_timeout_us > 0) { + /* + * If nothing was received in non-reverse running state + * then probably something got stuck - either client, + * server or network, and test should be terminated./ + */ + iperf_time_now(&now); + if (iperf_time_diff(&now, &last_receive_time, &diff_time) == 0) { + t_usecs = iperf_time_in_usecs(&diff_time); + if (t_usecs > rcv_timeout_us) { + /* Idle timeout if no new blocks received */ + if (test->blocks_received == last_receive_blocks) { + i_errno = IENOMSG; + goto cleanup_and_fail; + } + } + } } - /* Start the client and connect to the server */ - if (iperf_connect(test) < 0) - goto cleanup_and_fail; + /* See if the test is making progress */ + if (test->blocks_received > last_receive_blocks) { + last_receive_blocks = test->blocks_received; + last_receive_time = now; + } - /* Begin calculating CPU utilization */ - cpu_util(NULL); - if (test->mode != SENDER) - rcv_timeout_us = (test->settings->rcv_timeout.secs * SEC_TO_US) + test->settings->rcv_timeout.usecs; - else - rcv_timeout_us = 0; - - iperf_time_now(&last_receive_time); // Initialize last time something was received - last_receive_blocks = 0; - - startup = 1; - while (test->state != IPERF_DONE) { - memcpy(&read_set, &test->read_set, sizeof(fd_set)); - memcpy(&write_set, &test->write_set, sizeof(fd_set)); - iperf_time_now(&now); - timeout = tmr_timeout(&now); - - // In reverse active mode client ensures data is received - if (test->state == TEST_RUNNING && rcv_timeout_us > 0) { - timeout_us = -1; - if (timeout != NULL) { - used_timeout.tv_sec = timeout->tv_sec; - used_timeout.tv_usec = timeout->tv_usec; - timeout_us = (timeout->tv_sec * SEC_TO_US) + timeout->tv_usec; - } - /* Cap the maximum select timeout at 1 second */ - if (timeout_us > SEC_TO_US) { - timeout_us = SEC_TO_US; - } - if (timeout_us < 0 || timeout_us > rcv_timeout_us) { - used_timeout.tv_sec = test->settings->rcv_timeout.secs; - used_timeout.tv_usec = test->settings->rcv_timeout.usecs; - } - timeout = &used_timeout; + if (result > 0) { + if (FD_ISSET(test->ctrl_sck, &read_set)) { + if (iperf_handle_message_client(test) < 0) { + goto cleanup_and_fail; } + FD_CLR(test->ctrl_sck, &read_set); + } + } -#if (defined(__vxworks)) || (defined(__VXWORKS__)) - if (timeout != NULL && timeout->tv_sec == 0 && timeout->tv_usec == 0) { - taskDelay (1); - } - - result = select(test->max_fd + 1, - &read_set, - (test->state == TEST_RUNNING && !test->reverse) ? &write_set : NULL, - NULL, - timeout); -#else - result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout); -#endif // __vxworks or __VXWORKS__ - if (result < 0 && errno != EINTR) { - i_errno = IESELECT; - goto cleanup_and_fail; - } else if (result == 0 && test->state == TEST_RUNNING && rcv_timeout_us > 0) { - /* - * If nothing was received in non-reverse running state - * then probably something got stuck - either client, - * server or network, and test should be terminated./ - */ - iperf_time_now(&now); - if (iperf_time_diff(&now, &last_receive_time, &diff_time) == 0) { - t_usecs = iperf_time_in_usecs(&diff_time); - if (t_usecs > rcv_timeout_us) { - /* Idle timeout if no new blocks received */ - if (test->blocks_received == last_receive_blocks) { - i_errno = IENOMSG; - goto cleanup_and_fail; - } - } + if (test->state == TEST_RUNNING) { - } - } + /* Is this our first time really running? */ + if (startup) { + startup = 0; - /* See if the test is making progress */ - if (test->blocks_received > last_receive_blocks) { - last_receive_blocks = test->blocks_received; - last_receive_time = now; + /* Create and spin up threads */ + pthread_attr_t attr; + if (pthread_attr_init(&attr) != 0) { + i_errno = IEPTHREADATTRINIT; + goto cleanup_and_fail; } - if (result > 0) { - if (FD_ISSET(test->ctrl_sck, &read_set)) { - if (iperf_handle_message_client(test) < 0) { - goto cleanup_and_fail; - } - FD_CLR(test->ctrl_sck, &read_set); - } - } - - if (test->state == TEST_RUNNING) { - - /* Is this our first time really running? */ - if (startup) { - startup = 0; - - /* Create and spin up threads */ - pthread_attr_t attr; - if (pthread_attr_init(&attr) != 0) { - i_errno = IEPTHREADATTRINIT; - goto cleanup_and_fail; - } - - SLIST_FOREACH(sp, &test->streams, streams) { - if (pthread_create(&(sp->thr), &attr, &iperf_client_worker_run, sp) != 0) { - i_errno = IEPTHREADCREATE; - goto cleanup_and_fail; - } - if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Thread FD %d created\n", sp->socket); - } - } - if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "All threads created\n"); - } - if (pthread_attr_destroy(&attr) != 0) { - i_errno = IEPTHREADATTRDESTROY; - goto cleanup_and_fail; - } - - } - - /* Run the timers. */ - iperf_time_now(&now); - tmr_run(&now); - - /* - * Is the test done yet? We have to be out of omitting - * mode, and then we have to have fulfilled one of the - * ending criteria, either by times, bytes, or blocks. - * The bytes and blocks tests needs to handle both the - * cases of the client being the sender and the client - * being the receiver. - */ - if ((!test->omitting) && - (test->done || - (test->settings->bytes != 0 && (test->bytes_sent >= test->settings->bytes || - test->bytes_received >= test->settings->bytes)) || - (test->settings->blocks != 0 && (test->blocks_sent >= test->settings->blocks || - test->blocks_received >= test->settings->blocks)))) { - - /* Cancel outstanding sender threads */ - SLIST_FOREACH(sp, &test->streams, streams) { - if (sp->sender) { - int rc; - sp->done = 1; - rc = pthread_cancel(sp->thr); - if (rc != 0 && rc != ESRCH) { - i_errno = IEPTHREADCANCEL; - errno = rc; - iperf_err(test, "sender cancel in pthread_cancel - %s", iperf_strerror(i_errno)); - goto cleanup_and_fail; - } - rc = pthread_join(sp->thr, NULL); - if (rc != 0 && rc != ESRCH) { - i_errno = IEPTHREADJOIN; - errno = rc; - iperf_err(test, "sender cancel in pthread_join - %s", iperf_strerror(i_errno)); - goto cleanup_and_fail; - } - if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Thread FD %d stopped\n", sp->socket); - } - } - } - if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Sender threads stopped\n"); - } - - /* Yes, done! Send TEST_END. */ - test->done = 1; - cpu_util(test->cpu_util); - test->stats_callback(test); - if (iperf_set_send_state(test, TEST_END) != 0) - goto cleanup_and_fail; - } - } - } - - /* Cancel outstanding receiver threads */ - SLIST_FOREACH(sp, &test->streams, streams) { - if (!sp->sender) { + SLIST_FOREACH(sp, &test->streams, streams) { + if (pthread_create(&(sp->thr), &attr, &iperf_client_worker_run, sp) != + 0) { + i_errno = IEPTHREADCREATE; + goto cleanup_and_fail; + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Thread FD %d created\n", sp->socket); + } + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "All threads created\n"); + } + if (pthread_attr_destroy(&attr) != 0) { + i_errno = IEPTHREADATTRDESTROY; + goto cleanup_and_fail; + } + } + + /* Run the timers. */ + iperf_time_now(&now); + tmr_run(&now); + + /* + * Is the test done yet? We have to be out of omitting + * mode, and then we have to have fulfilled one of the + * ending criteria, either by times, bytes, or blocks. + * The bytes and blocks tests needs to handle both the + * cases of the client being the sender and the client + * being the receiver. + */ + if ((!test->omitting) && + (test->done || + (test->settings->bytes != 0 && + (test->bytes_sent >= test->settings->bytes || + test->bytes_received >= test->settings->bytes)) || + (test->settings->blocks != 0 && + (test->blocks_sent >= test->settings->blocks || + test->blocks_received >= test->settings->blocks)))) { + + /* Cancel outstanding sender threads */ + SLIST_FOREACH(sp, &test->streams, streams) { + if (sp->sender) { int rc; sp->done = 1; rc = pthread_cancel(sp->thr); if (rc != 0 && rc != ESRCH) { - i_errno = IEPTHREADCANCEL; - errno = rc; - iperf_err(test, "receiver cancel in pthread_cancel - %s", iperf_strerror(i_errno)); - goto cleanup_and_fail; + i_errno = IEPTHREADCANCEL; + errno = rc; + iperf_err(test, "sender cancel in pthread_cancel - %s", + iperf_strerror(i_errno)); + goto cleanup_and_fail; } rc = pthread_join(sp->thr, NULL); if (rc != 0 && rc != ESRCH) { - i_errno = IEPTHREADJOIN; - errno = rc; - iperf_err(test, "receiver cancel in pthread_join - %s", iperf_strerror(i_errno)); - goto cleanup_and_fail; + i_errno = IEPTHREADJOIN; + errno = rc; + iperf_err(test, "sender cancel in pthread_join - %s", + iperf_strerror(i_errno)); + goto cleanup_and_fail; } if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Thread FD %d stopped\n", sp->socket); + iperf_printf(test, "Thread FD %d stopped\n", sp->socket); } - } - } - if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Receiver threads stopped\n"); - } - - if (test->json_output) { - if (iperf_json_finish(test) < 0) - return -1; - } else { - iperf_printf(test, "\n"); - iperf_printf(test, "%s", report_done); - } - - iflush(test); - - return 0; - - cleanup_and_fail: - /* Cancel all outstanding threads */ - i_errno_save = i_errno; - SLIST_FOREACH(sp, &test->streams, streams) { - sp->done = 1; - int rc; - rc = pthread_cancel(sp->thr); - if (rc != 0 && rc != ESRCH) { - i_errno = IEPTHREADCANCEL; - errno = rc; - iperf_err(test, "cleanup_and_fail in pthread_cancel - %s", iperf_strerror(i_errno)); - } - rc = pthread_join(sp->thr, NULL); - if (rc != 0 && rc != ESRCH) { - i_errno = IEPTHREADJOIN; - errno = rc; - iperf_err(test, "cleanup_and_fail in pthread_join - %s", iperf_strerror(i_errno)); + } } if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Thread FD %d stopped\n", sp->socket); + iperf_printf(test, "Sender threads stopped\n"); } - } - if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "All threads stopped\n"); - } - i_errno = i_errno_save; - iperf_client_end(test); - if (test->json_output) { - cJSON_AddStringToObject(test->json_top, "error", iperf_strerror(i_errno)); - iperf_json_finish(test); + /* Yes, done! Send TEST_END. */ + test->done = 1; + cpu_util(test->cpu_util); + test->stats_callback(test); + if (iperf_set_send_state(test, TEST_END) != 0) + goto cleanup_and_fail; + } + } + } + + /* Cancel outstanding receiver threads */ + SLIST_FOREACH(sp, &test->streams, streams) { + if (!sp->sender) { + int rc; + sp->done = 1; + rc = pthread_cancel(sp->thr); + if (rc != 0 && rc != ESRCH) { + i_errno = IEPTHREADCANCEL; + errno = rc; + iperf_err(test, "receiver cancel in pthread_cancel - %s", + iperf_strerror(i_errno)); + goto cleanup_and_fail; + } + rc = pthread_join(sp->thr, NULL); + if (rc != 0 && rc != ESRCH) { + i_errno = IEPTHREADJOIN; + errno = rc; + iperf_err(test, "receiver cancel in pthread_join - %s", + iperf_strerror(i_errno)); + goto cleanup_and_fail; + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Thread FD %d stopped\n", sp->socket); + } + } + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Receiver threads stopped\n"); + } + + if (test->json_output) { + if (iperf_json_finish(test) < 0) + return -1; + } else { + iperf_printf(test, "\n"); + iperf_printf(test, "%s", report_done); + } + + iflush(test); + + return 0; + +cleanup_and_fail: + /* Cancel all outstanding threads */ + i_errno_save = i_errno; + SLIST_FOREACH(sp, &test->streams, streams) { + sp->done = 1; + int rc; + rc = pthread_cancel(sp->thr); + if (rc != 0 && rc != ESRCH) { + i_errno = IEPTHREADCANCEL; + errno = rc; + iperf_err(test, "cleanup_and_fail in pthread_cancel - %s", + iperf_strerror(i_errno)); + } + rc = pthread_join(sp->thr, NULL); + if (rc != 0 && rc != ESRCH) { + i_errno = IEPTHREADJOIN; + errno = rc; + iperf_err(test, "cleanup_and_fail in pthread_join - %s", + iperf_strerror(i_errno)); } - iflush(test); - return -1; + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Thread FD %d stopped\n", sp->socket); + } + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "All threads stopped\n"); + } + i_errno = i_errno_save; + + iperf_client_end(test); + if (test->json_output) { + cJSON_AddStringToObject(test->json_top, "error", iperf_strerror(i_errno)); + iperf_json_finish(test); + } + iflush(test); + return -1; } diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 7d512081c..c74b30b23 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -25,38 +25,37 @@ * file for complete information. */ /* iperf_server_api.c: Functions to be used by an iperf server -*/ + */ -#include -#include -#include -#include -#include -#include +#include #include +#include #include -#include -#include -#include -#include +#include #include -#include -#include -#include +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "iperf.h" #include "iperf_api.h" -#include "iperf_udp.h" +#include "iperf_locale.h" #include "iperf_tcp.h" -#include "iperf_util.h" -#include "timer.h" #include "iperf_time.h" +#include "iperf_udp.h" +#include "iperf_util.h" #include "net.h" +#include "timer.h" #include "units.h" -#include "iperf_util.h" -#include "iperf_locale.h" #if defined(HAVE_TCP_CONGESTION) #if !defined(TCP_CA_NAME_MAX) @@ -64,856 +63,880 @@ #endif /* TCP_CA_NAME_MAX */ #endif /* HAVE_TCP_CONGESTION */ -void * -iperf_server_worker_run(void *s) { - struct iperf_stream *sp = (struct iperf_stream *) s; - struct iperf_test *test = sp->test; +void *iperf_server_worker_run(void *s) { + struct iperf_stream *sp = (struct iperf_stream *)s; + struct iperf_test *test = sp->test; - /* Allow this thread to be cancelled even if it's in a syscall */ - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + /* Allow this thread to be cancelled even if it's in a syscall */ + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); - while (! (test->done) && ! (sp->done)) { - if (sp->sender) { - if (iperf_send_mt(sp) < 0) { - goto cleanup_and_fail; - } - } - else { - if (iperf_recv_mt(sp) < 0) { - goto cleanup_and_fail; - } - } + while (!(test->done) && !(sp->done)) { + if (sp->sender) { + if (iperf_send_mt(sp) < 0) { + goto cleanup_and_fail; + } + } else { + if (iperf_recv_mt(sp) < 0) { + goto cleanup_and_fail; + } } - return NULL; + } + return NULL; - cleanup_and_fail: - return NULL; +cleanup_and_fail: + return NULL; } -int -iperf_server_listen(struct iperf_test *test) -{ - retry: - if((test->listener = netannounce(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, test->server_port)) < 0) { - if (errno == EAFNOSUPPORT && (test->settings->domain == AF_INET6 || test->settings->domain == AF_UNSPEC)) { - /* If we get "Address family not supported by protocol", that - ** probably means we were compiled with IPv6 but the running - ** kernel does not actually do IPv6. This is not too unusual, - ** v6 support is and perhaps always will be spotty. - */ - warning("this system does not seem to support IPv6 - trying IPv4"); - test->settings->domain = AF_INET; - goto retry; - } else { - i_errno = IELISTEN; - return -1; - } +int iperf_server_listen(struct iperf_test *test) { +retry: + if ((test->listener = + netannounce(test->settings->domain, Ptcp, test->bind_address, + test->bind_dev, test->server_port)) < 0) { + if (errno == EAFNOSUPPORT && (test->settings->domain == AF_INET6 || + test->settings->domain == AF_UNSPEC)) { + /* If we get "Address family not supported by protocol", that + ** probably means we were compiled with IPv6 but the running + ** kernel does not actually do IPv6. This is not too unusual, + ** v6 support is and perhaps always will be spotty. + */ + warning("this system does not seem to support IPv6 - trying IPv4"); + test->settings->domain = AF_INET; + goto retry; + } else { + i_errno = IELISTEN; + return -1; } - - if (!test->json_output) { - if (test->server_last_run_rc != 2) - test->server_test_number +=1; - if (test->debug || test->server_last_run_rc != 2) { - iperf_printf(test, "-----------------------------------------------------------\n"); - iperf_printf(test, "Server listening on %d (test #%d)\n", test->server_port, test->server_test_number); - iperf_printf(test, "-----------------------------------------------------------\n"); - if (test->forceflush) - iflush(test); - } + } + + if (!test->json_output) { + if (test->server_last_run_rc != 2) + test->server_test_number += 1; + if (test->debug || test->server_last_run_rc != 2) { + iperf_printf( + test, + "-----------------------------------------------------------\n"); + iperf_printf(test, "Server listening on %d (test #%d)\n", + test->server_port, test->server_test_number); + iperf_printf( + test, + "-----------------------------------------------------------\n"); + if (test->forceflush) + iflush(test); } + } - FD_ZERO(&test->read_set); - FD_ZERO(&test->write_set); - FD_SET(test->listener, &test->read_set); - if (test->listener > test->max_fd) test->max_fd = test->listener; + FD_ZERO(&test->read_set); + FD_ZERO(&test->write_set); + FD_SET(test->listener, &test->read_set); + if (test->listener > test->max_fd) + test->max_fd = test->listener; - return 0; + return 0; } -int -iperf_accept(struct iperf_test *test) -{ - int s; - int ret = -1; - signed char rbuf = ACCESS_DENIED; - socklen_t len; - struct sockaddr_storage addr; - - len = sizeof(addr); - if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) { - i_errno = IEACCEPT; - return ret; +int iperf_accept(struct iperf_test *test) { + int s; + int ret = -1; + signed char rbuf = ACCESS_DENIED; + socklen_t len; + struct sockaddr_storage addr; + + len = sizeof(addr); + if ((s = accept(test->listener, (struct sockaddr *)&addr, &len)) < 0) { + i_errno = IEACCEPT; + return ret; + } + + if (test->ctrl_sck == -1) { + /* Server free, accept new client */ + test->ctrl_sck = s; + // set TCP_NODELAY for lower latency on control messages + int flag = 1; + if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, + sizeof(int))) { + i_errno = IESETNODELAY; + goto error_handling; } - if (test->ctrl_sck == -1) { - /* Server free, accept new client */ - test->ctrl_sck = s; - // set TCP_NODELAY for lower latency on control messages - int flag = 1; - if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int))) { - i_errno = IESETNODELAY; - goto error_handling; - } - #if defined(HAVE_TCP_USER_TIMEOUT) - int opt; - if ((opt = test->settings->snd_timeout)) { - if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) { - i_errno = IESETUSERTIMEOUT; - goto error_handling; - } - } + int opt; + if ((opt = test->settings->snd_timeout)) { + if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) { + i_errno = IESETUSERTIMEOUT; + goto error_handling; + } + } #endif /* HAVE_TCP_USER_TIMEOUT */ - if (Nread(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) != COOKIE_SIZE) { - /* - * Note this error covers both the case of a system error - * or the inability to read the correct amount of data - * (i.e. timed out). - */ - i_errno = IERECVCOOKIE; - goto error_handling; - } + if (Nread(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) != COOKIE_SIZE) { + /* + * Note this error covers both the case of a system error + * or the inability to read the correct amount of data + * (i.e. timed out). + */ + i_errno = IERECVCOOKIE; + goto error_handling; + } FD_SET(test->ctrl_sck, &test->read_set); - if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck; + if (test->ctrl_sck > test->max_fd) + test->max_fd = test->ctrl_sck; if (iperf_set_send_state(test, PARAM_EXCHANGE) != 0) - goto error_handling; + goto error_handling; if (iperf_exchange_parameters(test) < 0) - goto error_handling; + goto error_handling; if (test->server_affinity != -1) - if (iperf_setaffinity(test, test->server_affinity) != 0) - goto error_handling; - if (test->on_connect) - test->on_connect(test); + if (iperf_setaffinity(test, test->server_affinity) != 0) + goto error_handling; + if (test->on_connect) + test->on_connect(test); + } else { + /* + * Don't try to read from the socket. It could block an ongoing test. + * Just send ACCESS_DENIED. + * Also, if sending failed, don't return an error, as the request is not + * related to the ongoing test, and returning an error will terminate the + * test. + */ + if (Nwrite(s, (char *)&rbuf, sizeof(rbuf), Ptcp) < 0) { + if (test->debug) + printf("failed to send ACCESS_DENIED to an unsolicited connection " + "request during active test\n"); } else { - /* - * Don't try to read from the socket. It could block an ongoing test. - * Just send ACCESS_DENIED. - * Also, if sending failed, don't return an error, as the request is not related - * to the ongoing test, and returning an error will terminate the test. - */ - if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp) < 0) { - if (test->debug) - printf("failed to send ACCESS_DENIED to an unsolicited connection request during active test\n"); - } else { - if (test->debug) - printf("successfully sent ACCESS_DENIED to an unsolicited connection request during active test\n"); - } - close(s); + if (test->debug) + printf("successfully sent ACCESS_DENIED to an unsolicited connection " + "request during active test\n"); } - return 0; - error_handling: - close(s); - return ret; + close(s); + } + return 0; +error_handling: + close(s); + return ret; } - /**************************************************************************/ -int -iperf_handle_message_server(struct iperf_test *test) -{ - int rval; - struct iperf_stream *sp; - - // XXX: Need to rethink how this behaves to fit API - if ((rval = Nread(test->ctrl_sck, (char*) &test->state, sizeof(signed char), Ptcp)) <= 0) { - if (rval == 0) { - iperf_err(test, "the client has unexpectedly closed the connection"); - i_errno = IECTRLCLOSE; - test->state = IPERF_DONE; - return 0; - } else { - i_errno = IERECVMESSAGE; - return -1; - } +int iperf_handle_message_server(struct iperf_test *test) { + int rval; + struct iperf_stream *sp; + + // XXX: Need to rethink how this behaves to fit API + if ((rval = Nread(test->ctrl_sck, (char *)&test->state, sizeof(signed char), + Ptcp)) <= 0) { + if (rval == 0) { + iperf_err(test, "the client has unexpectedly closed the connection"); + i_errno = IECTRLCLOSE; + test->state = IPERF_DONE; + return 0; + } else { + i_errno = IERECVMESSAGE; + return -1; } + } - switch(test->state) { - case TEST_START: - break; - case TEST_END: - test->done = 1; - cpu_util(test->cpu_util); - test->stats_callback(test); - SLIST_FOREACH(sp, &test->streams, streams) { - FD_CLR(sp->socket, &test->read_set); - FD_CLR(sp->socket, &test->write_set); - close(sp->socket); - } - test->reporter_callback(test); - if (iperf_set_send_state(test, EXCHANGE_RESULTS) != 0) - return -1; - if (iperf_exchange_results(test) < 0) - return -1; - if (iperf_set_send_state(test, DISPLAY_RESULTS) != 0) - return -1; - if (test->on_test_finish) - test->on_test_finish(test); - break; - case IPERF_DONE: - break; - case CLIENT_TERMINATE: - i_errno = IECLIENTTERM; - - // Temporarily be in DISPLAY_RESULTS phase so we can get - // ending summary statistics. - signed char oldstate = test->state; - cpu_util(test->cpu_util); - test->state = DISPLAY_RESULTS; - test->reporter_callback(test); - test->state = oldstate; - - // XXX: Remove this line below! - iperf_err(test, "the client has terminated"); - SLIST_FOREACH(sp, &test->streams, streams) { - FD_CLR(sp->socket, &test->read_set); - FD_CLR(sp->socket, &test->write_set); - close(sp->socket); - } - test->state = IPERF_DONE; - break; - default: - i_errno = IEMESSAGE; - return -1; + switch (test->state) { + case TEST_START: + break; + case TEST_END: + test->done = 1; + cpu_util(test->cpu_util); + test->stats_callback(test); + SLIST_FOREACH(sp, &test->streams, streams) { + FD_CLR(sp->socket, &test->read_set); + FD_CLR(sp->socket, &test->write_set); + close(sp->socket); } - - return 0; + test->reporter_callback(test); + if (iperf_set_send_state(test, EXCHANGE_RESULTS) != 0) + return -1; + if (iperf_exchange_results(test) < 0) + return -1; + if (iperf_set_send_state(test, DISPLAY_RESULTS) != 0) + return -1; + if (test->on_test_finish) + test->on_test_finish(test); + break; + case IPERF_DONE: + break; + case CLIENT_TERMINATE: + i_errno = IECLIENTTERM; + + // Temporarily be in DISPLAY_RESULTS phase so we can get + // ending summary statistics. + signed char oldstate = test->state; + cpu_util(test->cpu_util); + test->state = DISPLAY_RESULTS; + test->reporter_callback(test); + test->state = oldstate; + + // XXX: Remove this line below! + iperf_err(test, "the client has terminated"); + SLIST_FOREACH(sp, &test->streams, streams) { + FD_CLR(sp->socket, &test->read_set); + FD_CLR(sp->socket, &test->write_set); + close(sp->socket); + } + test->state = IPERF_DONE; + break; + default: + i_errno = IEMESSAGE; + return -1; + } + + return 0; } -static void -server_timer_proc(TimerClientData client_data, struct iperf_time *nowP) -{ - struct iperf_test *test = client_data.p; - struct iperf_stream *sp; - - test->timer = NULL; - if (test->done) - return; - test->done = 1; - /* Free streams */ - while (!SLIST_EMPTY(&test->streams)) { - sp = SLIST_FIRST(&test->streams); - SLIST_REMOVE_HEAD(&test->streams, streams); - close(sp->socket); - iperf_free_stream(sp); - } - close(test->ctrl_sck); - test->ctrl_sck = -1; +static void server_timer_proc(TimerClientData client_data, + struct iperf_time *nowP) { + struct iperf_test *test = client_data.p; + struct iperf_stream *sp; + + test->timer = NULL; + if (test->done) + return; + test->done = 1; + /* Free streams */ + while (!SLIST_EMPTY(&test->streams)) { + sp = SLIST_FIRST(&test->streams); + SLIST_REMOVE_HEAD(&test->streams, streams); + close(sp->socket); + iperf_free_stream(sp); + } + close(test->ctrl_sck); + test->ctrl_sck = -1; } -static void -server_stats_timer_proc(TimerClientData client_data, struct iperf_time *nowP) -{ - struct iperf_test *test = client_data.p; +static void server_stats_timer_proc(TimerClientData client_data, + struct iperf_time *nowP) { + struct iperf_test *test = client_data.p; - if (test->done) - return; - if (test->stats_callback) - test->stats_callback(test); + if (test->done) + return; + if (test->stats_callback) + test->stats_callback(test); } -static void -server_reporter_timer_proc(TimerClientData client_data, struct iperf_time *nowP) -{ - struct iperf_test *test = client_data.p; +static void server_reporter_timer_proc(TimerClientData client_data, + struct iperf_time *nowP) { + struct iperf_test *test = client_data.p; - if (test->done) - return; - if (test->reporter_callback) - test->reporter_callback(test); + if (test->done) + return; + if (test->reporter_callback) + test->reporter_callback(test); } -static int -create_server_timers(struct iperf_test * test) -{ - struct iperf_time now; - TimerClientData cd; - int max_rtt = 4; /* seconds */ - int state_transitions = 10; /* number of state transitions in iperf3 */ - int grace_period = max_rtt * state_transitions; - - if (iperf_time_now(&now) < 0) { - i_errno = IEINITTEST; - return -1; +static int create_server_timers(struct iperf_test *test) { + struct iperf_time now; + TimerClientData cd; + int max_rtt = 4; /* seconds */ + int state_transitions = 10; /* number of state transitions in iperf3 */ + int grace_period = max_rtt * state_transitions; + + if (iperf_time_now(&now) < 0) { + i_errno = IEINITTEST; + return -1; + } + cd.p = test; + test->timer = test->stats_timer = test->reporter_timer = NULL; + if (test->duration != 0) { + test->done = 0; + test->timer = + tmr_create(&now, server_timer_proc, cd, + (test->duration + test->omit + grace_period) * SEC_TO_US, 0); + if (test->timer == NULL) { + i_errno = IEINITTEST; + return -1; } - cd.p = test; - test->timer = test->stats_timer = test->reporter_timer = NULL; - if (test->duration != 0 ) { - test->done = 0; - test->timer = tmr_create(&now, server_timer_proc, cd, (test->duration + test->omit + grace_period) * SEC_TO_US, 0); - if (test->timer == NULL) { - i_errno = IEINITTEST; - return -1; - } - } - - test->stats_timer = test->reporter_timer = NULL; - if (test->stats_interval != 0) { - test->stats_timer = tmr_create(&now, server_stats_timer_proc, cd, test->stats_interval * SEC_TO_US, 1); - if (test->stats_timer == NULL) { - i_errno = IEINITTEST; - return -1; - } + } + + test->stats_timer = test->reporter_timer = NULL; + if (test->stats_interval != 0) { + test->stats_timer = tmr_create(&now, server_stats_timer_proc, cd, + test->stats_interval * SEC_TO_US, 1); + if (test->stats_timer == NULL) { + i_errno = IEINITTEST; + return -1; } - if (test->reporter_interval != 0) { - test->reporter_timer = tmr_create(&now, server_reporter_timer_proc, cd, test->reporter_interval * SEC_TO_US, 1); - if (test->reporter_timer == NULL) { - i_errno = IEINITTEST; - return -1; - } + } + if (test->reporter_interval != 0) { + test->reporter_timer = tmr_create(&now, server_reporter_timer_proc, cd, + test->reporter_interval * SEC_TO_US, 1); + if (test->reporter_timer == NULL) { + i_errno = IEINITTEST; + return -1; } - return 0; + } + return 0; } -static void -server_omit_timer_proc(TimerClientData client_data, struct iperf_time *nowP) -{ - struct iperf_test *test = client_data.p; - - test->omit_timer = NULL; - test->omitting = 0; - iperf_reset_stats(test); - if (test->verbose && !test->json_output && test->reporter_interval == 0) - iperf_printf(test, "%s", report_omit_done); - - /* Reset the timers. */ - if (test->stats_timer != NULL) - tmr_reset(nowP, test->stats_timer); - if (test->reporter_timer != NULL) - tmr_reset(nowP, test->reporter_timer); +static void server_omit_timer_proc(TimerClientData client_data, + struct iperf_time *nowP) { + struct iperf_test *test = client_data.p; + + test->omit_timer = NULL; + test->omitting = 0; + iperf_reset_stats(test); + if (test->verbose && !test->json_output && test->reporter_interval == 0) + iperf_printf(test, "%s", report_omit_done); + + /* Reset the timers. */ + if (test->stats_timer != NULL) + tmr_reset(nowP, test->stats_timer); + if (test->reporter_timer != NULL) + tmr_reset(nowP, test->reporter_timer); } -static int -create_server_omit_timer(struct iperf_test * test) -{ - struct iperf_time now; - TimerClientData cd; +static int create_server_omit_timer(struct iperf_test *test) { + struct iperf_time now; + TimerClientData cd; - if (test->omit == 0) { - test->omit_timer = NULL; - test->omitting = 0; - } else { - if (iperf_time_now(&now) < 0) { - i_errno = IEINITTEST; - return -1; - } - test->omitting = 1; - cd.p = test; - test->omit_timer = tmr_create(&now, server_omit_timer_proc, cd, test->omit * SEC_TO_US, 0); - if (test->omit_timer == NULL) { - i_errno = IEINITTEST; - return -1; - } + if (test->omit == 0) { + test->omit_timer = NULL; + test->omitting = 0; + } else { + if (iperf_time_now(&now) < 0) { + i_errno = IEINITTEST; + return -1; + } + test->omitting = 1; + cd.p = test; + test->omit_timer = + tmr_create(&now, server_omit_timer_proc, cd, test->omit * SEC_TO_US, 0); + if (test->omit_timer == NULL) { + i_errno = IEINITTEST; + return -1; } + } - return 0; + return 0; } -static void -cleanup_server(struct iperf_test *test) -{ - struct iperf_stream *sp; - - /* Cancel outstanding threads */ - int i_errno_save = i_errno; - SLIST_FOREACH(sp, &test->streams, streams) { - int rc; - sp->done = 1; - rc = pthread_cancel(sp->thr); - if (rc != 0 && rc != ESRCH) { - i_errno = IEPTHREADCANCEL; - errno = rc; - iperf_err(test, "cleanup_server in pthread_cancel - %s", iperf_strerror(i_errno)); - } - rc = pthread_join(sp->thr, NULL); - if (rc != 0 && rc != ESRCH) { - i_errno = IEPTHREADJOIN; - errno = rc; - iperf_err(test, "cleanup_server in pthread_join - %s", iperf_strerror(i_errno)); - } - if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Thread FD %d stopped\n", sp->socket); - } +static void cleanup_server(struct iperf_test *test) { + struct iperf_stream *sp; + + /* Cancel outstanding threads */ + int i_errno_save = i_errno; + SLIST_FOREACH(sp, &test->streams, streams) { + int rc; + sp->done = 1; + rc = pthread_cancel(sp->thr); + if (rc != 0 && rc != ESRCH) { + i_errno = IEPTHREADCANCEL; + errno = rc; + iperf_err(test, "cleanup_server in pthread_cancel - %s", + iperf_strerror(i_errno)); + } + rc = pthread_join(sp->thr, NULL); + if (rc != 0 && rc != ESRCH) { + i_errno = IEPTHREADJOIN; + errno = rc; + iperf_err(test, "cleanup_server in pthread_join - %s", + iperf_strerror(i_errno)); } - i_errno = i_errno_save; - if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "All threads stopped\n"); + iperf_printf(test, "Thread FD %d stopped\n", sp->socket); } - - /* Close open streams */ - SLIST_FOREACH(sp, &test->streams, streams) { - if (sp->socket > -1) { - FD_CLR(sp->socket, &test->read_set); - FD_CLR(sp->socket, &test->write_set); - close(sp->socket); - sp->socket = -1; - } + } + i_errno = i_errno_save; + + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "All threads stopped\n"); + } + + /* Close open streams */ + SLIST_FOREACH(sp, &test->streams, streams) { + if (sp->socket > -1) { + FD_CLR(sp->socket, &test->read_set); + FD_CLR(sp->socket, &test->write_set); + close(sp->socket); + sp->socket = -1; } + } - /* Close open test sockets */ - if (test->ctrl_sck > -1) { - close(test->ctrl_sck); - test->ctrl_sck = -1; - } - if (test->listener > -1) { - close(test->listener); - test->listener = -1; - } - if (test->prot_listener > -1) { // May remain open if create socket failed - close(test->prot_listener); - test->prot_listener = -1; - } + /* Close open test sockets */ + if (test->ctrl_sck > -1) { + close(test->ctrl_sck); + test->ctrl_sck = -1; + } + if (test->listener > -1) { + close(test->listener); + test->listener = -1; + } + if (test->prot_listener > -1) { // May remain open if create socket failed + close(test->prot_listener); + test->prot_listener = -1; + } + + /* Cancel any remaining timers. */ + if (test->stats_timer != NULL) { + tmr_cancel(test->stats_timer); + test->stats_timer = NULL; + } + if (test->reporter_timer != NULL) { + tmr_cancel(test->reporter_timer); + test->reporter_timer = NULL; + } + if (test->omit_timer != NULL) { + tmr_cancel(test->omit_timer); + test->omit_timer = NULL; + } + if (test->congestion_used != NULL) { + free(test->congestion_used); + test->congestion_used = NULL; + } + if (test->timer != NULL) { + tmr_cancel(test->timer); + test->timer = NULL; + } +} - /* Cancel any remaining timers. */ - if (test->stats_timer != NULL) { - tmr_cancel(test->stats_timer); - test->stats_timer = NULL; - } - if (test->reporter_timer != NULL) { - tmr_cancel(test->reporter_timer); - test->reporter_timer = NULL; - } - if (test->omit_timer != NULL) { - tmr_cancel(test->omit_timer); - test->omit_timer = NULL; +int iperf_run_server(struct iperf_test *test) { + int result, s; + int send_streams_accepted, rec_streams_accepted; + int streams_to_send = 0, streams_to_rec = 0; +#if defined(HAVE_TCP_CONGESTION) + int saved_errno; +#endif /* HAVE_TCP_CONGESTION */ + fd_set read_set, write_set; + struct iperf_stream *sp; + struct iperf_time now; + struct iperf_time last_receive_time; + struct iperf_time diff_time; + struct timeval *timeout; + struct timeval used_timeout; + iperf_size_t last_receive_blocks; + int flag; + int64_t t_usecs; + int64_t timeout_us; + int64_t rcv_timeout_us; + + if (test->logfile) + if (iperf_open_logfile(test) < 0) + return -2; + + if (test->affinity != -1) + if (iperf_setaffinity(test, test->affinity) != 0) { + cleanup_server(test); + return -2; } - if (test->congestion_used != NULL) { - free(test->congestion_used); - test->congestion_used = NULL; + + if (test->json_output) + if (iperf_json_start(test) < 0) { + cleanup_server(test); + return -2; } - if (test->timer != NULL) { - tmr_cancel(test->timer); - test->timer = NULL; + + if (test->json_output) { + cJSON_AddItemToObject(test->json_start, "version", + cJSON_CreateString(version)); + cJSON_AddItemToObject(test->json_start, "system_info", + cJSON_CreateString(get_system_info())); + } else if (test->verbose) { + iperf_printf(test, "%s\n", version); + iperf_printf(test, "%s", ""); + iperf_printf(test, "%s\n", get_system_info()); + iflush(test); + } + + // Open socket and listen + if (iperf_server_listen(test) < 0) { + cleanup_server(test); + return -2; + } + + iperf_time_now( + &last_receive_time); // Initialize last time something was received + last_receive_blocks = 0; + + test->state = IPERF_START; + send_streams_accepted = 0; + rec_streams_accepted = 0; + rcv_timeout_us = (test->settings->rcv_timeout.secs * SEC_TO_US) + + test->settings->rcv_timeout.usecs; + + while (test->state != IPERF_DONE) { + + // Check if average transfer rate was exceeded (condition set in the + // callback routines) + if (test->bitrate_limit_exceeded) { + cleanup_server(test); + i_errno = IETOTALRATE; + return -1; } -} + memcpy(&read_set, &test->read_set, sizeof(fd_set)); + memcpy(&write_set, &test->write_set, sizeof(fd_set)); + + iperf_time_now(&now); + timeout = tmr_timeout(&now); + + // Ensure select() will timeout to allow handling error cases that require + // server restart + if (test->state == IPERF_START) { // In idle mode server may need to restart + if (timeout == NULL && test->settings->idle_timeout > 0) { + used_timeout.tv_sec = test->settings->idle_timeout; + used_timeout.tv_usec = 0; + timeout = &used_timeout; + } + } else if (test->mode != SENDER) { // In non-reverse active mode server + // ensures data is received + timeout_us = -1; + if (timeout != NULL) { + used_timeout.tv_sec = timeout->tv_sec; + used_timeout.tv_usec = timeout->tv_usec; + timeout_us = (timeout->tv_sec * SEC_TO_US) + timeout->tv_usec; + } + /* Cap the maximum select timeout at 1 second */ + if (timeout_us > SEC_TO_US) { + timeout_us = SEC_TO_US; + } + if (timeout_us < 0 || timeout_us > rcv_timeout_us) { + used_timeout.tv_sec = test->settings->rcv_timeout.secs; + used_timeout.tv_usec = test->settings->rcv_timeout.usecs; + } + timeout = &used_timeout; + } -int -iperf_run_server(struct iperf_test *test) -{ - int result, s; - int send_streams_accepted, rec_streams_accepted; - int streams_to_send = 0, streams_to_rec = 0; -#if defined(HAVE_TCP_CONGESTION) - int saved_errno; -#endif /* HAVE_TCP_CONGESTION */ - fd_set read_set, write_set; - struct iperf_stream *sp; - struct iperf_time now; - struct iperf_time last_receive_time; - struct iperf_time diff_time; - struct timeval* timeout; - struct timeval used_timeout; - iperf_size_t last_receive_blocks; - int flag; - int64_t t_usecs; - int64_t timeout_us; - int64_t rcv_timeout_us; - - if (test->logfile) - if (iperf_open_logfile(test) < 0) - return -2; - - if (test->affinity != -1) - if (iperf_setaffinity(test, test->affinity) != 0) { + result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout); + if (result < 0 && errno != EINTR) { + cleanup_server(test); + i_errno = IESELECT; + return -1; + } else if (result == 0) { + /* + * If nothing was received during the specified time (per + * state) then probably something got stuck either at the + * client, server or network, and test should be forced to + * end. + */ + iperf_time_now(&now); + t_usecs = 0; + if (iperf_time_diff(&now, &last_receive_time, &diff_time) == 0) { + t_usecs = iperf_time_in_usecs(&diff_time); + + /* We're in the state where we're still accepting connections */ + if (test->state == IPERF_START) { + if (test->settings->idle_timeout > 0 && + t_usecs >= test->settings->idle_timeout * SEC_TO_US) { + test->server_forced_idle_restarts_count += 1; + if (test->debug) + printf("Server restart (#%d) in idle state as no connection " + "request was received for %d sec\n", + test->server_forced_idle_restarts_count, + test->settings->idle_timeout); cleanup_server(test); - return -2; + if (iperf_get_test_one_off(test)) { + if (test->debug) + printf("No connection request was received for %d sec in " + "one-off mode; exiting.\n", + test->settings->idle_timeout); + exit(0); + } + + return 2; + } } - if (test->json_output) - if (iperf_json_start(test) < 0) { + /* + * Running a test. If we're receiving, be sure we're making + * progress (sender hasn't died/crashed). + */ + else if (test->mode != SENDER && t_usecs > rcv_timeout_us) { + /* Idle timeout if no new blocks received */ + if (test->blocks_received == last_receive_blocks) { + test->server_forced_no_msg_restarts_count += 1; + i_errno = IENOMSG; + if (iperf_get_verbose(test)) + iperf_err(test, + "Server restart (#%d) during active test due to idle " + "timeout for receiving data", + test->server_forced_no_msg_restarts_count); cleanup_server(test); - return -2; + return -1; + } } - - if (test->json_output) { - cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version)); - cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info())); - } else if (test->verbose) { - iperf_printf(test, "%s\n", version); - iperf_printf(test, "%s", ""); - iperf_printf(test, "%s\n", get_system_info()); - iflush(test); + } } - // Open socket and listen - if (iperf_server_listen(test) < 0) { - cleanup_server(test); - return -2; + /* See if the test is making progress */ + if (test->blocks_received > last_receive_blocks) { + last_receive_blocks = test->blocks_received; + last_receive_time = now; } - iperf_time_now(&last_receive_time); // Initialize last time something was received - last_receive_blocks = 0; - - test->state = IPERF_START; - send_streams_accepted = 0; - rec_streams_accepted = 0; - rcv_timeout_us = (test->settings->rcv_timeout.secs * SEC_TO_US) + test->settings->rcv_timeout.usecs; + if (result > 0) { + if (FD_ISSET(test->listener, &read_set)) { + if (test->state != CREATE_STREAMS) { + if (iperf_accept(test) < 0) { + cleanup_server(test); + return -1; + } + FD_CLR(test->listener, &read_set); + + // Set streams number + if (test->mode == BIDIRECTIONAL) { + streams_to_send = test->num_streams; + streams_to_rec = test->num_streams; + } else if (test->mode == RECEIVER) { + streams_to_rec = test->num_streams; + streams_to_send = 0; + } else { + streams_to_send = test->num_streams; + streams_to_rec = 0; + } + } + } + if (FD_ISSET(test->ctrl_sck, &read_set)) { + if (iperf_handle_message_server(test) < 0) { + cleanup_server(test); + return -1; + } + FD_CLR(test->ctrl_sck, &read_set); + } - while (test->state != IPERF_DONE) { + if (test->state == CREATE_STREAMS) { + if (FD_ISSET(test->prot_listener, &read_set)) { - // Check if average transfer rate was exceeded (condition set in the callback routines) - if (test->bitrate_limit_exceeded) { - cleanup_server(test); - i_errno = IETOTALRATE; + if ((s = test->protocol->accept(test)) < 0) { + cleanup_server(test); return -1; - } + } - memcpy(&read_set, &test->read_set, sizeof(fd_set)); - memcpy(&write_set, &test->write_set, sizeof(fd_set)); + /* apply other common socket options */ + if (iperf_common_sockopts(test, s) < 0) { + cleanup_server(test); + return -1; + } - iperf_time_now(&now); - timeout = tmr_timeout(&now); + if (!is_closed(s)) { - // Ensure select() will timeout to allow handling error cases that require server restart - if (test->state == IPERF_START) { // In idle mode server may need to restart - if (timeout == NULL && test->settings->idle_timeout > 0) { - used_timeout.tv_sec = test->settings->idle_timeout; - used_timeout.tv_usec = 0; - timeout = &used_timeout; - } - } else if (test->mode != SENDER) { // In non-reverse active mode server ensures data is received - timeout_us = -1; - if (timeout != NULL) { - used_timeout.tv_sec = timeout->tv_sec; - used_timeout.tv_usec = timeout->tv_usec; - timeout_us = (timeout->tv_sec * SEC_TO_US) + timeout->tv_usec; - } - /* Cap the maximum select timeout at 1 second */ - if (timeout_us > SEC_TO_US) { - timeout_us = SEC_TO_US; - } - if (timeout_us < 0 || timeout_us > rcv_timeout_us) { - used_timeout.tv_sec = test->settings->rcv_timeout.secs; - used_timeout.tv_usec = test->settings->rcv_timeout.usecs; +#if defined(HAVE_TCP_USER_TIMEOUT) + if (test->protocol->id == Ptcp) { + int opt; + if ((opt = test->settings->snd_timeout)) { + if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, + sizeof(opt)) < 0) { + saved_errno = errno; + close(s); + cleanup_server(test); + errno = saved_errno; + i_errno = IESETUSERTIMEOUT; + return -1; + } + } } - timeout = &used_timeout; - } +#endif /* HAVE_TCP_USER_TIMEOUT */ - result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout); - if (result < 0 && errno != EINTR) { - cleanup_server(test); - i_errno = IESELECT; - return -1; - } else if (result == 0) { - /* - * If nothing was received during the specified time (per - * state) then probably something got stuck either at the - * client, server or network, and test should be forced to - * end. - */ - iperf_time_now(&now); - t_usecs = 0; - if (iperf_time_diff(&now, &last_receive_time, &diff_time) == 0) { - t_usecs = iperf_time_in_usecs(&diff_time); - - /* We're in the state where we're still accepting connections */ - if (test->state == IPERF_START) { - if (test->settings->idle_timeout > 0 && t_usecs >= test->settings->idle_timeout * SEC_TO_US) { - test->server_forced_idle_restarts_count += 1; - if (test->debug) - printf("Server restart (#%d) in idle state as no connection request was received for %d sec\n", - test->server_forced_idle_restarts_count, test->settings->idle_timeout); - cleanup_server(test); - if ( iperf_get_test_one_off(test) ) { - if (test->debug) - printf("No connection request was received for %d sec in one-off mode; exiting.\n", - test->settings->idle_timeout); - exit(0); - } - - return 2; - } +#if defined(HAVE_TCP_CONGESTION) + if (test->protocol->id == Ptcp) { + if (test->congestion) { + if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, + strlen(test->congestion)) < 0) { + /* + * ENOENT means we tried to set the + * congestion algorithm but the algorithm + * specified doesn't exist. This can happen + * if the client and server have different + * congestion algorithms available. In this + * case, print a warning, but otherwise + * continue. + */ + if (errno == ENOENT) { + warning("TCP congestion control algorithm not supported"); + } else { + saved_errno = errno; + close(s); + cleanup_server(test); + errno = saved_errno; + i_errno = IESETCONGESTION; + return -1; + } + } + } + { + socklen_t len = TCP_CA_NAME_MAX; + char ca[TCP_CA_NAME_MAX + 1]; + int rc; + rc = getsockopt(s, IPPROTO_TCP, TCP_CONGESTION, ca, &len); + if (rc < 0 && test->congestion) { + saved_errno = errno; + close(s); + cleanup_server(test); + errno = saved_errno; + i_errno = IESETCONGESTION; + return -1; } - /* - * Running a test. If we're receiving, be sure we're making - * progress (sender hasn't died/crashed). + * If not the first connection, discard prior + * congestion algorithm name so we don't leak + * duplicated strings. We probably don't need + * the old string anyway. */ - else if (test->mode != SENDER && t_usecs > rcv_timeout_us) { - /* Idle timeout if no new blocks received */ - if (test->blocks_received == last_receive_blocks) { - test->server_forced_no_msg_restarts_count += 1; - i_errno = IENOMSG; - if (iperf_get_verbose(test)) - iperf_err(test, "Server restart (#%d) during active test due to idle timeout for receiving data", - test->server_forced_no_msg_restarts_count); - cleanup_server(test); - return -1; - } + if (test->congestion_used != NULL) { + free(test->congestion_used); } - } - } - - /* See if the test is making progress */ - if (test->blocks_received > last_receive_blocks) { - last_receive_blocks = test->blocks_received; - last_receive_time = now; - } - - if (result > 0) { - if (FD_ISSET(test->listener, &read_set)) { - if (test->state != CREATE_STREAMS) { - if (iperf_accept(test) < 0) { - cleanup_server(test); - return -1; - } - FD_CLR(test->listener, &read_set); - - // Set streams number - if (test->mode == BIDIRECTIONAL) { - streams_to_send = test->num_streams; - streams_to_rec = test->num_streams; - } else if (test->mode == RECEIVER) { - streams_to_rec = test->num_streams; - streams_to_send = 0; - } else { - streams_to_send = test->num_streams; - streams_to_rec = 0; - } + // Set actual used congestion alg, or set to unknown if could + // not get it + if (rc < 0) + test->congestion_used = strdup("unknown"); + else + test->congestion_used = strdup(ca); + if (test->debug) { + printf("Congestion algorithm is %s\n", test->congestion_used); } + } } - if (FD_ISSET(test->ctrl_sck, &read_set)) { - if (iperf_handle_message_server(test) < 0) { - cleanup_server(test); - return -1; - } - FD_CLR(test->ctrl_sck, &read_set); - } +#endif /* HAVE_TCP_CONGESTION */ - if (test->state == CREATE_STREAMS) { - if (FD_ISSET(test->prot_listener, &read_set)) { + if (rec_streams_accepted != streams_to_rec) { + flag = 0; + ++rec_streams_accepted; + } else if (send_streams_accepted != streams_to_send) { + flag = 1; + ++send_streams_accepted; + } - if ((s = test->protocol->accept(test)) < 0) { - cleanup_server(test); - return -1; - } + if (flag != -1) { + sp = iperf_new_stream(test, s, flag); + if (!sp) { + cleanup_server(test); + return -1; + } - /* apply other common socket options */ - if (iperf_common_sockopts(test, s) < 0) - { - cleanup_server(test); - return -1; - } + if (s > test->max_fd) + test->max_fd = s; - if (!is_closed(s)) { + if (test->on_new_stream) + test->on_new_stream(sp); -#if defined(HAVE_TCP_USER_TIMEOUT) - if (test->protocol->id == Ptcp) { - int opt; - if ((opt = test->settings->snd_timeout)) { - if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) { - saved_errno = errno; - close(s); - cleanup_server(test); - errno = saved_errno; - i_errno = IESETUSERTIMEOUT; - return -1; - } - } - } -#endif /* HAVE_TCP_USER_TIMEOUT */ + flag = -1; + } + } + FD_CLR(test->prot_listener, &read_set); + } -#if defined(HAVE_TCP_CONGESTION) - if (test->protocol->id == Ptcp) { - if (test->congestion) { - if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) { - /* - * ENOENT means we tried to set the - * congestion algorithm but the algorithm - * specified doesn't exist. This can happen - * if the client and server have different - * congestion algorithms available. In this - * case, print a warning, but otherwise - * continue. - */ - if (errno == ENOENT) { - warning("TCP congestion control algorithm not supported"); - } - else { - saved_errno = errno; - close(s); - cleanup_server(test); - errno = saved_errno; - i_errno = IESETCONGESTION; - return -1; - } - } - } - { - socklen_t len = TCP_CA_NAME_MAX; - char ca[TCP_CA_NAME_MAX + 1]; - int rc; - rc = getsockopt(s, IPPROTO_TCP, TCP_CONGESTION, ca, &len); - if (rc < 0 && test->congestion) { - saved_errno = errno; - close(s); - cleanup_server(test); - errno = saved_errno; - i_errno = IESETCONGESTION; - return -1; - } - /* - * If not the first connection, discard prior - * congestion algorithm name so we don't leak - * duplicated strings. We probably don't need - * the old string anyway. - */ - if (test->congestion_used != NULL) { - free(test->congestion_used); - } - // Set actual used congestion alg, or set to unknown if could not get it - if (rc < 0) - test->congestion_used = strdup("unknown"); - else - test->congestion_used = strdup(ca); - if (test->debug) { - printf("Congestion algorithm is %s\n", test->congestion_used); - } - } - } -#endif /* HAVE_TCP_CONGESTION */ + if (rec_streams_accepted == streams_to_rec && + send_streams_accepted == streams_to_send) { + if (test->protocol->id != Ptcp) { + FD_CLR(test->prot_listener, &test->read_set); + close(test->prot_listener); + test->prot_listener = -1; + } else { + if (test->no_delay || test->settings->mss || + test->settings->socket_bufsize) { + FD_CLR(test->listener, &test->read_set); + close(test->listener); + test->listener = -1; + if ((s = netannounce(test->settings->domain, Ptcp, + test->bind_address, test->bind_dev, + test->server_port)) < 0) { + cleanup_server(test); + i_errno = IELISTEN; + return -1; + } + test->listener = s; + FD_SET(test->listener, &test->read_set); + if (test->listener > test->max_fd) + test->max_fd = test->listener; + } + } + test->prot_listener = -1; + + /* Ensure that total requested data rate is not above limit */ + iperf_size_t total_requested_rate = + test->num_streams * test->settings->rate * + (test->mode == BIDIRECTIONAL ? 2 : 1); + if (test->settings->bitrate_limit > 0 && + total_requested_rate > test->settings->bitrate_limit) { + if (iperf_get_verbose(test)) + iperf_err(test, + "Client total requested throughput rate of %" PRIu64 + " bps exceeded %" PRIu64 " bps limit", + total_requested_rate, test->settings->bitrate_limit); + cleanup_server(test); + i_errno = IETOTALRATE; + return -1; + } - if (rec_streams_accepted != streams_to_rec) { - flag = 0; - ++rec_streams_accepted; - } else if (send_streams_accepted != streams_to_send) { - flag = 1; - ++send_streams_accepted; - } - - if (flag != -1) { - sp = iperf_new_stream(test, s, flag); - if (!sp) { - cleanup_server(test); - return -1; - } - - if (s > test->max_fd) test->max_fd = s; - - if (test->on_new_stream) - test->on_new_stream(sp); - - flag = -1; - } - } - FD_CLR(test->prot_listener, &read_set); - } + // Begin calculating CPU utilization + cpu_util(NULL); + if (iperf_set_send_state(test, TEST_START) != 0) { + cleanup_server(test); + return -1; + } + if (iperf_init_test(test) < 0) { + cleanup_server(test); + return -1; + } + if (create_server_timers(test) < 0) { + cleanup_server(test); + return -1; + } + if (create_server_omit_timer(test) < 0) { + cleanup_server(test); + return -1; + } + if (test->mode != RECEIVER) + if (iperf_create_send_timers(test) < 0) { + cleanup_server(test); + return -1; + } + if (iperf_set_send_state(test, TEST_RUNNING) != 0) { + cleanup_server(test); + return -1; + } - if (rec_streams_accepted == streams_to_rec && send_streams_accepted == streams_to_send) { - if (test->protocol->id != Ptcp) { - FD_CLR(test->prot_listener, &test->read_set); - close(test->prot_listener); - test->prot_listener = -1; - } else { - if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) { - FD_CLR(test->listener, &test->read_set); - close(test->listener); - test->listener = -1; - if ((s = netannounce(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, test->server_port)) < 0) { - cleanup_server(test); - i_errno = IELISTEN; - return -1; - } - test->listener = s; - FD_SET(test->listener, &test->read_set); - if (test->listener > test->max_fd) test->max_fd = test->listener; - } - } - test->prot_listener = -1; - - /* Ensure that total requested data rate is not above limit */ - iperf_size_t total_requested_rate = test->num_streams * test->settings->rate * (test->mode == BIDIRECTIONAL? 2 : 1); - if (test->settings->bitrate_limit > 0 && total_requested_rate > test->settings->bitrate_limit) { - if (iperf_get_verbose(test)) - iperf_err(test, "Client total requested throughput rate of %" PRIu64 " bps exceeded %" PRIu64 " bps limit", - total_requested_rate, test->settings->bitrate_limit); - cleanup_server(test); - i_errno = IETOTALRATE; - return -1; - } - - // Begin calculating CPU utilization - cpu_util(NULL); - - if (iperf_set_send_state(test, TEST_START) != 0) { - cleanup_server(test); - return -1; - } - if (iperf_init_test(test) < 0) { - cleanup_server(test); - return -1; - } - if (create_server_timers(test) < 0) { - cleanup_server(test); - return -1; - } - if (create_server_omit_timer(test) < 0) { - cleanup_server(test); - return -1; - } - if (test->mode != RECEIVER) - if (iperf_create_send_timers(test) < 0) { - cleanup_server(test); - return -1; - } - if (iperf_set_send_state(test, TEST_RUNNING) != 0) { - cleanup_server(test); - return -1; - } - - /* Create and spin up threads */ - pthread_attr_t attr; - if (pthread_attr_init(&attr) != 0) { - i_errno = IEPTHREADATTRINIT; - cleanup_server(test); - }; - - SLIST_FOREACH(sp, &test->streams, streams) { - if (pthread_create(&(sp->thr), &attr, &iperf_server_worker_run, sp) != 0) { - i_errno = IEPTHREADCREATE; - cleanup_server(test); - return -1; - } - if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "Thread FD %d created\n", sp->socket); - } - } - if (test->debug_level >= DEBUG_LEVEL_INFO) { - iperf_printf(test, "All threads created\n"); - } - if (pthread_attr_destroy(&attr) != 0) { - i_errno = IEPTHREADATTRDESTROY; - cleanup_server(test); - }; - } + /* Create and spin up threads */ + pthread_attr_t attr; + if (pthread_attr_init(&attr) != 0) { + i_errno = IEPTHREADATTRINIT; + cleanup_server(test); + }; + + SLIST_FOREACH(sp, &test->streams, streams) { + if (pthread_create(&(sp->thr), &attr, &iperf_server_worker_run, + sp) != 0) { + i_errno = IEPTHREADCREATE; + cleanup_server(test); + return -1; + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "Thread FD %d created\n", sp->socket); } + } + if (test->debug_level >= DEBUG_LEVEL_INFO) { + iperf_printf(test, "All threads created\n"); + } + if (pthread_attr_destroy(&attr) != 0) { + i_errno = IEPTHREADATTRDESTROY; + cleanup_server(test); + }; } - - if (result == 0 || - (timeout != NULL && timeout->tv_sec == 0 && timeout->tv_usec == 0)) { - /* Run the timers. */ - iperf_time_now(&now); - tmr_run(&now); - } + } } - - if (test->json_output) { - if (iperf_json_finish(test) < 0) - return -1; + if (result == 0 || + (timeout != NULL && timeout->tv_sec == 0 && timeout->tv_usec == 0)) { + /* Run the timers. */ + iperf_time_now(&now); + tmr_run(&now); } + } - iflush(test); - cleanup_server(test); + if (test->json_output) { + if (iperf_json_finish(test) < 0) + return -1; + } - if (test->server_affinity != -1) - if (iperf_clearaffinity(test) != 0) - return -1; + iflush(test); + cleanup_server(test); + + if (test->server_affinity != -1) + if (iperf_clearaffinity(test) != 0) + return -1; - return 0; + return 0; }