feat: switch iperf3 to JNI with 3.19 source
- Switch from bundled iperf3 binary (Process.start) to JNI compilation - iperf3 3.21 source API was incompatible (start_time, iperf_strerror, etc.) - iperf3 3.19 source is proven to work (used by CellularLab) - CMakeLists.txt updated: config.h generated into cpp/iperf/ for correct include paths - iperf_jni.c fixed for 3.19 API (no start_time on iperf_test) - getTestProgress() returns g_test->duration as fallback (no real-time elapsed tracking yet) - NDK CMake pinned to 3.22.1 - UI layer updated: removed Iperf3BinaryPathProvider references - Tests updated: TestRunViewModelTest matches new service interface - pubspec.yaml: removed iperf3 assets bundle, added http dependency - AndroidManifest: INTERNET permission (needed for hostname resolution)
This commit is contained in:
@@ -22,11 +22,14 @@ android {
|
||||
minSdk = flutter.minSdkVersion
|
||||
targetSdk = flutter.targetSdkVersion
|
||||
// Uses the version code from pubspec.yaml. When using split APKs, 1000 * ABI_VERSION
|
||||
// is added automatically by Flutter. (https://developer.android.com/studio/build/configure-apk-splits#configure-APK-versions)
|
||||
// is added automatically by Flutter. (https://developer.android.com/studio/build/configure-apk-splits#configure-apk-versions)
|
||||
// You can force using the value of versionCode by specifying the `-P force-version-code-ignoring-abi=true`
|
||||
// flag during build.
|
||||
versionCode = flutter.versionCode
|
||||
versionName = flutter.versionName
|
||||
ndk {
|
||||
abiFilters += listOf("armeabi-v7a", "arm64-v8a")
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -36,6 +39,13 @@ android {
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path = file("src/main/cpp/CMakeLists.txt")
|
||||
version = "3.22.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
cmake_minimum_required(VERSION 3.22.1)
|
||||
project("iperf3jni")
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# Paths
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# CMAKE_SOURCE_DIR here is android/app/src/main/cpp/
|
||||
set(IPERF_SRC_DIR ${CMAKE_SOURCE_DIR}/iperf)
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# Generate iperf_config.h from our Android template, into cpp/iperf/
|
||||
# (iperf.h uses #include "iperf_config.h" which resolves relative to its dir)
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
configure_file(
|
||||
${IPERF_SRC_DIR}/iperf_config_android.h
|
||||
${IPERF_SRC_DIR}/iperf_config.h
|
||||
)
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# Shared library: iperf3jni
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
add_library(iperf3jni SHARED
|
||||
|
||||
# JNI bridge (directly in cpp/)
|
||||
iperf_jni.c
|
||||
|
||||
# iperf3 source files (in cpp/iperf/)
|
||||
iperf/iperf_api.c
|
||||
iperf/iperf_client_api.c
|
||||
iperf/iperf_util.c
|
||||
iperf/iperf_udp.c
|
||||
iperf/iperf_tcp.c
|
||||
iperf/iperf_server_api.c
|
||||
iperf/iperf_sctp.c
|
||||
iperf/iperf_error.c
|
||||
iperf/iperf_time.c
|
||||
iperf/iperf_locale.c
|
||||
iperf/iperf_pthread.c
|
||||
iperf/iperf_auth.c
|
||||
iperf/net.c
|
||||
iperf/timer.c
|
||||
iperf/units.c
|
||||
iperf/cjson.c
|
||||
iperf/dscp.c
|
||||
iperf/tcp_info.c
|
||||
)
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# Include directories - iperf/ FIRST so "iperf_config.h" resolves there
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
target_include_directories(iperf3jni PRIVATE
|
||||
${IPERF_SRC_DIR} # for iperf headers (iperf_config.h, iperf.h, etc.)
|
||||
${CMAKE_SOURCE_DIR} # for JNI includes
|
||||
)
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# Compile definitions
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
target_compile_definitions(iperf3jni PRIVATE
|
||||
HAVE_PTHREAD
|
||||
__ANDROID__
|
||||
# Disable the main() entry point — we're a shared lib, not a standalone binary
|
||||
IPERF_JNI_BUILD=1
|
||||
)
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# Link libraries
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(iperf3jni
|
||||
Threads::Threads
|
||||
log
|
||||
android
|
||||
)
|
||||
@@ -0,0 +1,104 @@
|
||||
lib_LTLIBRARIES = libiperf.la # Build and install an iperf library
|
||||
bin_PROGRAMS = iperf3 # Build and install an iperf binary
|
||||
if ENABLE_PROFILING
|
||||
noinst_PROGRAMS = t_timer t_units t_uuid t_api t_auth iperf3_profile # Build, but don't install the test programs and a profiled version of iperf3
|
||||
else
|
||||
noinst_PROGRAMS = t_timer t_units t_uuid t_api t_auth # Build, but don't install the test programs
|
||||
endif
|
||||
include_HEADERS = iperf_api.h # Defines the headers that get installed with the program
|
||||
|
||||
|
||||
# Specify the source files and flags for the iperf library
|
||||
libiperf_la_SOURCES = \
|
||||
cjson.c \
|
||||
cjson.h \
|
||||
flowlabel.h \
|
||||
iperf.h \
|
||||
iperf_api.c \
|
||||
iperf_api.h \
|
||||
iperf_error.c \
|
||||
iperf_auth.h \
|
||||
iperf_auth.c \
|
||||
iperf_client_api.c \
|
||||
iperf_locale.c \
|
||||
iperf_locale.h \
|
||||
iperf_server_api.c \
|
||||
iperf_tcp.c \
|
||||
iperf_tcp.h \
|
||||
iperf_udp.c \
|
||||
iperf_udp.h \
|
||||
iperf_sctp.c \
|
||||
iperf_sctp.h \
|
||||
iperf_util.c \
|
||||
iperf_util.h \
|
||||
iperf_time.c \
|
||||
iperf_time.h \
|
||||
iperf_pthread.c \
|
||||
iperf_pthread.h \
|
||||
dscp.c \
|
||||
net.c \
|
||||
net.h \
|
||||
portable_endian.h \
|
||||
queue.h \
|
||||
tcp_info.c \
|
||||
timer.c \
|
||||
timer.h \
|
||||
units.c \
|
||||
units.h \
|
||||
version.h
|
||||
|
||||
# Specify the sources and various flags for the iperf binary
|
||||
iperf3_SOURCES = main.c
|
||||
iperf3_CFLAGS = -g
|
||||
iperf3_LDADD = libiperf.la
|
||||
iperf3_LDFLAGS = -g
|
||||
|
||||
if ENABLE_PROFILING
|
||||
# If the iperf-profiled-binary is enabled
|
||||
# Specify the sources and various flags for the profiled iperf binary. This
|
||||
# binary recompiles all the source files to make sure they are all profiled.
|
||||
iperf3_profile_SOURCES = main.c \
|
||||
$(libiperf_la_SOURCES)
|
||||
|
||||
iperf3_profile_CFLAGS = -pg -g
|
||||
iperf3_profile_LDADD = libiperf.la
|
||||
iperf3_profile_LDFLAGS = -pg -g
|
||||
endif
|
||||
|
||||
# Specify the sources and various flags for the test cases
|
||||
t_timer_SOURCES = t_timer.c
|
||||
t_timer_CFLAGS = -g
|
||||
t_timer_LDFLAGS =
|
||||
t_timer_LDADD = libiperf.la
|
||||
|
||||
t_units_SOURCES = t_units.c
|
||||
t_units_CFLAGS = -g
|
||||
t_units_LDFLAGS =
|
||||
t_units_LDADD = libiperf.la
|
||||
|
||||
t_uuid_SOURCES = t_uuid.c
|
||||
t_uuid_CFLAGS = -g
|
||||
t_uuid_LDFLAGS =
|
||||
t_uuid_LDADD = libiperf.la
|
||||
|
||||
t_api_SOURCES = t_api.c
|
||||
t_api_CFLAGS = -g
|
||||
t_api_LDFLAGS =
|
||||
t_api_LDADD = libiperf.la
|
||||
|
||||
t_auth_SOURCES = t_auth.c
|
||||
t_auth_CFLAGS = -g
|
||||
t_auth_LDFLAGS =
|
||||
t_auth_LDADD = libiperf.la
|
||||
|
||||
|
||||
|
||||
# Specify which tests to run during a "make check"
|
||||
TESTS = \
|
||||
t_timer \
|
||||
t_units \
|
||||
t_uuid \
|
||||
t_api \
|
||||
t_auth
|
||||
|
||||
dist_man_MANS = iperf3.1 libiperf.3
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef cJSON__h
|
||||
#define cJSON__h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
|
||||
#define __WINDOWS__
|
||||
#endif
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
|
||||
|
||||
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
|
||||
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
|
||||
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
|
||||
|
||||
For *nix builds that support visibility attribute, you can define similar behavior by
|
||||
|
||||
setting default visibility to hidden by adding
|
||||
-fvisibility=hidden (for gcc)
|
||||
or
|
||||
-xldscope=hidden (for sun cc)
|
||||
to CFLAGS
|
||||
|
||||
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
|
||||
|
||||
*/
|
||||
|
||||
#define CJSON_CDECL __cdecl
|
||||
#define CJSON_STDCALL __stdcall
|
||||
|
||||
/* export symbols by default, this is necessary for copy pasting the C and header file */
|
||||
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
|
||||
#define CJSON_EXPORT_SYMBOLS
|
||||
#endif
|
||||
|
||||
#if defined(CJSON_HIDE_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) type CJSON_STDCALL
|
||||
#elif defined(CJSON_EXPORT_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
|
||||
#elif defined(CJSON_IMPORT_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
|
||||
#endif
|
||||
#else /* !__WINDOWS__ */
|
||||
#define CJSON_CDECL
|
||||
#define CJSON_STDCALL
|
||||
|
||||
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
|
||||
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
|
||||
#else
|
||||
#define CJSON_PUBLIC(type) type
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* project version */
|
||||
#define CJSON_VERSION_MAJOR 1
|
||||
#define CJSON_VERSION_MINOR 7
|
||||
#define CJSON_VERSION_PATCH 15
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* cJSON Types: */
|
||||
#define cJSON_Invalid (0)
|
||||
#define cJSON_False (1 << 0)
|
||||
#define cJSON_True (1 << 1)
|
||||
#define cJSON_NULL (1 << 2)
|
||||
#define cJSON_Number (1 << 3)
|
||||
#define cJSON_String (1 << 4)
|
||||
#define cJSON_Array (1 << 5)
|
||||
#define cJSON_Object (1 << 6)
|
||||
#define cJSON_Raw (1 << 7) /* raw json */
|
||||
|
||||
#define cJSON_IsReference 256
|
||||
#define cJSON_StringIsConst 512
|
||||
|
||||
/* The cJSON structure: */
|
||||
typedef struct cJSON
|
||||
{
|
||||
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
|
||||
struct cJSON *next;
|
||||
struct cJSON *prev;
|
||||
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
|
||||
struct cJSON *child;
|
||||
|
||||
/* The type of the item, as above. */
|
||||
int type;
|
||||
|
||||
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
|
||||
char *valuestring;
|
||||
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
|
||||
int64_t valueint;
|
||||
/* The item's number, if type==cJSON_Number */
|
||||
double valuedouble;
|
||||
|
||||
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
|
||||
char *string;
|
||||
} cJSON;
|
||||
|
||||
typedef struct cJSON_Hooks
|
||||
{
|
||||
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
|
||||
void *(CJSON_CDECL *malloc_fn)(size_t sz);
|
||||
void (CJSON_CDECL *free_fn)(void *ptr);
|
||||
} cJSON_Hooks;
|
||||
|
||||
typedef int cJSON_bool;
|
||||
|
||||
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
|
||||
* This is to prevent stack overflows. */
|
||||
#ifndef CJSON_NESTING_LIMIT
|
||||
#define CJSON_NESTING_LIMIT 1000
|
||||
#endif
|
||||
|
||||
/* returns the version of cJSON as a string */
|
||||
CJSON_PUBLIC(const char*) cJSON_Version(void);
|
||||
|
||||
/* Supply malloc, realloc and free functions to cJSON */
|
||||
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||
|
||||
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
|
||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
|
||||
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
||||
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||
|
||||
/* Render a cJSON entity to text for transfer/storage. */
|
||||
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
||||
/* Render a cJSON entity to text for transfer/storage without any formatting. */
|
||||
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
|
||||
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
|
||||
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
|
||||
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
|
||||
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
|
||||
/* Delete a cJSON entity and all subentities. */
|
||||
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
|
||||
|
||||
/* Returns the number of items in an array (or object). */
|
||||
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
|
||||
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
|
||||
/* Get item "string" from object. Case insensitive. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
|
||||
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
||||
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
|
||||
|
||||
/* Check item type and return its value */
|
||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
|
||||
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
|
||||
|
||||
/* These functions check the type of an item */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
|
||||
|
||||
/* These calls create a cJSON item of the appropriate type. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
|
||||
/* raw json */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
|
||||
|
||||
/* Create a string where valuestring references a string so
|
||||
* it will not be freed by cJSON_Delete */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
|
||||
/* Create an object/array that only references it's elements so
|
||||
* they will not be freed by cJSON_Delete */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
|
||||
|
||||
/* These utilities create an Array of count items.
|
||||
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
|
||||
|
||||
/* Append item to the specified array/object. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
|
||||
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
|
||||
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
|
||||
* writing to `item->string` */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
|
||||
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
|
||||
|
||||
/* Remove/Detach items from Arrays/Objects. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
|
||||
/* Update array items. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
|
||||
|
||||
/* Duplicate a cJSON item */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
|
||||
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
||||
* need to be released. With recurse!=0, it will duplicate any children connected to the item.
|
||||
* The item->next and ->prev pointers are always zero on return from Duplicate. */
|
||||
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
|
||||
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
|
||||
|
||||
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
|
||||
* The input pointer json cannot point to a read-only address area, such as a string constant,
|
||||
* but should point to a readable and writable address area. */
|
||||
CJSON_PUBLIC(void) cJSON_Minify(char *json);
|
||||
|
||||
/* Helper functions for creating and adding items to an object at the same time.
|
||||
* They return the added item or NULL on failure. */
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
|
||||
|
||||
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
|
||||
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
|
||||
/* helper for the cJSON_SetNumberValue macro */
|
||||
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
|
||||
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
|
||||
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
|
||||
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
|
||||
|
||||
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
|
||||
#define cJSON_SetBoolValue(object, boolValue) ( \
|
||||
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
|
||||
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
|
||||
cJSON_Invalid\
|
||||
)
|
||||
|
||||
/* Macro for iterating over an array or object */
|
||||
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
|
||||
|
||||
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
|
||||
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
|
||||
CJSON_PUBLIC(void) cJSON_free(void *object);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,162 @@
|
||||
/* dscp lookup routines lifted wholesale from openssh */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
* Copyright (c) 2005,2006 Damien Miller. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#define strcasecmp(a,b) _stricmp(a,b)
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
int parse_qos(const char *cp);
|
||||
const char * iptos2str(int iptos);
|
||||
|
||||
/*
|
||||
* Definitions for IP type of service (ip_tos)
|
||||
*/
|
||||
|
||||
#ifdef HAVE_NETINET_IN_SYSTM_H
|
||||
#include <netinet/in_systm.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETINET_IP_H
|
||||
#include <netinet/ip.h>
|
||||
#endif
|
||||
|
||||
#ifndef IPTOS_LOWDELAY
|
||||
# define IPTOS_LOWDELAY 0x10
|
||||
# define IPTOS_THROUGHPUT 0x08
|
||||
# define IPTOS_RELIABILITY 0x04
|
||||
# define IPTOS_LOWCOST 0x02
|
||||
# define IPTOS_MINCOST IPTOS_LOWCOST
|
||||
#endif /* IPTOS_LOWDELAY */
|
||||
|
||||
/*
|
||||
* Definitions for DiffServ Codepoints as per RFC2474
|
||||
*/
|
||||
#ifndef IPTOS_DSCP_AF11
|
||||
# define IPTOS_DSCP_AF11 0x28
|
||||
# define IPTOS_DSCP_AF12 0x30
|
||||
# define IPTOS_DSCP_AF13 0x38
|
||||
# define IPTOS_DSCP_AF21 0x48
|
||||
# define IPTOS_DSCP_AF22 0x50
|
||||
# define IPTOS_DSCP_AF23 0x58
|
||||
# define IPTOS_DSCP_AF31 0x68
|
||||
# define IPTOS_DSCP_AF32 0x70
|
||||
# define IPTOS_DSCP_AF33 0x78
|
||||
# define IPTOS_DSCP_AF41 0x88
|
||||
# define IPTOS_DSCP_AF42 0x90
|
||||
# define IPTOS_DSCP_AF43 0x98
|
||||
# define IPTOS_DSCP_EF 0xb8
|
||||
#endif /* IPTOS_DSCP_AF11 */
|
||||
|
||||
#ifndef IPTOS_DSCP_CS0
|
||||
# define IPTOS_DSCP_CS0 0x00
|
||||
# define IPTOS_DSCP_CS1 0x20
|
||||
# define IPTOS_DSCP_CS2 0x40
|
||||
# define IPTOS_DSCP_CS3 0x60
|
||||
# define IPTOS_DSCP_CS4 0x80
|
||||
# define IPTOS_DSCP_CS5 0xa0
|
||||
# define IPTOS_DSCP_CS6 0xc0
|
||||
# define IPTOS_DSCP_CS7 0xe0
|
||||
#endif /* IPTOS_DSCP_CS0 */
|
||||
#ifndef IPTOS_DSCP_EF
|
||||
# define IPTOS_DSCP_EF 0xb8
|
||||
#endif /* IPTOS_DSCP_EF */
|
||||
#ifndef IPTOS_DSCP_VA
|
||||
# define IPTOS_DSCP_VA 0xb0
|
||||
#endif /* IPTOS_DSCP_VA */
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
int value;
|
||||
} ipqos[] = {
|
||||
{ "af11", IPTOS_DSCP_AF11 },
|
||||
{ "af12", IPTOS_DSCP_AF12 },
|
||||
{ "af13", IPTOS_DSCP_AF13 },
|
||||
{ "af21", IPTOS_DSCP_AF21 },
|
||||
{ "af22", IPTOS_DSCP_AF22 },
|
||||
{ "af23", IPTOS_DSCP_AF23 },
|
||||
{ "af31", IPTOS_DSCP_AF31 },
|
||||
{ "af32", IPTOS_DSCP_AF32 },
|
||||
{ "af33", IPTOS_DSCP_AF33 },
|
||||
{ "af41", IPTOS_DSCP_AF41 },
|
||||
{ "af42", IPTOS_DSCP_AF42 },
|
||||
{ "af43", IPTOS_DSCP_AF43 },
|
||||
{ "cs0", IPTOS_DSCP_CS0 },
|
||||
{ "cs1", IPTOS_DSCP_CS1 },
|
||||
{ "cs2", IPTOS_DSCP_CS2 },
|
||||
{ "cs3", IPTOS_DSCP_CS3 },
|
||||
{ "cs4", IPTOS_DSCP_CS4 },
|
||||
{ "cs5", IPTOS_DSCP_CS5 },
|
||||
{ "cs6", IPTOS_DSCP_CS6 },
|
||||
{ "cs7", IPTOS_DSCP_CS7 },
|
||||
{ "ef", IPTOS_DSCP_EF },
|
||||
{ "va", IPTOS_DSCP_VA },
|
||||
{ "lowdelay", IPTOS_LOWDELAY },
|
||||
{ "throughput", IPTOS_THROUGHPUT },
|
||||
{ "reliability", IPTOS_RELIABILITY },
|
||||
{ NULL, -1 }
|
||||
};
|
||||
|
||||
int
|
||||
parse_qos(const char *cp)
|
||||
{
|
||||
unsigned int i;
|
||||
char *ep = NULL;
|
||||
long val;
|
||||
|
||||
if (cp == NULL)
|
||||
return -1;
|
||||
for (i = 0; ipqos[i].name != NULL; i++) {
|
||||
if (strcasecmp(cp, ipqos[i].name) == 0)
|
||||
return ipqos[i].value;
|
||||
}
|
||||
/* Try parsing as an integer */
|
||||
/* Max DSCP value is 2**6 - 1 */
|
||||
val = strtol(cp, &ep, 0);
|
||||
if (*cp == '\0' || *ep != '\0' || val < 0 || val > 63)
|
||||
return -1;
|
||||
return val << 2;
|
||||
}
|
||||
|
||||
const char *
|
||||
iptos2str(int iptos)
|
||||
{
|
||||
int i;
|
||||
static char iptos_str[sizeof "0xff"];
|
||||
if (iptos < 0 || iptos > 64) iptos = 0;
|
||||
for (i = 0; ipqos[i].name != NULL; i++) {
|
||||
if (ipqos[i].value == iptos)
|
||||
return ipqos[i].name;
|
||||
}
|
||||
snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos);
|
||||
return iptos_str;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#ifndef __FLOW_LABEL_H
|
||||
#define __FLOW_LABEL_H
|
||||
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
|
||||
#include <linux/in6.h>
|
||||
#else
|
||||
#ifndef __ANDROID__
|
||||
/*
|
||||
It is just a stripped copy of the Linux kernel header "linux/in6.h"
|
||||
"Flow label" things are still not defined in "netinet/in*.h" headers,
|
||||
but we cannot use "linux/in6.h" immediately because it currently
|
||||
conflicts with "netinet/in.h" . (in kernel versions < 3.7.0)
|
||||
*/
|
||||
struct in6_flowlabel_req
|
||||
{
|
||||
struct in6_addr flr_dst;
|
||||
__u32 flr_label;
|
||||
__u8 flr_action;
|
||||
__u8 flr_share;
|
||||
__u16 flr_flags;
|
||||
__u16 flr_expires;
|
||||
__u16 flr_linger;
|
||||
__u32 __flr_pad;
|
||||
/* Options in format of IPV6_PKTOPTIONS */
|
||||
};
|
||||
#endif
|
||||
|
||||
#define IPV6_FL_A_GET 0
|
||||
#define IPV6_FL_A_PUT 1
|
||||
#define IPV6_FL_A_RENEW 2
|
||||
|
||||
#define IPV6_FL_F_CREATE 1
|
||||
#define IPV6_FL_F_EXCL 2
|
||||
|
||||
#define IPV6_FL_S_NONE 0
|
||||
#define IPV6_FL_S_EXCL 1
|
||||
#define IPV6_FL_S_PROCESS 2
|
||||
#define IPV6_FL_S_USER 3
|
||||
#define IPV6_FL_S_ANY 255
|
||||
|
||||
#define IPV6_FLOWINFO_FLOWLABEL 0x000fffff
|
||||
#define IPV6_FLOWINFO_PRIORITY 0x0ff00000
|
||||
|
||||
#endif
|
||||
#define IPV6_FLOWLABEL_MGR 32
|
||||
#define IPV6_FLOWINFO_SEND 33
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2020, 2023, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#ifndef __IPERF_H
|
||||
#define __IPERF_H
|
||||
|
||||
#include "iperf_config.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_TCP_H
|
||||
#include <linux/tcp.h>
|
||||
#else
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
#include <net/if.h> // for IFNAMSIZ
|
||||
|
||||
#if defined(HAVE_CPUSET_SETAFFINITY)
|
||||
#include <sys/param.h>
|
||||
#include <sys/cpuset.h>
|
||||
#endif /* HAVE_CPUSET_SETAFFINITY */
|
||||
|
||||
#include "timer.h"
|
||||
#include "queue.h"
|
||||
#include "cjson.h"
|
||||
#include "iperf_time.h"
|
||||
#include "portable_endian.h"
|
||||
|
||||
#if defined(HAVE_SSL)
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/evp.h>
|
||||
#endif // HAVE_SSL
|
||||
|
||||
#include "iperf_pthread.h"
|
||||
|
||||
/*
|
||||
* Atomic types highly desired, but if not, we approximate what we need
|
||||
* with normal integers and warn.
|
||||
*/
|
||||
#ifdef HAVE_STDATOMIC_H
|
||||
#include <stdatomic.h>
|
||||
#else
|
||||
#warning "No <stdatomic.h> available."
|
||||
typedef uint64_t atomic_uint_fast64_t;
|
||||
#endif // HAVE_STDATOMIC_H
|
||||
|
||||
#if !defined(__IPERF_API_H)
|
||||
typedef uint_fast64_t iperf_size_t;
|
||||
typedef atomic_uint_fast64_t atomic_iperf_size_t;
|
||||
#endif // __IPERF_API_H
|
||||
|
||||
#if (defined(__vxworks)) || (defined(__VXWORKS__))
|
||||
typedef unsigned int uint
|
||||
#endif // __vxworks or __VXWORKS__
|
||||
|
||||
struct iperf_sctp_info
|
||||
{
|
||||
long rtt;
|
||||
long pmtu;
|
||||
uint32_t wnd;
|
||||
uint32_t cwnd;
|
||||
};
|
||||
|
||||
struct iperf_interval_results
|
||||
{
|
||||
atomic_iperf_size_t bytes_transferred; /* bytes transferred in this interval */
|
||||
struct iperf_time interval_start_time;
|
||||
struct iperf_time interval_end_time;
|
||||
float interval_duration;
|
||||
|
||||
/* for UDP */
|
||||
int64_t interval_packet_count;
|
||||
int64_t interval_outoforder_packets;
|
||||
int64_t interval_cnt_error;
|
||||
int64_t packet_count;
|
||||
double jitter;
|
||||
int64_t outoforder_packets;
|
||||
int64_t cnt_error;
|
||||
|
||||
int omitted;
|
||||
#if (defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)) && \
|
||||
defined(TCP_INFO)
|
||||
struct tcp_info tcpInfo; /* getsockopt(TCP_INFO) for Linux, {Free,Net,Open}BSD */
|
||||
#else
|
||||
/* Just placeholders, never accessed. */
|
||||
char *tcpInfo;
|
||||
#endif
|
||||
#if defined(HAVE_SCTP_H)
|
||||
struct iperf_sctp_info sctp_info;
|
||||
#endif /* HAVE_SCTP_H */
|
||||
long interval_retrans;
|
||||
long snd_cwnd;
|
||||
long snd_wnd;
|
||||
TAILQ_ENTRY(iperf_interval_results) irlistentries;
|
||||
void *custom_data;
|
||||
long rtt;
|
||||
long rttvar;
|
||||
long pmtu;
|
||||
};
|
||||
|
||||
struct iperf_stream_result
|
||||
{
|
||||
atomic_iperf_size_t bytes_received;
|
||||
atomic_iperf_size_t bytes_sent;
|
||||
atomic_iperf_size_t bytes_received_this_interval;
|
||||
atomic_iperf_size_t bytes_sent_this_interval;
|
||||
atomic_iperf_size_t bytes_sent_omit;
|
||||
long stream_prev_total_retrans;
|
||||
long stream_retrans;
|
||||
long stream_max_rtt;
|
||||
long stream_min_rtt;
|
||||
long stream_sum_rtt;
|
||||
int stream_count_rtt;
|
||||
long stream_max_snd_cwnd;
|
||||
long stream_max_snd_wnd;
|
||||
struct iperf_time start_time;
|
||||
struct iperf_time end_time;
|
||||
struct iperf_time start_time_fixed;
|
||||
double sender_time;
|
||||
double receiver_time;
|
||||
TAILQ_HEAD(irlisthead, iperf_interval_results) interval_results;
|
||||
void *data;
|
||||
};
|
||||
|
||||
#define COOKIE_SIZE 37 /* size of an ascii uuid */
|
||||
struct iperf_settings
|
||||
{
|
||||
int domain; /* AF_INET or AF_INET6 */
|
||||
int socket_bufsize; /* window size for TCP */
|
||||
int blksize; /* size of read/writes (-l) */
|
||||
iperf_size_t rate; /* target data rate for application pacing*/
|
||||
iperf_size_t bitrate_limit; /* server's maximum allowed total data rate for all streams*/
|
||||
double bitrate_limit_interval; /* interval for averaging total data rate */
|
||||
int bitrate_limit_stats_per_interval; /* calculated number of stats periods for averaging total data rate */
|
||||
uint64_t fqrate; /* target data rate for FQ pacing*/
|
||||
int pacing_timer; /* pacing timer in microseconds */
|
||||
int burst; /* packets per burst */
|
||||
int mss; /* for TCP MSS */
|
||||
int ttl; /* IP TTL option */
|
||||
int tos; /* type of service bit */
|
||||
int flowlabel; /* IPv6 flow label */
|
||||
iperf_size_t bytes; /* number of bytes to send */
|
||||
iperf_size_t blocks; /* number of blocks (packets) to send */
|
||||
char unit_format; /* -f */
|
||||
int num_ostreams; /* SCTP initmsg settings */
|
||||
int dont_fragment; /* Whether to set IP flag Do-Not_Fragment */
|
||||
#if defined(HAVE_SSL)
|
||||
char *authtoken; /* Authentication token */
|
||||
char *client_username;
|
||||
char *client_password;
|
||||
EVP_PKEY *client_rsa_pubkey;
|
||||
#endif // HAVE_SSL
|
||||
int skip_rx_copy; /* Whether to ignore received messages data, using MSG_TRUNC option */
|
||||
int connect_timeout; /* socket connection timeout, in ms */
|
||||
int idle_timeout; /* server idle time timeout */
|
||||
unsigned int snd_timeout; /* Timeout for sending tcp messages in active mode, in us */
|
||||
struct iperf_time rcv_timeout; /* Timeout for receiving messages in active mode, in us */
|
||||
int cntl_ka; /* Use Control TCP connection Keepalive */
|
||||
int cntl_ka_keepidle; /* Control TCP connection Keepalive idle time (TCP_KEEPIDLE) */
|
||||
int cntl_ka_interval; /* Control TCP connection Keepalive interval between retries (TCP_KEEPINTV) */
|
||||
int cntl_ka_count; /* Control TCP connection Keepalive number of retries (TCP_KEEPCNT) */
|
||||
};
|
||||
|
||||
struct iperf_test;
|
||||
|
||||
struct iperf_stream
|
||||
{
|
||||
struct iperf_test* test;
|
||||
|
||||
pthread_t thr;
|
||||
int thread_created;
|
||||
int done;
|
||||
|
||||
/* configurable members */
|
||||
int local_port;
|
||||
int remote_port;
|
||||
int socket;
|
||||
int id;
|
||||
int sender;
|
||||
/* XXX: is settings just a pointer to the same struct in iperf_test? if not,
|
||||
should it be? */
|
||||
struct iperf_settings *settings; /* pointer to structure settings */
|
||||
|
||||
/* non configurable members */
|
||||
struct iperf_stream_result *result; /* structure pointer to result */
|
||||
Timer *send_timer;
|
||||
int green_light;
|
||||
int buffer_fd; /* data to send, file descriptor */
|
||||
char *buffer; /* data to send, mmapped */
|
||||
int pending_size; /* pending data to send */
|
||||
int diskfile_fd; /* file to send, file descriptor */
|
||||
int diskfile_left; /* remaining file data on disk */
|
||||
|
||||
/*
|
||||
* for udp measurements - This can be a structure outside stream, and
|
||||
* stream can have a pointer to this
|
||||
*/
|
||||
int64_t packet_count;
|
||||
int64_t peer_packet_count;
|
||||
int64_t peer_omitted_packet_count;
|
||||
int64_t omitted_packet_count;
|
||||
double jitter;
|
||||
double prev_transit;
|
||||
int64_t outoforder_packets;
|
||||
int64_t omitted_outoforder_packets;
|
||||
int64_t cnt_error;
|
||||
int64_t omitted_cnt_error;
|
||||
uint64_t target;
|
||||
|
||||
struct sockaddr_storage local_addr;
|
||||
struct sockaddr_storage remote_addr;
|
||||
|
||||
int (*rcv) (struct iperf_stream * stream);
|
||||
int (*snd) (struct iperf_stream * stream);
|
||||
|
||||
/* chained send/receive routines for -F mode */
|
||||
int (*rcv2) (struct iperf_stream * stream);
|
||||
int (*snd2) (struct iperf_stream * stream);
|
||||
|
||||
// struct iperf_stream *next;
|
||||
SLIST_ENTRY(iperf_stream) streams;
|
||||
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct protocol {
|
||||
int id;
|
||||
char *name;
|
||||
int (*accept)(struct iperf_test *);
|
||||
int (*listen)(struct iperf_test *);
|
||||
int (*connect)(struct iperf_test *);
|
||||
int (*send)(struct iperf_stream *);
|
||||
int (*recv)(struct iperf_stream *);
|
||||
int (*init)(struct iperf_test *);
|
||||
SLIST_ENTRY(protocol) protocols;
|
||||
};
|
||||
|
||||
struct iperf_textline {
|
||||
char *line;
|
||||
TAILQ_ENTRY(iperf_textline) textlineentries;
|
||||
};
|
||||
|
||||
struct xbind_entry {
|
||||
char *name;
|
||||
struct addrinfo *ai;
|
||||
TAILQ_ENTRY(xbind_entry) link;
|
||||
};
|
||||
|
||||
enum iperf_mode {
|
||||
SENDER = 1,
|
||||
RECEIVER = 0,
|
||||
BIDIRECTIONAL = -1
|
||||
};
|
||||
|
||||
enum debug_level {
|
||||
DEBUG_LEVEL_ERROR = 1,
|
||||
DEBUG_LEVEL_WARN = 2,
|
||||
DEBUG_LEVEL_INFO = 3,
|
||||
DEBUG_LEVEL_DEBUG = 4,
|
||||
DEBUG_LEVEL_MAX = 4
|
||||
};
|
||||
|
||||
|
||||
struct iperf_test
|
||||
{
|
||||
pthread_mutex_t print_mutex;
|
||||
|
||||
char role; /* 'c' lient or 's' erver */
|
||||
enum iperf_mode mode;
|
||||
int sender_has_retransmits;
|
||||
int other_side_has_retransmits; /* used if mode == BIDIRECTIONAL */
|
||||
struct protocol *protocol;
|
||||
signed char state;
|
||||
char *server_hostname; /* -c option */
|
||||
char *tmp_template;
|
||||
char *bind_address; /* first -B option */
|
||||
char *bind_dev; /* bind to network device */
|
||||
TAILQ_HEAD(xbind_addrhead, xbind_entry) xbind_addrs; /* all -X opts */
|
||||
int bind_port; /* --cport option */
|
||||
int server_port;
|
||||
int omit; /* duration of omit period (-O flag) */
|
||||
int duration; /* total duration of test (-t flag) */
|
||||
char *diskfile_name; /* -F option */
|
||||
int affinity, server_affinity; /* -A option */
|
||||
#if defined(HAVE_CPUSET_SETAFFINITY)
|
||||
cpuset_t cpumask;
|
||||
#endif /* HAVE_CPUSET_SETAFFINITY */
|
||||
char *title; /* -T option */
|
||||
char *extra_data; /* --extra-data */
|
||||
char *congestion; /* -C option */
|
||||
char *congestion_used; /* what was actually used */
|
||||
char *remote_congestion_used; /* what the other side used */
|
||||
char *pidfile; /* -P option */
|
||||
|
||||
char *logfile; /* --logfile option */
|
||||
FILE *outfile;
|
||||
|
||||
int ctrl_sck;
|
||||
int mapped_v4;
|
||||
int listener;
|
||||
int prot_listener;
|
||||
|
||||
int ctrl_sck_mss; /* MSS for the control channel */
|
||||
|
||||
#if defined(HAVE_SSL)
|
||||
char *server_authorized_users;
|
||||
EVP_PKEY *server_rsa_private_key;
|
||||
int server_skew_threshold;
|
||||
int use_pkcs1_padding;
|
||||
#endif // HAVE_SSL
|
||||
|
||||
/* boolean variables for Options */
|
||||
int daemon; /* -D option */
|
||||
int one_off; /* -1 option */
|
||||
int no_delay; /* -N option */
|
||||
int reverse; /* -R option */
|
||||
int bidirectional; /* --bidirectional */
|
||||
int verbose; /* -V option - verbose mode */
|
||||
int json_output; /* -J option - JSON output */
|
||||
int json_stream; /* --json-stream */
|
||||
void (*json_callback) (struct iperf_test *, char *); /* allow user apps to receive the
|
||||
JSON strings,instead of writing them to the output file */
|
||||
int zerocopy; /* -Z option - use sendfile */
|
||||
int debug; /* -d option - enable debug */
|
||||
enum debug_level debug_level; /* -d option option - level of debug messages to show */
|
||||
int get_server_output; /* --get-server-output */
|
||||
int udp_counters_64bit; /* --use-64-bit-udp-counters */
|
||||
int forceflush; /* --forceflush - flushing output at every interval */
|
||||
int multisend;
|
||||
int repeating_payload; /* --repeating-payload */
|
||||
int timestamps; /* --timestamps */
|
||||
char *timestamp_format;
|
||||
int mptcp; /* -m, --mptcp */
|
||||
|
||||
char *json_output_string; /* rendered JSON output if json_output is set */
|
||||
/* Select related parameters */
|
||||
int max_fd;
|
||||
fd_set read_set; /* set of read sockets */
|
||||
fd_set write_set; /* set of write sockets */
|
||||
|
||||
/* Interval related members */
|
||||
int omitting;
|
||||
double stats_interval;
|
||||
double reporter_interval;
|
||||
void (*stats_callback) (struct iperf_test *);
|
||||
void (*reporter_callback) (struct iperf_test *);
|
||||
Timer *omit_timer;
|
||||
Timer *timer;
|
||||
int done;
|
||||
Timer *stats_timer;
|
||||
Timer *reporter_timer;
|
||||
|
||||
double cpu_util[3]; /* cpu utilization of the test - total, user, system */
|
||||
double remote_cpu_util[3]; /* cpu utilization for the remote host/client - total, user, system */
|
||||
|
||||
int num_streams; /* total streams in the test (-P) */
|
||||
|
||||
atomic_iperf_size_t bytes_sent;
|
||||
atomic_iperf_size_t blocks_sent;
|
||||
|
||||
atomic_iperf_size_t bytes_received;
|
||||
atomic_iperf_size_t blocks_received;
|
||||
|
||||
iperf_size_t bitrate_limit_stats_count; /* Number of stats periods accumulated for server's total bitrate average */
|
||||
iperf_size_t *bitrate_limit_intervals_traffic_bytes; /* Pointer to a cyclic array that includes the last interval's bytes transferred */
|
||||
iperf_size_t bitrate_limit_last_interval_index; /* Index of the last interval traffic inserted into the cyclic array */
|
||||
int bitrate_limit_exceeded; /* Set by callback routine when average data rate exceeded the server's bitrate limit */
|
||||
|
||||
int server_last_run_rc; /* Save last server run rc for next test */
|
||||
uint server_forced_idle_restarts_count; /* count number of forced server restarts to make sure it is not stack */
|
||||
uint server_forced_no_msg_restarts_count; /* count number of forced server restarts to make sure it is not stack */
|
||||
uint server_test_number; /* count number of tests performed by a server */
|
||||
|
||||
char cookie[COOKIE_SIZE];
|
||||
// struct iperf_stream *streams; /* pointer to list of struct stream */
|
||||
SLIST_HEAD(slisthead, iperf_stream) streams;
|
||||
struct iperf_settings *settings;
|
||||
|
||||
SLIST_HEAD(plisthead, protocol) protocols;
|
||||
|
||||
/* callback functions */
|
||||
void (*on_new_stream)(struct iperf_stream *);
|
||||
void (*on_test_start)(struct iperf_test *);
|
||||
void (*on_connect)(struct iperf_test *);
|
||||
void (*on_test_finish)(struct iperf_test *);
|
||||
|
||||
/* cJSON handles for use when in -J mode */\
|
||||
cJSON *json_top;
|
||||
cJSON *json_start;
|
||||
cJSON *json_connected;
|
||||
cJSON *json_intervals;
|
||||
cJSON *json_end;
|
||||
|
||||
/* Server output (use on client side only) */
|
||||
char *server_output_text;
|
||||
cJSON *json_server_output;
|
||||
|
||||
/* Server output (use on server side only) */
|
||||
TAILQ_HEAD(iperf_textlisthead, iperf_textline) server_output_list;
|
||||
|
||||
};
|
||||
|
||||
/* default settings */
|
||||
#define PORT 5201 /* default port to listen on (don't use the same port as iperf2) */
|
||||
#define uS_TO_NS 1000
|
||||
#define mS_TO_US 1000
|
||||
#define SEC_TO_mS 1000
|
||||
#define SEC_TO_US 1000000LL
|
||||
#define UDP_RATE (1024 * 1024) /* 1 Mbps */
|
||||
#define OMIT 0 /* seconds */
|
||||
#define DURATION 10 /* seconds */
|
||||
|
||||
#define SEC_TO_NS 1000000000LL /* too big for enum/const on some platforms */
|
||||
#define MAX_RESULT_STRING 4096
|
||||
|
||||
#define UDP_BUFFER_EXTRA 1024
|
||||
|
||||
#define MAX_PARAMS_JSON_STRING 8 * 1024
|
||||
|
||||
/* constants for command line arg sanity checks */
|
||||
#define MB (1024 * 1024)
|
||||
#define MAX_TCP_BUFFER (512 * MB)
|
||||
#define MAX_BLOCKSIZE MB
|
||||
/* Minimum size UDP send is the size of two 32-bit ints followed by a 64-bit int */
|
||||
#define MIN_UDP_BLOCKSIZE (4 + 4 + 8)
|
||||
/* Maximum size UDP send is (64K - 1) - IP and UDP header sizes */
|
||||
#define MAX_UDP_BLOCKSIZE (65535 - 8 - 20)
|
||||
#define MIN_INTERVAL 0.1
|
||||
#define MAX_INTERVAL 60.0
|
||||
#define MAX_TIME 86400
|
||||
#define MAX_OMIT_TIME 600
|
||||
#define MAX_BURST 1000
|
||||
#define MAX_MSS (9 * 1024)
|
||||
#define MAX_STREAMS 128
|
||||
|
||||
#define TIMESTAMP_FORMAT "%c "
|
||||
|
||||
extern int gerror; /* error value from getaddrinfo(3), for use in internal error handling */
|
||||
|
||||
/* UDP "connect" message and reply (textual value for Wireshark, etc. readability - legacy was numeric) */
|
||||
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
#define UDP_CONNECT_MSG 0x39383736
|
||||
#define UDP_CONNECT_REPLY 0x36373839
|
||||
#define LEGACY_UDP_CONNECT_REPLY 0xb168de3a
|
||||
#else
|
||||
#define UDP_CONNECT_MSG 0x36373839 // "6789" - legacy value was 123456789
|
||||
#define UDP_CONNECT_REPLY 0x39383736 // "9876" - legacy value was 987654321
|
||||
#define LEGACY_UDP_CONNECT_REPLY 987654321 // Old servers may still reply with the legacy value
|
||||
#endif
|
||||
|
||||
/* In Reverse mode, maximum number of packets to wait for "accept" response - to handle out of order packets */
|
||||
#define MAX_REVERSE_OUT_OF_ORDER_PACKETS 2
|
||||
|
||||
#endif /* !__IPERF_H */
|
||||
@@ -0,0 +1,529 @@
|
||||
.TH IPERF3 1 "May 2025" ESnet "User Manuals"
|
||||
.SH NAME
|
||||
iperf3 \- perform network throughput tests
|
||||
.SH SYNOPSIS
|
||||
.B iperf3 -s [
|
||||
.I options
|
||||
.B ]
|
||||
.br
|
||||
.B iperf3 -c
|
||||
.I server
|
||||
.B [
|
||||
.I options
|
||||
.B ]
|
||||
|
||||
.SH DESCRIPTION
|
||||
iperf3 is a tool for performing network throughput measurements.
|
||||
It can test TCP, UDP, or SCTP throughput.
|
||||
To perform an iperf3 test the user must establish both a server and a
|
||||
client.
|
||||
.PP
|
||||
The iperf3 executable contains both client and server functionality.
|
||||
An iperf3 server can be started using either of the -s or
|
||||
--server command-line parameters, for example:
|
||||
.IP
|
||||
\fCiperf3 -s\fR
|
||||
.IP
|
||||
\fCiperf3 --server \fR
|
||||
.PP
|
||||
Note that many iperf3 parameters have both short (-s) and long
|
||||
(--server) forms.
|
||||
In this section we will generally use the short form of command-line
|
||||
flags, unless only the long form of a flag is available.
|
||||
.PP
|
||||
By default, the iperf3 server listens on TCP port 5201 for connections
|
||||
from an iperf3 client.
|
||||
A custom port can be specified by using the -p flag, for
|
||||
example:
|
||||
.IP
|
||||
\fCiperf3 -s -p 5002\fR
|
||||
.PP
|
||||
After the server is started, it will listen for connections from
|
||||
iperf3 clients (in other words, the iperf3 program run in client
|
||||
mode).
|
||||
The client mode can be started using the -c command-line option,
|
||||
which also requires a host to which iperf3 should connect.
|
||||
The host can by specified by hostname, IPv4 literal, or IPv6 literal:
|
||||
.IP
|
||||
\fCiperf3 -c iperf3.example.com\fR
|
||||
.IP
|
||||
\fCiperf3 -c 192.0.2.1\fR
|
||||
.IP
|
||||
\fCiperf3 -c 2001:db8::1\fR
|
||||
.PP
|
||||
If the iperf3 server is running on a non-default TCP port, that port
|
||||
number needs to be specified on the client as well:
|
||||
.IP
|
||||
\fCiperf3 -c iperf3.example.com -p 5002\fR
|
||||
.PP
|
||||
The initial TCP connection is used to exchange test parameters,
|
||||
control the start and end of the test, and to exchange test results.
|
||||
This is sometimes referred to as the "control connection".
|
||||
The actual test data is sent over a separate TCP connection, as a
|
||||
separate flow of UDP packets, or as an independent SCTP connection,
|
||||
depending on what protocol was specified by the client.
|
||||
.PP
|
||||
Normally, the test data is sent from the client to the server, and
|
||||
measures the upload speed of the client.
|
||||
Measuring the download speed from the server can be done by specifying
|
||||
the -R flag on the client.
|
||||
This causes data to be sent from the server to the client.
|
||||
.IP
|
||||
\fCiperf3 -c iperf3.example.com -p 5202 -R
|
||||
.PP
|
||||
Results are displayed on both the client and server.
|
||||
There will be at least one line of output per measurement interval (by
|
||||
default a measurement interval lasts for one second, but this can be
|
||||
changed by the -i option).
|
||||
Each line of output includes (at least) the time since the start of
|
||||
the test, amount of data transferred during the interval, and the
|
||||
average bitrate over that interval.
|
||||
Note that the values for each measurement interval are taken from the
|
||||
point of view of the endpoint process emitting that output (in other
|
||||
words, the output on the client shows the measurement interval data for
|
||||
the client.
|
||||
.PP
|
||||
At the end of the test is a set of statistics that shows (at
|
||||
least as much as possible) a summary of the test as seen by both the
|
||||
sender and the receiver, with lines tagged accordingly.
|
||||
Recall that by default the client is the sender and the server is the
|
||||
receiver, although as indicated above, use of the \fC-R\fR flag will
|
||||
reverse these roles.
|
||||
.PP
|
||||
The client can be made to retrieve the server-side output for a given
|
||||
test by specifying the --get-server-output flag.
|
||||
.PP
|
||||
Either the client or the server can produce its output in a JSON
|
||||
structure, useful for integration with other programs, by passing it
|
||||
the -J flag.
|
||||
Normally the contents of the JSON structure are only completely known
|
||||
after the test has finished, no JSON output will be emitted until the
|
||||
end of the test.
|
||||
By enabling line-delimited JSON multiple objects will be emitted to
|
||||
provide a real-time parsable JSON output.
|
||||
.PP
|
||||
iperf3 has a (overly) large set of command-line options that can be
|
||||
used to set the parameters of a test.
|
||||
They are given in the "GENERAL OPTIONS" section of the manual page
|
||||
below, as well as summarized in iperf3's help output, which can be
|
||||
viewed by running iperf3 with the -h flag.
|
||||
.SH "GENERAL OPTIONS"
|
||||
.TP
|
||||
.BR -p ", " --port " \fIn\fR"
|
||||
set server port to listen on/connect to to \fIn\fR (default 5201)
|
||||
.TP
|
||||
.BR -f ", " --format " "
|
||||
[kmgtKMGT] format to report: Kbits/Mbits/Gbits/Tbits
|
||||
.TP
|
||||
.BR -i ", " --interval " \fIn\fR"
|
||||
pause \fIn\fR seconds between periodic throughput reports;
|
||||
default is 1, use 0 to disable
|
||||
.TP
|
||||
.BR -I ", " --pidfile " \fIfile\fR"
|
||||
write a file with the process ID, most useful when running as a daemon.
|
||||
.TP
|
||||
.BR -F ", " --file " \fIname\fR"
|
||||
Use a file as the source (on the sender) or sink (on the receiver) of
|
||||
data, rather than just generating random data or throwing it away.
|
||||
This feature is used for finding whether or not the storage subsystem
|
||||
is the bottleneck for file transfers.
|
||||
It does not turn iperf3 into a file transfer tool.
|
||||
The length, attributes, and in some cases contents of the received
|
||||
file may not match those of the original file.
|
||||
.TP
|
||||
.BR -A ", " --affinity " \fIn/n,m\fR"
|
||||
Set the CPU affinity, if possible (Linux, FreeBSD, and Windows only).
|
||||
On both the client and server you can set the local affinity by using
|
||||
the \fIn\fR form of this argument (where \fIn\fR is a CPU number).
|
||||
In addition, on the client side you can override the server's
|
||||
affinity for just that one test, using the \fIn,m\fR form of
|
||||
argument.
|
||||
Note that when using this feature, a process will only be bound
|
||||
to a single CPU (as opposed to a set containing potentially multiple
|
||||
CPUs).
|
||||
.TP
|
||||
.BR -B ", " --bind " \fIhost\fR[\fB%\fIdev\fR]"
|
||||
bind to the specific interface associated with address \fIhost\fR.
|
||||
If an optional interface is specified, it is treated as a shortcut
|
||||
for \fB--bind-dev \fIdev\fR.
|
||||
Note that a percent sign and interface device name are required for IPv6 link-local address literals.
|
||||
.TP
|
||||
.BR --bind-dev " \fIdev\fR"
|
||||
bind to the specified network interface.
|
||||
This option uses SO_BINDTODEVICE, and may require root permissions.
|
||||
(Available on Linux and possibly other systems.)
|
||||
.TP
|
||||
.BR -V ", " --verbose " "
|
||||
give more detailed output
|
||||
.TP
|
||||
.BR -J ", " --json " "
|
||||
output in JSON format
|
||||
.TP
|
||||
.BR --json-stream " "
|
||||
output in line-delimited JSON format
|
||||
.TP
|
||||
.BR --logfile " \fIfile\fR"
|
||||
send output to a log file.
|
||||
.TP
|
||||
.BR --forceflush " "
|
||||
force flushing output at every interval.
|
||||
Used to avoid buffering when sending output to pipe.
|
||||
.TP
|
||||
.BR --timestamps "[\fB=\fIformat\fR]"
|
||||
prepend a timestamp at the start of each output line.
|
||||
By default, timestamps have the format emitted by
|
||||
.BR ctime ( 1 ).
|
||||
Optionally, \fC=\fR followed by
|
||||
a format specification can be passed to customize the
|
||||
timestamps, see
|
||||
.BR strftime ( 3 ).
|
||||
If this optional format is given, the \fC=\fR must immediately
|
||||
follow the \fB--timestamps\fR option with no whitespace intervening.
|
||||
.TP
|
||||
.BR --rcv-timeout " \fI#\fR"
|
||||
set idle timeout for receiving data during active tests. The receiver
|
||||
will halt a test if no data is received from the sender for this
|
||||
number of ms (default to 120000 ms, or 2 minutes).
|
||||
.TP
|
||||
.BR --snd-timeout " \fI#\fR"
|
||||
set timeout for unacknowledged TCP data (on both test and control
|
||||
connections) This option can be used to force a faster test timeout
|
||||
in case of a network partition during a test. The required
|
||||
parameter is specified in ms, and defaults to the system settings.
|
||||
This functionality depends on the TCP_USER_TIMEOUT socket option, and
|
||||
will not work on systems that do not support it.
|
||||
.TP
|
||||
.BR --use-pkcs1-padding
|
||||
This option is only meaningful when using iperf3's authentication
|
||||
features. Versions of iperf3 prior to 3.17 used PCKS1 padding in the
|
||||
RSA-encrypted credentials, which was vulnerable to a side-channel
|
||||
attack that could reveal a server's private key. Beginning with
|
||||
iperf-3.17, OAEP padding is used, however this is a breaking change
|
||||
that is not compatible with older iperf3 versions. Use this option to
|
||||
preserve the less secure, but more compatible, behavior.
|
||||
.TP
|
||||
.BR -m ", " --mptcp " "
|
||||
use mptcp variant for the current protocol. This only applies to
|
||||
TCP and enables MPTCP usage.
|
||||
.TP
|
||||
.BR -d ", " --debug " "
|
||||
emit debugging output.
|
||||
Primarily (perhaps exclusively) of use to developers.
|
||||
.TP
|
||||
.BR -v ", " --version " "
|
||||
show version information and quit
|
||||
.TP
|
||||
.BR -h ", " --help " "
|
||||
show a help synopsis
|
||||
|
||||
.SH "SERVER SPECIFIC OPTIONS"
|
||||
.TP
|
||||
.BR -s ", " --server " "
|
||||
run in server mode
|
||||
.TP
|
||||
.BR -D ", " --daemon " "
|
||||
run the server in background as a daemon
|
||||
.TP
|
||||
.BR -1 ", " --one-off
|
||||
handle one client connection, then exit. If an idle time is set, the
|
||||
server will exit after that amount of time with no connection.
|
||||
.TP
|
||||
.BR --idle-timeout " \fIn\fR"
|
||||
restart the server after \fIn\fR seconds in case it gets stuck. In
|
||||
one-off mode, this is the number of seconds the server will wait
|
||||
before exiting.
|
||||
.TP
|
||||
.BR --server-bitrate-limit " \fIn\fR[KMGT]"
|
||||
set a limit on the server side, which will cause a test to abort if
|
||||
the client specifies a test of more than \fIn\fR bits per second, or
|
||||
if the average data sent or received by the client (including all data
|
||||
streams) is greater than \fIn\fR bits per second. The default limit
|
||||
is zero, which implies no limit. The interval over which to average
|
||||
the data rate is 5 seconds by default, but can be specified by adding
|
||||
a '/' and a number to the bitrate specifier.
|
||||
.TP
|
||||
.BR --rsa-private-key-path " \fIfile\fR"
|
||||
path to the RSA private key (not password-protected) used to decrypt
|
||||
authentication credentials from the client (if built with OpenSSL
|
||||
support).
|
||||
.TP
|
||||
.BR --authorized-users-path " \fIfile\fR"
|
||||
path to the configuration file containing authorized users credentials to run
|
||||
iperf tests (if built with OpenSSL support).
|
||||
The file is a comma separated list of usernames and password hashes;
|
||||
more information on the structure of the file can be found in the
|
||||
EXAMPLES section.
|
||||
.TP
|
||||
.BR --time-skew-threshold second " \fIseconds\fR"
|
||||
time skew threshold (in seconds) between the server and client
|
||||
during the authentication process.
|
||||
.SH "CLIENT SPECIFIC OPTIONS"
|
||||
.TP
|
||||
.BR -c ", " --client " \fIhost\fR[\fB%\fIdev\fR]"
|
||||
run in client mode, connecting to the specified server.
|
||||
By default, a test consists of sending data from the client to the
|
||||
server, unless the \-R flag is specified.
|
||||
If an optional interface is specified, it is treated as a shortcut
|
||||
for \fB--bind-dev \fIdev\fR.
|
||||
Note that a percent sign and interface device name are required for IPv6 link-local address literals.
|
||||
.TP
|
||||
.BR --sctp
|
||||
use SCTP rather than TCP (FreeBSD and Linux)
|
||||
.TP
|
||||
.BR -u ", " --udp
|
||||
use UDP rather than TCP
|
||||
.TP
|
||||
.BR --connect-timeout " \fIn\fR"
|
||||
set timeout for establishing the initial control connection to the
|
||||
server, in milliseconds.
|
||||
The default behavior is the operating system's timeout for TCP
|
||||
connection establishment.
|
||||
Providing a shorter value may speed up detection of a down iperf3
|
||||
server.
|
||||
.TP
|
||||
.BR -b ", " --bitrate " \fIn\fR[KMGT]"
|
||||
set target bitrate to \fIn\fR bits/sec (default 1 Mbit/sec for UDP,
|
||||
unlimited for TCP/SCTP).
|
||||
If there are multiple streams (\-P flag), the throughput limit is applied
|
||||
separately to each stream.
|
||||
You can also add a '/' and a number to the bitrate specifier.
|
||||
This is called "burst mode".
|
||||
It will send the given number of packets without pausing, even if that
|
||||
temporarily exceeds the specified throughput limit.
|
||||
Setting the target bitrate to 0 will disable bitrate limits
|
||||
(particularly useful for UDP tests).
|
||||
This throughput limit is implemented internally inside iperf3, and is
|
||||
available on all platforms.
|
||||
Compare with the \--fq-rate flag.
|
||||
This option replaces the \--bandwidth flag, which is now deprecated
|
||||
but (at least for now) still accepted.
|
||||
.TP
|
||||
.BR --pacing-timer " \fIn\fR[KMGT]"
|
||||
set pacing timer interval in microseconds (default 1000 microseconds,
|
||||
or 1 ms).
|
||||
This controls iperf3's internal pacing timer for the \-b/\--bitrate
|
||||
option.
|
||||
The timer fires at the interval set by this parameter.
|
||||
Smaller values of the pacing timer parameter smooth out the traffic
|
||||
emitted by iperf3, but potentially at the cost of performance due to
|
||||
more frequent timer processing.
|
||||
.TP
|
||||
.BR --fq-rate " \fIn\fR[KMGT]"
|
||||
Set a rate to be used with fair-queueing based socket-level pacing,
|
||||
in bits per second.
|
||||
This pacing (if specified) will be in addition to any pacing due to
|
||||
iperf3's internal throughput pacing (\-b/\--bitrate flag), and both can be
|
||||
specified for the same test.
|
||||
Only available on platforms supporting the
|
||||
\fCSO_MAX_PACING_RATE\fR socket option (currently only Linux).
|
||||
The default is no fair-queueing based pacing.
|
||||
.TP
|
||||
.BR --no-fq-socket-pacing
|
||||
This option is deprecated and will be removed.
|
||||
It is equivalent to specifying --fq-rate=0.
|
||||
.TP
|
||||
.BR -t ", " --time " \fIn\fR"
|
||||
time in seconds to transmit for (default 10 secs)
|
||||
.TP
|
||||
.BR -n ", " --bytes " \fIn\fR[KMGT]"
|
||||
number of bytes to transmit (instead of \-t)
|
||||
.TP
|
||||
.BR -k ", " --blockcount " \fIn\fR[KMGT]"
|
||||
number of blocks (packets) to transmit (instead of \-t or \-n)
|
||||
.TP
|
||||
.BR -l ", " --length " \fIn\fR[KMGT]"
|
||||
length of buffer to read or write. For TCP tests, the default value
|
||||
is 128KB.
|
||||
In the case of UDP, iperf3 tries to dynamically determine a reasonable
|
||||
sending size based on the path MTU; if that cannot be determined it
|
||||
uses 1460 bytes as a sending size.
|
||||
For SCTP tests, the default size is 64KB.
|
||||
.TP
|
||||
.BR --cport " \fIport\fR"
|
||||
bind data streams to a specific client port (for TCP and UDP only,
|
||||
default is to use an ephemeral port)
|
||||
.TP
|
||||
.BR -P ", " --parallel " \fIn\fR"
|
||||
number of parallel client streams to run. iperf3 will spawn off a
|
||||
separate thread for each test stream. Using multiple streams may
|
||||
result in higher throughput than a single stream.
|
||||
.TP
|
||||
.BR -R ", " --reverse
|
||||
reverse the direction of a test, so that the server sends data to the
|
||||
client
|
||||
.TP
|
||||
.BR --bidir
|
||||
test in both directions (normal and reverse), with both the client and
|
||||
server sending and receiving data simultaneously
|
||||
.TP
|
||||
.BR -w ", " --window " \fIn\fR[KMGT]"
|
||||
set socket buffer size / window size.
|
||||
This value gets sent to the server and used on that side too; on both
|
||||
sides this option sets both the sending and receiving socket buffer sizes.
|
||||
This option can be used to set (indirectly) the maximum TCP window size.
|
||||
Note that on Linux systems, the effective maximum window size is approximately
|
||||
double what is specified by this option (this behavior is not a bug in iperf3
|
||||
but a "feature" of the Linux kernel, as documented by tcp(7) and socket(7)).
|
||||
.TP
|
||||
.BR -M ", " --set-mss " \fIn\fR"
|
||||
set TCP/SCTP maximum segment size (MTU - 40 bytes)
|
||||
.TP
|
||||
.BR -N ", " --no-delay " "
|
||||
set TCP/SCTP no delay, disabling Nagle's Algorithm
|
||||
.TP
|
||||
.BR -4 ", " --version4 " "
|
||||
only use IPv4
|
||||
.TP
|
||||
.BR -6 ", " --version6 " "
|
||||
only use IPv6
|
||||
.TP
|
||||
.BR -S ", " --tos " \fIn\fR"
|
||||
set the IP type of service. The usual prefixes for octal and hex can be used,
|
||||
i.e. 52, 064 and 0x34 all specify the same value.
|
||||
.TP
|
||||
.BR "--dscp " \fIdscp\fR
|
||||
set the IP DSCP bits. Both numeric and symbolic values are accepted. Numeric
|
||||
values can be specified in decimal, octal and hex (see --tos above).
|
||||
.TP
|
||||
.BR -L ", " --flowlabel " \fIn\fR"
|
||||
set the IPv6 flow label (currently only supported on Linux)
|
||||
.TP
|
||||
.BR -X ", " --xbind " \fIname\fR"
|
||||
Bind SCTP associations to a specific subset of links using sctp_bindx(3).
|
||||
The \fB--B\fR flag will be ignored if this flag is specified.
|
||||
Normally SCTP will include the protocol addresses of all active links
|
||||
on the local host when setting up an association. Specifying at least
|
||||
one \fB--X\fR name will disable this behaviour.
|
||||
This flag must be specified for each link to be included in the
|
||||
association, and is supported for both iperf servers and clients
|
||||
(the latter are supported by passing the first \fB--X\fR argument to bind(2)).
|
||||
Hostnames are accepted as arguments and are resolved using
|
||||
getaddrinfo(3).
|
||||
If the \fB--4\fR or \fB--6\fR flags are specified, names
|
||||
which do not resolve to addresses within the
|
||||
specified protocol family will be ignored.
|
||||
.TP
|
||||
.BR --nstreams " \fIn\fR"
|
||||
Set number of SCTP streams.
|
||||
.TP
|
||||
.BR -Z ", " --zerocopy " "
|
||||
Use a "zero copy" method of sending data, such as sendfile(2),
|
||||
instead of the usual write(2).
|
||||
.TP
|
||||
.BR --skip-rx-copy
|
||||
Ignored received packet data, using the MSG_TRUNC flag to the
|
||||
recv(2) system call.
|
||||
.TP
|
||||
.BR -O ", " --omit " \fIn\fR"
|
||||
Perform pre-test for N seconds and omit the pre-test statistics, to skip past the TCP slow-start
|
||||
period.
|
||||
.TP
|
||||
.BR -T ", " --title " \fIstr\fR"
|
||||
Prefix every output line with this string.
|
||||
.TP
|
||||
.BR --extra-data " \fIstr\fR"
|
||||
Specify an extra data string field to be included in JSON output.
|
||||
.TP
|
||||
.BR -C ", " --congestion " \fIalgo\fR"
|
||||
Set the congestion control algorithm (Linux and FreeBSD only). An
|
||||
older
|
||||
.B --linux-congestion
|
||||
synonym for this flag is accepted but is deprecated.
|
||||
.TP
|
||||
.BR "--get-server-output"
|
||||
Get the output from the server.
|
||||
The output format is determined by the server (in particular, if the
|
||||
server was invoked with the \fB--json\fR flag, the output will be in
|
||||
JSON format, otherwise it will be in human-readable format).
|
||||
If the client is run with \fB--json\fR, the server output is included
|
||||
in a JSON object; otherwise it is appended at the bottom of the
|
||||
human-readable output.
|
||||
.TP
|
||||
.BR --udp-counters-64bit
|
||||
Use 64-bit counters in UDP test packets.
|
||||
The use of this option can help prevent counter overflows during long
|
||||
or high-bitrate UDP tests. Both client and server need to be running
|
||||
at least version 3.1 for this option to work. It may become the
|
||||
default behavior at some point in the future.
|
||||
.TP
|
||||
.BR --repeating-payload
|
||||
Use repeating pattern in payload, instead of random bytes.
|
||||
The same payload is used in iperf2 (ASCII '0..9' repeating).
|
||||
It might help to test and reveal problems in networking gear with hardware
|
||||
compression (including some WiFi access points), where iperf2 and iperf3
|
||||
perform differently, just based on payload entropy.
|
||||
.TP
|
||||
.BR --dont-fragment
|
||||
Set the IPv4 Don't Fragment (DF) bit on outgoing packets.
|
||||
Only applicable to tests doing UDP over IPv4.
|
||||
.TP
|
||||
.BR --username " \fIusername\fR"
|
||||
username to use for authentication to the iperf server (if built with
|
||||
OpenSSL support).
|
||||
The password will be prompted for interactively when the test is run. Note,
|
||||
the password to use can also be specified via the IPERF3_PASSWORD environment
|
||||
variable. If this variable is present, the password prompt will be skipped.
|
||||
.TP
|
||||
.BR --rsa-public-key-path " \fIfile\fR"
|
||||
path to the RSA public key used to encrypt authentication credentials
|
||||
(if built with OpenSSL support)
|
||||
|
||||
.SH EXAMPLES
|
||||
.SS "Authentication - RSA Keypair"
|
||||
The authentication feature of iperf3 requires an RSA public keypair.
|
||||
The public key is used to encrypt the authentication token containing the
|
||||
user credentials, while the private key is used to decrypt the authentication token.
|
||||
The private key must be in PEM format and additionally must not have a
|
||||
password set.
|
||||
The public key must be in PEM format and use SubjectPrefixKeyInfo encoding.
|
||||
An example of a set of UNIX/Linux commands using OpenSSL
|
||||
to generate a correctly-formed keypair follows:
|
||||
.sp 1
|
||||
.in +.5i
|
||||
> openssl genrsa -des3 -out private.pem 2048
|
||||
.sp 0
|
||||
> openssl rsa -in private.pem -outform PEM -pubout -out public.pem
|
||||
.sp 0
|
||||
> openssl rsa -in private.pem -out private_not_protected.pem -outform PEM
|
||||
.in -.5i
|
||||
.sp 1
|
||||
After these commands, the public key will be contained in the file
|
||||
public.pem and the private key will be contained in the file
|
||||
private_not_protected.pem.
|
||||
.SS "Authentication - Authorized users configuration file"
|
||||
A simple plaintext file must be provided to the iperf3 server in order to specify
|
||||
the authorized user credentials.
|
||||
The file is a simple list of comma-separated pairs of a username and a
|
||||
corresponding password hash.
|
||||
The password hash is a SHA256 hash of the string "{$user}$password".
|
||||
The file can also contain commented lines (starting with the \fC#\fR
|
||||
character).
|
||||
An example of commands to generate the password hash on a UNIX/Linux system
|
||||
is given below:
|
||||
.sp 1
|
||||
.in +.5i
|
||||
> S_USER=mario S_PASSWD=rossi
|
||||
.sp 0
|
||||
> echo -n "{$S_USER}$S_PASSWD" | sha256sum | awk '{ print $1 }'
|
||||
.in -.5i
|
||||
.sp 1
|
||||
An example of a password file (with an entry corresponding to the
|
||||
above username and password) is given below:
|
||||
.sp 0
|
||||
.in +.5i
|
||||
> cat credentials.csv
|
||||
.sp 0
|
||||
# file format: username,sha256
|
||||
.sp 0
|
||||
mario,bf7a49a846d44b454a5d11e7acfaf13d138bbe0b7483aa3e050879700572709b
|
||||
.in -.5i
|
||||
.sp 1
|
||||
|
||||
.SH AUTHORS
|
||||
A list of the contributors to iperf3 can be found within the
|
||||
documentation located at
|
||||
\fChttps://software.es.net/iperf/dev.html#authors\fR.
|
||||
|
||||
.SH "SEE ALSO"
|
||||
libiperf(3),
|
||||
https://software.es.net/iperf
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,518 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2025, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#ifndef __IPERF_API_H
|
||||
#define __IPERF_API_H
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" { /* open extern "C" */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Atomic types highly desired, but if not, we approximate what we need
|
||||
* with normal integers and warn.
|
||||
*/
|
||||
#ifdef HAVE_STDATOMIC_H
|
||||
#include <stdatomic.h>
|
||||
#else
|
||||
#warning "No <stdatomic.h> available"
|
||||
typedef u_int64_t atomic_uint_fast64_t;
|
||||
#endif // HAVE_STDATOMIC_H
|
||||
|
||||
struct iperf_test;
|
||||
struct iperf_stream_result;
|
||||
struct iperf_interval_results;
|
||||
struct iperf_stream;
|
||||
struct iperf_time;
|
||||
|
||||
#if !defined(__IPERF_H)
|
||||
typedef uint_fast64_t iperf_size_t;
|
||||
typedef atomic_uint_fast64_t atomic_iperf_size_t;
|
||||
#endif // __IPERF_H
|
||||
|
||||
/* default settings */
|
||||
#define Ptcp SOCK_STREAM
|
||||
#define Pudp SOCK_DGRAM
|
||||
#define Psctp 12
|
||||
#define DEFAULT_UDP_BLKSIZE 1460 /* default is dynamically set, else this */
|
||||
#define DEFAULT_TCP_BLKSIZE (128 * 1024) /* default read/write block size */
|
||||
#define DEFAULT_SCTP_BLKSIZE (64 * 1024)
|
||||
#define DEFAULT_PACING_TIMER 1000
|
||||
#define DEFAULT_NO_MSG_RCVD_TIMEOUT 120000
|
||||
#define MIN_NO_MSG_RCVD_TIMEOUT 100
|
||||
|
||||
#define WARN_STR_LEN 128
|
||||
|
||||
/* short option equivalents, used to support options that only have long form */
|
||||
#define OPT_SCTP 1
|
||||
#define OPT_LOGFILE 2
|
||||
#define OPT_GET_SERVER_OUTPUT 3
|
||||
#define OPT_UDP_COUNTERS_64BIT 4
|
||||
#define OPT_CLIENT_PORT 5
|
||||
#define OPT_NUMSTREAMS 6
|
||||
#define OPT_FORCEFLUSH 7
|
||||
#define OPT_NO_FQ_SOCKET_PACING 9 /* UNUSED */
|
||||
#define OPT_FQ_RATE 10
|
||||
#define OPT_DSCP 11
|
||||
#define OPT_CLIENT_USERNAME 12
|
||||
#define OPT_CLIENT_RSA_PUBLIC_KEY 13
|
||||
#define OPT_SERVER_RSA_PRIVATE_KEY 14
|
||||
#define OPT_SERVER_AUTHORIZED_USERS 15
|
||||
#define OPT_PACING_TIMER 16
|
||||
#define OPT_CONNECT_TIMEOUT 17
|
||||
#define OPT_REPEATING_PAYLOAD 18
|
||||
#define OPT_EXTRA_DATA 19
|
||||
#define OPT_BIDIRECTIONAL 20
|
||||
#define OPT_SERVER_BITRATE_LIMIT 21
|
||||
#define OPT_TIMESTAMPS 22
|
||||
#define OPT_SERVER_SKEW_THRESHOLD 23
|
||||
#define OPT_BIND_DEV 24
|
||||
#define OPT_IDLE_TIMEOUT 25
|
||||
#define OPT_DONT_FRAGMENT 26
|
||||
#define OPT_RCV_TIMEOUT 27
|
||||
#define OPT_JSON_STREAM 28
|
||||
#define OPT_SND_TIMEOUT 29
|
||||
#define OPT_USE_PKCS1_PADDING 30
|
||||
#define OPT_CNTL_KA 31
|
||||
#define OPT_SKIP_RX_COPY 32
|
||||
|
||||
/* states */
|
||||
#define TEST_START 1
|
||||
#define TEST_RUNNING 2
|
||||
#define RESULT_REQUEST 3 /* not used */
|
||||
#define TEST_END 4
|
||||
#define STREAM_BEGIN 5 /* not used */
|
||||
#define STREAM_RUNNING 6 /* not used */
|
||||
#define STREAM_END 7 /* not used */
|
||||
#define ALL_STREAMS_END 8 /* not used */
|
||||
#define PARAM_EXCHANGE 9
|
||||
#define CREATE_STREAMS 10
|
||||
#define SERVER_TERMINATE 11
|
||||
#define CLIENT_TERMINATE 12
|
||||
#define EXCHANGE_RESULTS 13
|
||||
#define DISPLAY_RESULTS 14
|
||||
#define IPERF_START 15
|
||||
#define IPERF_DONE 16
|
||||
#define ACCESS_DENIED (-1)
|
||||
#define SERVER_ERROR (-2)
|
||||
|
||||
/* Getter routines for some fields inside iperf_test. */
|
||||
int iperf_get_verbose( struct iperf_test* ipt );
|
||||
int iperf_get_control_socket( struct iperf_test* ipt );
|
||||
int iperf_get_test_omit( struct iperf_test* ipt );
|
||||
int iperf_get_test_duration( struct iperf_test* ipt );
|
||||
char iperf_get_test_role( struct iperf_test* ipt );
|
||||
int iperf_get_test_reverse( struct iperf_test* ipt );
|
||||
int iperf_get_test_bidirectional( struct iperf_test* ipt );
|
||||
int iperf_get_test_blksize( struct iperf_test* ipt );
|
||||
FILE* iperf_get_test_outfile( struct iperf_test* ipt );
|
||||
uint64_t iperf_get_test_rate( struct iperf_test* ipt );
|
||||
int iperf_get_test_pacing_timer( struct iperf_test* ipt );
|
||||
uint64_t iperf_get_test_bytes( struct iperf_test* ipt );
|
||||
uint64_t iperf_get_test_blocks( struct iperf_test* ipt );
|
||||
int iperf_get_test_burst( struct iperf_test* ipt );
|
||||
int iperf_get_test_socket_bufsize( struct iperf_test* ipt );
|
||||
double iperf_get_test_reporter_interval( struct iperf_test* ipt );
|
||||
double iperf_get_test_stats_interval( struct iperf_test* ipt );
|
||||
int iperf_get_test_num_streams( struct iperf_test* ipt );
|
||||
int iperf_get_test_repeating_payload( struct iperf_test* ipt );
|
||||
int iperf_get_test_timestamps( struct iperf_test* ipt );
|
||||
const char* iperf_get_test_timestamp_format( struct iperf_test* ipt );
|
||||
int iperf_get_test_bind_port( struct iperf_test* ipt );
|
||||
int iperf_get_test_server_port( struct iperf_test* ipt );
|
||||
char* iperf_get_test_server_hostname( struct iperf_test* ipt );
|
||||
char* iperf_get_test_template( struct iperf_test* ipt );
|
||||
int iperf_get_test_protocol_id( struct iperf_test* ipt );
|
||||
int iperf_get_test_json_output( struct iperf_test* ipt );
|
||||
char* iperf_get_test_json_output_string ( struct iperf_test* ipt );
|
||||
int iperf_get_test_json_stream( struct iperf_test* ipt );
|
||||
int iperf_get_test_zerocopy( struct iperf_test* ipt );
|
||||
int iperf_get_test_get_server_output( struct iperf_test* ipt );
|
||||
char iperf_get_test_unit_format(struct iperf_test *ipt);
|
||||
char* iperf_get_test_bind_address ( struct iperf_test* ipt );
|
||||
char* iperf_get_test_bind_dev(struct iperf_test *ipt);
|
||||
int iperf_get_test_udp_counters_64bit( struct iperf_test* ipt );
|
||||
int iperf_get_test_one_off( struct iperf_test* ipt );
|
||||
int iperf_get_test_tos( struct iperf_test* ipt );
|
||||
char* iperf_get_extra_data( struct iperf_test* ipt );
|
||||
char* iperf_get_iperf_version(void);
|
||||
int iperf_get_test_no_delay( struct iperf_test* ipt );
|
||||
int iperf_get_test_connect_timeout( struct iperf_test* ipt );
|
||||
int iperf_get_dont_fragment( struct iperf_test* ipt );
|
||||
char* iperf_get_test_congestion_control(struct iperf_test* ipt);
|
||||
int iperf_get_test_mss(struct iperf_test* ipt);
|
||||
int iperf_get_mapped_v4(struct iperf_test* ipt);
|
||||
|
||||
/* Setter routines for some fields inside iperf_test. */
|
||||
void iperf_set_verbose( struct iperf_test* ipt, int verbose );
|
||||
void iperf_set_control_socket( struct iperf_test* ipt, int ctrl_sck );
|
||||
void iperf_set_test_omit( struct iperf_test* ipt, int omit );
|
||||
void iperf_set_test_duration( struct iperf_test* ipt, int duration );
|
||||
void iperf_set_test_reporter_interval( struct iperf_test* ipt, double reporter_interval );
|
||||
void iperf_set_test_stats_interval( struct iperf_test* ipt, double stats_interval );
|
||||
void iperf_set_test_state( struct iperf_test* ipt, signed char state );
|
||||
void iperf_set_test_blksize( struct iperf_test* ipt, int blksize );
|
||||
void iperf_set_test_logfile( struct iperf_test* ipt, const char *logfile );
|
||||
void iperf_set_test_rate( struct iperf_test* ipt, uint64_t rate );
|
||||
void iperf_set_test_pacing_timer( struct iperf_test* ipt, int pacing_timer );
|
||||
void iperf_set_test_bytes( struct iperf_test* ipt, uint64_t bytes );
|
||||
void iperf_set_test_blocks( struct iperf_test* ipt, uint64_t blocks );
|
||||
void iperf_set_test_burst( struct iperf_test* ipt, int burst );
|
||||
void iperf_set_test_bind_port( struct iperf_test* ipt, int bind_port );
|
||||
void iperf_set_test_server_port( struct iperf_test* ipt, int server_port );
|
||||
void iperf_set_test_socket_bufsize( struct iperf_test* ipt, int socket_bufsize );
|
||||
void iperf_set_test_num_streams( struct iperf_test* ipt, int num_streams );
|
||||
void iperf_set_test_repeating_payload( struct iperf_test* ipt, int repeating_payload );
|
||||
void iperf_set_test_timestamps( struct iperf_test* ipt, int timestamps );
|
||||
void iperf_set_test_timestamp_format( struct iperf_test*, const char *tf );
|
||||
void iperf_set_test_role( struct iperf_test* ipt, char role );
|
||||
void iperf_set_test_server_hostname( struct iperf_test* ipt, const char* server_hostname );
|
||||
void iperf_set_test_template( struct iperf_test *ipt, const char *tmp_template );
|
||||
void iperf_set_test_reverse( struct iperf_test* ipt, int reverse );
|
||||
void iperf_set_test_json_output( struct iperf_test* ipt, int json_output );
|
||||
void iperf_set_test_json_stream( struct iperf_test* ipt, int json_stream );
|
||||
void iperf_set_test_json_callback(struct iperf_test *ipt, void (*callback)(struct iperf_test *, char *));
|
||||
int iperf_has_zerocopy( void );
|
||||
void iperf_set_test_zerocopy( struct iperf_test* ipt, int zerocopy );
|
||||
void iperf_set_test_get_server_output( struct iperf_test* ipt, int get_server_output );
|
||||
void iperf_set_test_unit_format(struct iperf_test *ipt, char unit_format);
|
||||
void iperf_set_test_bind_address( struct iperf_test* ipt, const char *bind_address );
|
||||
void iperf_set_test_bind_dev(struct iperf_test *ipt, const char *bnd_dev);
|
||||
void iperf_set_test_udp_counters_64bit( struct iperf_test* ipt, int udp_counters_64bit );
|
||||
void iperf_set_test_one_off( struct iperf_test* ipt, int one_off );
|
||||
void iperf_set_test_tos( struct iperf_test* ipt, int tos );
|
||||
void iperf_set_test_extra_data( struct iperf_test* ipt, const char *dat );
|
||||
void iperf_set_test_bidirectional( struct iperf_test* ipt, int bidirectional);
|
||||
void iperf_set_test_no_delay( struct iperf_test* ipt, int no_delay);
|
||||
void iperf_set_dont_fragment( struct iperf_test* ipt, int dont_fragment );
|
||||
void iperf_set_test_congestion_control(struct iperf_test* ipt, char* cc);
|
||||
void iperf_set_test_mss(struct iperf_test* ipt, int mss);
|
||||
void iperf_set_mapped_v4(struct iperf_test* ipt, const int val);
|
||||
void iperf_set_on_new_stream_callback(struct iperf_test* ipt, void (*callback)(struct iperf_stream *));
|
||||
void iperf_set_on_test_start_callback(struct iperf_test* ipt, void (*callback)(struct iperf_test *));
|
||||
void iperf_set_on_test_connect_callback(struct iperf_test* ipt, void (*callback)(struct iperf_test *));
|
||||
void iperf_set_on_test_finish_callback(struct iperf_test* ipt, void (*callback)(struct iperf_test *));
|
||||
|
||||
#if defined(HAVE_SSL)
|
||||
void iperf_set_test_client_username(struct iperf_test *ipt, const char *client_username);
|
||||
void iperf_set_test_client_password(struct iperf_test *ipt, const char *client_password);
|
||||
void iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, const char *client_rsa_pubkey_base64);
|
||||
void iperf_set_test_server_authorized_users(struct iperf_test *ipt, const char *server_authorized_users);
|
||||
void iperf_set_test_server_skew_threshold(struct iperf_test *ipt, int server_skew_threshold);
|
||||
void iperf_set_test_server_rsa_privkey(struct iperf_test *ipt, const char *server_rsa_privkey_base64);
|
||||
#endif // HAVE_SSL
|
||||
|
||||
void iperf_set_test_connect_timeout(struct iperf_test *ipt, int ct);
|
||||
|
||||
/**
|
||||
* exchange_parameters - handles the param_Exchange part for client
|
||||
*
|
||||
*/
|
||||
int iperf_exchange_parameters(struct iperf_test * test);
|
||||
|
||||
/**
|
||||
* add_to_interval_list -- adds new interval to the interval_list
|
||||
*
|
||||
*/
|
||||
void add_to_interval_list(struct iperf_stream_result * rp, struct iperf_interval_results *temp);
|
||||
|
||||
/**
|
||||
* connect_msg -- displays connection message
|
||||
* denoting senfer/receiver details
|
||||
*
|
||||
*/
|
||||
void connect_msg(struct iperf_stream * sp);
|
||||
|
||||
/**
|
||||
* iperf_stats_callback -- handles the statistic gathering
|
||||
*
|
||||
*/
|
||||
void iperf_stats_callback(struct iperf_test * test);
|
||||
|
||||
/**
|
||||
* iperf_reporter_callback -- handles the report printing
|
||||
*
|
||||
*/
|
||||
void iperf_reporter_callback(struct iperf_test * test);
|
||||
|
||||
/**
|
||||
* iperf_new_test -- return a new iperf_test with default values
|
||||
*
|
||||
* returns NULL on failure
|
||||
*
|
||||
*/
|
||||
struct iperf_test *iperf_new_test(void);
|
||||
|
||||
int iperf_defaults(struct iperf_test * testp);
|
||||
|
||||
/**
|
||||
* iperf_free_test -- free resources used by test, calls iperf_free_stream to
|
||||
* free streams
|
||||
*
|
||||
*/
|
||||
void iperf_free_test(struct iperf_test * testp);
|
||||
|
||||
/**
|
||||
* iperf_new_stream -- return a net iperf_stream with default values
|
||||
*
|
||||
* returns NULL on failure
|
||||
*
|
||||
*/
|
||||
struct iperf_stream *iperf_new_stream(struct iperf_test *, int, int);
|
||||
|
||||
/**
|
||||
* iperf_add_stream -- add a stream to a test
|
||||
*
|
||||
*/
|
||||
void iperf_add_stream(struct iperf_test * test, struct iperf_stream * stream);
|
||||
|
||||
/**
|
||||
* iperf_init_stream -- init resources associated with test
|
||||
*
|
||||
*/
|
||||
int iperf_init_stream(struct iperf_stream *, struct iperf_test *);
|
||||
|
||||
/**
|
||||
* iperf_free_stream -- free resources associated with test
|
||||
*
|
||||
*/
|
||||
void iperf_free_stream(struct iperf_stream * sp);
|
||||
|
||||
/**
|
||||
* iperf_common_sockopts -- init stream socket with common socket options
|
||||
*
|
||||
*/
|
||||
int iperf_common_sockopts(struct iperf_test *, int s);
|
||||
|
||||
#if defined (HAVE_TCP_KEEPALIVE)
|
||||
/**
|
||||
* iperf_set_control_keepalive -- set control connection TCP keepalive
|
||||
*
|
||||
*/
|
||||
int iperf_set_control_keepalive(struct iperf_test *test);
|
||||
#endif //HAVE_TCP_KEEPALIVE
|
||||
|
||||
int has_tcpinfo(void);
|
||||
int has_tcpinfo_retransmits(void);
|
||||
void save_tcpinfo(struct iperf_stream *sp, struct iperf_interval_results *irp);
|
||||
long get_total_retransmits(struct iperf_interval_results *irp);
|
||||
long get_snd_cwnd(struct iperf_interval_results *irp);
|
||||
long get_snd_wnd(struct iperf_interval_results *irp);
|
||||
long get_rtt(struct iperf_interval_results *irp);
|
||||
long get_rttvar(struct iperf_interval_results *irp);
|
||||
long get_pmtu(struct iperf_interval_results *irp);
|
||||
void print_tcpinfo(struct iperf_test *test);
|
||||
void build_tcpinfo_message(struct iperf_interval_results *r, char *message);
|
||||
|
||||
int iperf_set_send_state(struct iperf_test *test, signed char state);
|
||||
void iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP);
|
||||
int iperf_send_mt(struct iperf_stream *) /* __attribute__((hot)) */;
|
||||
int iperf_recv_mt(struct iperf_stream *);
|
||||
void iperf_catch_sigend(void (*handler)(int));
|
||||
void iperf_got_sigend(struct iperf_test *test, int sig) __attribute__ ((noreturn));
|
||||
void usage(void);
|
||||
void usage_long(FILE * f);
|
||||
void warning(const char *);
|
||||
int iperf_exchange_results(struct iperf_test *);
|
||||
int iperf_init_test(struct iperf_test *);
|
||||
int iperf_create_send_timers(struct iperf_test *);
|
||||
int iperf_parse_arguments(struct iperf_test *, int, char **);
|
||||
int iperf_open_logfile(struct iperf_test *);
|
||||
void iperf_close_logfile(struct iperf_test *);
|
||||
void iperf_reset_test(struct iperf_test *);
|
||||
void iperf_reset_stats(struct iperf_test * test);
|
||||
|
||||
struct protocol *get_protocol(struct iperf_test *, int);
|
||||
int set_protocol(struct iperf_test *, int);
|
||||
|
||||
void iperf_on_new_stream(struct iperf_stream *);
|
||||
void iperf_on_test_start(struct iperf_test *);
|
||||
void iperf_on_connect(struct iperf_test *);
|
||||
void iperf_on_test_finish(struct iperf_test *);
|
||||
|
||||
extern jmp_buf env;
|
||||
|
||||
/* Client routines. */
|
||||
int iperf_run_client(struct iperf_test *);
|
||||
int iperf_connect(struct iperf_test *);
|
||||
int iperf_create_streams(struct iperf_test *, int sender);
|
||||
int iperf_handle_message_client(struct iperf_test *);
|
||||
int iperf_client_end(struct iperf_test *);
|
||||
|
||||
/* Server routines. */
|
||||
int iperf_run_server(struct iperf_test *);
|
||||
int iperf_server_listen(struct iperf_test *);
|
||||
int iperf_accept(struct iperf_test *);
|
||||
int iperf_handle_message_server(struct iperf_test *);
|
||||
int iperf_create_pidfile(struct iperf_test *);
|
||||
int iperf_delete_pidfile(struct iperf_test *);
|
||||
void iperf_check_total_rate(struct iperf_test *, iperf_size_t);
|
||||
|
||||
/* JSON output routines. */
|
||||
int iperf_json_start(struct iperf_test *);
|
||||
int iperf_json_finish(struct iperf_test *);
|
||||
|
||||
/* CPU affinity routines */
|
||||
int iperf_setaffinity(struct iperf_test *, int affinity);
|
||||
int iperf_clearaffinity(struct iperf_test *);
|
||||
|
||||
/* Custom printf routine. */
|
||||
int iperf_printf(struct iperf_test *test, const char *format, ...) __attribute__ ((format(printf,2,3)));
|
||||
int iflush(struct iperf_test *test);
|
||||
|
||||
/* Error routines. */
|
||||
void iperf_err(struct iperf_test *test, const char *format, ...) __attribute__ ((format(printf,2,3)));
|
||||
void iperf_errexit(struct iperf_test *test, const char *format, ...) __attribute__ ((format(printf,2,3),noreturn));
|
||||
void iperf_signormalexit(struct iperf_test *test, const char *format, ...) __attribute__ ((format(printf,2,3),noreturn));
|
||||
void iperf_exit(struct iperf_test *test, int exit_code, const char *format, va_list argp) __attribute__ ((noreturn));
|
||||
char *iperf_strerror(int);
|
||||
extern int i_errno;
|
||||
enum {
|
||||
IENONE = 0, // No error
|
||||
/* Parameter errors */
|
||||
IESERVCLIENT = 1, // Iperf cannot be both server and client
|
||||
IENOROLE = 2, // Iperf must either be a client (-c) or server (-s)
|
||||
IESERVERONLY = 3, // This option is server only
|
||||
IECLIENTONLY = 4, // This option is client only
|
||||
IEDURATION = 5, // test duration too long. Maximum value = %dMAX_TIME
|
||||
IENUMSTREAMS = 6, // Number of parallel streams too large. Maximum value = %dMAX_STREAMS
|
||||
IEBLOCKSIZE = 7, // Block size too large. Maximum value = %dMAX_BLOCKSIZE
|
||||
IEBUFSIZE = 8, // Socket buffer size too large. Maximum value = %dMAX_TCP_BUFFER
|
||||
IEINTERVAL = 9, // Invalid report interval (min = %gMIN_INTERVAL, max = %gMAX_INTERVAL seconds)
|
||||
IEMSS = 10, // MSS too large. Maximum value = %dMAX_MSS
|
||||
IENOSENDFILE = 11, // This OS does not support sendfile
|
||||
IEOMIT = 12, // Bogus value for --omit
|
||||
IEUNIMP = 13, // Not implemented yet
|
||||
IEFILE = 14, // -F file couldn't be opened
|
||||
IEBURST = 15, // Invalid burst count. Maximum value = %dMAX_BURST
|
||||
IEENDCONDITIONS = 16, // Only one test end condition (-t, -n, -k) may be specified
|
||||
IELOGFILE = 17, // Can't open log file
|
||||
IENOSCTP = 18, // No SCTP support available
|
||||
IEBIND = 19, // UNUSED: Local port specified with no local bind option
|
||||
IEUDPBLOCKSIZE = 20, // Block size invalid
|
||||
IEBADTOS = 21, // Bad TOS value
|
||||
IESETCLIENTAUTH = 22, // Bad configuration of client authentication
|
||||
IESETSERVERAUTH = 23, // Bad configuration of server authentication
|
||||
IEBADFORMAT = 24, // Bad format argument to -f
|
||||
IEREVERSEBIDIR = 25, // Iperf cannot be both reverse and bidirectional
|
||||
IEBADPORT = 26, // Bad port number
|
||||
IETOTALRATE = 27, // Total required bandwidth is larger than server's limit
|
||||
IETOTALINTERVAL = 28, // Invalid time interval for calculating average data rate
|
||||
IESKEWTHRESHOLD = 29, // Invalid value specified as skew threshold
|
||||
IEIDLETIMEOUT = 30, // Invalid value specified as idle state timeout
|
||||
IERCVTIMEOUT = 31, // Illegal message receive timeout
|
||||
IERVRSONLYRCVTIMEOUT = 32, // Client receive timeout is valid only in reverse mode
|
||||
IESNDTIMEOUT = 33, // Illegal message send timeout
|
||||
IEUDPFILETRANSFER = 34, // Cannot transfer file using UDP
|
||||
IESERVERAUTHUSERS = 35, // Cannot access authorized users file
|
||||
IECNTLKA = 36, // Control connection Keepalive period should be larger than the full retry period (interval * count)
|
||||
/* Test errors */
|
||||
IENEWTEST = 100, // Unable to create a new test (check perror)
|
||||
IEINITTEST = 101, // Test initialization failed (check perror)
|
||||
IELISTEN = 102, // Unable to listen for connections (check perror)
|
||||
IECONNECT = 103, // Unable to connect to server (check herror/perror) [from netdial]
|
||||
IEACCEPT = 104, // Unable to accept connection from client (check herror/perror)
|
||||
IESENDCOOKIE = 105, // Unable to send cookie to server (check perror)
|
||||
IERECVCOOKIE = 106, // Unable to receive cookie from client (check perror)
|
||||
IECTRLWRITE = 107, // Unable to write to the control socket (check perror)
|
||||
IECTRLREAD = 108, // Unable to read from the control socket (check perror)
|
||||
IECTRLCLOSE = 109, // Control socket has closed unexpectedly
|
||||
IEMESSAGE = 110, // Received an unknown message
|
||||
IESENDMESSAGE = 111, // Unable to send control message to client/server (check perror)
|
||||
IERECVMESSAGE = 112, // Unable to receive control message from client/server (check perror)
|
||||
IESENDPARAMS = 113, // Unable to send parameters to server (check perror)
|
||||
IERECVPARAMS = 114, // Unable to receive parameters from client (check perror)
|
||||
IEPACKAGERESULTS = 115, // Unable to package results (check perror)
|
||||
IESENDRESULTS = 116, // Unable to send results to client/server (check perror)
|
||||
IERECVRESULTS = 117, // Unable to receive results from client/server (check perror)
|
||||
IESELECT = 118, // Select failed (check perror)
|
||||
IECLIENTTERM = 119, // The client has terminated
|
||||
IESERVERTERM = 120, // The server has terminated
|
||||
IEACCESSDENIED = 121, // The server is busy running a test. Try again later.
|
||||
IESETNODELAY = 122, // Unable to set TCP/SCTP NODELAY (check perror)
|
||||
IESETMSS = 123, // Unable to set TCP/SCTP MSS (check perror)
|
||||
IESETBUF = 124, // Unable to set socket buffer size (check perror)
|
||||
IESETTOS = 125, // Unable to set IP TOS (check perror)
|
||||
IESETCOS = 126, // Unable to set IPv6 traffic class (check perror)
|
||||
IESETFLOW = 127, // Unable to set IPv6 flow label
|
||||
IEREUSEADDR = 128, // Unable to set reuse address on socket (check perror)
|
||||
IENONBLOCKING = 129, // Unable to set socket to non-blocking (check perror)
|
||||
IESETWINDOWSIZE = 130, // Unable to set socket window size (check perror)
|
||||
IEPROTOCOL = 131, // Protocol does not exist
|
||||
IEAFFINITY = 132, // Unable to set CPU affinity (check perror)
|
||||
IEDAEMON = 133, // Unable to become a daemon process
|
||||
IESETCONGESTION = 134, // Unable to set TCP_CONGESTION
|
||||
IEPIDFILE = 135, // Unable to write PID file
|
||||
IEV6ONLY = 136, // Unable to set/unset IPV6_V6ONLY (check perror)
|
||||
IESETSCTPDISABLEFRAG = 137, // Unable to set SCTP Fragmentation (check perror)
|
||||
IESETSCTPNSTREAM= 138, // Unable to set SCTP number of streams (check perror)
|
||||
IESETSCTPBINDX= 139, // Unable to process sctp_bindx() parameters
|
||||
IESETPACING= 140, // Unable to set socket pacing rate
|
||||
IESETBUF2= 141, // Socket buffer size incorrect (written value != read value)
|
||||
IEAUTHTEST = 142, // Test authorization failed
|
||||
IEBINDDEV = 143, // Unable to bind-to-device (check perror, maybe permissions?)
|
||||
IENOMSG = 144, // No message was received for NO_MSG_RCVD_TIMEOUT time period
|
||||
IESETDONTFRAGMENT = 145, // Unable to set IP Do-Not-Fragment
|
||||
IEBINDDEVNOSUPPORT = 146, // `ip%%dev` is not supported as system does not support bind to device
|
||||
IEHOSTDEV = 147, // host device name (ip%%<dev>) is supported (and required) only for IPv6 link-local address
|
||||
IESETUSERTIMEOUT = 148, // Unable to set TCP USER_TIMEOUT (check perror)
|
||||
IEPTHREADCREATE=150, // Unable to create thread (check perror)
|
||||
IEPTHREADCANCEL=151, // Unable to cancel thread (check perror)
|
||||
IEPTHREADJOIN=152, // Unable to join thread (check perror)
|
||||
IEPTHREADATTRINIT=153, // Unable to initialize thread attribute (check perror)
|
||||
IEPTHREADATTRDESTROY=154, // Unable to destroy thread attribute (check perror)
|
||||
IESETCNTLKA = 155, // Unable to set socket keepalive (SO_KEEPALIVE) option
|
||||
IESETCNTLKAKEEPIDLE = 156, // Unable to set socket keepalive TCP period (TCP_KEEPIDLE) option
|
||||
IESETCNTLKAINTERVAL = 157, // Unable to set/get socket keepalive TCP retry interval (TCP_KEEPINTVL) option
|
||||
IESETCNTLKACOUNT = 158, // Unable to set/get socket keepalive TCP number of retries (TCP_KEEPCNT) option
|
||||
IEPTHREADSIGMASK=159, // Unable to initialize sub thread signal mask (check perror)
|
||||
/* Stream errors */
|
||||
IECREATESTREAM = 200, // Unable to create a new stream (check herror/perror)
|
||||
IEINITSTREAM = 201, // Unable to initialize stream (check herror/perror)
|
||||
IESTREAMLISTEN = 202, // Unable to start stream listener (check perror)
|
||||
IESTREAMCONNECT = 203, // Unable to connect stream (check herror/perror)
|
||||
IESTREAMACCEPT = 204, // Unable to accepte stream connection (check perror)
|
||||
IESTREAMWRITE = 205, // Unable to write to stream socket (check perror)
|
||||
IESTREAMREAD = 206, // Unable to read from stream (check perror)
|
||||
IESTREAMCLOSE = 207, // Stream has closed unexpectedly
|
||||
IESTREAMID = 208, // Stream has invalid ID
|
||||
/* Timer errors */
|
||||
IENEWTIMER = 300, // Unable to create new timer (check perror)
|
||||
IEUPDATETIMER = 301, // Unable to update timer (check perror)
|
||||
};
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* close extern "C" */
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* !__IPERF_API_H */
|
||||
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2023, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE file
|
||||
* for complete information.
|
||||
*/
|
||||
|
||||
#include "iperf_config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
/* FreeBSD needs _WITH_GETLINE to enable the getline() declaration */
|
||||
#define _WITH_GETLINE
|
||||
#include <stdio.h>
|
||||
#include <termios.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(HAVE_SSL)
|
||||
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/buffer.h>
|
||||
#include <openssl/err.h>
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/core_names.h>
|
||||
#endif
|
||||
|
||||
const char *auth_text_format = "user: %s\npwd: %s\nts: %"PRId64;
|
||||
|
||||
void sha256(const char *string, char outputBuffer[65])
|
||||
{
|
||||
unsigned char hash[SHA256_DIGEST_LENGTH];
|
||||
|
||||
SHA256((const unsigned char *) string, strlen(string), hash);
|
||||
int i = 0;
|
||||
for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
|
||||
{
|
||||
sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
|
||||
}
|
||||
outputBuffer[64] = 0;
|
||||
}
|
||||
|
||||
int check_authentication(const char *username, const char *password, const time_t ts, const char *filename, int skew_threshold){
|
||||
time_t t = time(NULL);
|
||||
time_t utc_seconds = mktime(localtime(&t));
|
||||
if ( (utc_seconds - ts) > skew_threshold || (utc_seconds - ts) < -skew_threshold ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
char passwordHash[65];
|
||||
char salted[strlen(username) + strlen(password) + 3];
|
||||
sprintf(salted, "{%s}%s", username, password);
|
||||
sha256(&salted[0], passwordHash);
|
||||
|
||||
char *s_username, *s_password;
|
||||
int i;
|
||||
FILE *ptr_file;
|
||||
char buf[1024];
|
||||
|
||||
ptr_file =fopen(filename,"r");
|
||||
if (!ptr_file)
|
||||
return 2;
|
||||
|
||||
while (fgets(buf,1024, ptr_file)){
|
||||
//strip the \n or \r\n chars
|
||||
for (i = 0; buf[i] != '\0'; i++){
|
||||
if (buf[i] == '\n' || buf[i] == '\r'){
|
||||
buf[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
//skip empty / not completed / comment lines
|
||||
if (strlen(buf) == 0 || strchr(buf, ',') == NULL || buf[0] == '#'){
|
||||
continue;
|
||||
}
|
||||
s_username = strtok(buf, ",");
|
||||
s_password = strtok(NULL, ",");
|
||||
if (strcmp( username, s_username ) == 0 && strcmp( passwordHash, s_password ) == 0){
|
||||
fclose(ptr_file);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
fclose(ptr_file);
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
int Base64Encode(const unsigned char* buffer, const size_t length, char** b64text) { //Encodes a binary safe base 64 string
|
||||
BIO *bio, *b64;
|
||||
BUF_MEM *bufferPtr;
|
||||
|
||||
b64 = BIO_new(BIO_f_base64());
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
bio = BIO_push(b64, bio);
|
||||
|
||||
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Ignore newlines - write everything in one line
|
||||
BIO_write(bio, buffer, length);
|
||||
BIO_flush(bio);
|
||||
BIO_get_mem_ptr(bio, &bufferPtr);
|
||||
*b64text = strndup( (*bufferPtr).data, (*bufferPtr).length );
|
||||
BIO_free_all(bio);
|
||||
|
||||
return (0); //success
|
||||
}
|
||||
|
||||
size_t calcDecodeLength(const char* b64input) { //Calculates the length of a decoded string
|
||||
size_t len = strlen(b64input), padding = 0;
|
||||
if (b64input[len-1] == '=' && b64input[len-2] == '=') //last two chars are =
|
||||
padding = 2;
|
||||
else if (b64input[len-1] == '=') //last char is =
|
||||
padding = 1;
|
||||
|
||||
return (len*3)/4 - padding;
|
||||
}
|
||||
|
||||
int Base64Decode(const char* b64message, unsigned char** buffer, size_t* length) { //Decodes a base64 encoded string
|
||||
BIO *bio, *b64;
|
||||
|
||||
int decodeLen = calcDecodeLength(b64message);
|
||||
*buffer = (unsigned char*)malloc(decodeLen + 1);
|
||||
(*buffer)[decodeLen] = '\0';
|
||||
|
||||
bio = BIO_new_mem_buf(b64message, -1);
|
||||
b64 = BIO_new(BIO_f_base64());
|
||||
bio = BIO_push(b64, bio);
|
||||
|
||||
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Do not use newlines to flush buffer
|
||||
*length = BIO_read(bio, *buffer, strlen(b64message));
|
||||
assert(*length == decodeLen); //length should equal decodeLen, else something went horribly wrong
|
||||
BIO_free_all(bio);
|
||||
|
||||
return (0); //success
|
||||
}
|
||||
|
||||
EVP_PKEY *load_pubkey_from_file(const char *file) {
|
||||
BIO *key = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
|
||||
if (file) {
|
||||
key = BIO_new_file(file, "r");
|
||||
if (key != NULL) {
|
||||
pkey = PEM_read_bio_PUBKEY(key, NULL, NULL, NULL);
|
||||
BIO_free(key);
|
||||
}
|
||||
}
|
||||
return (pkey);
|
||||
}
|
||||
|
||||
EVP_PKEY *load_pubkey_from_base64(const char *buffer) {
|
||||
unsigned char *key = NULL;
|
||||
size_t key_len;
|
||||
Base64Decode(buffer, &key, &key_len);
|
||||
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
BIO_write(bio, key, key_len);
|
||||
free(key);
|
||||
EVP_PKEY *pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
|
||||
BIO_free(bio);
|
||||
return (pkey);
|
||||
}
|
||||
|
||||
EVP_PKEY *load_privkey_from_file(const char *file) {
|
||||
BIO *key = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
|
||||
if (file) {
|
||||
key = BIO_new_file(file, "r");
|
||||
if (key != NULL) {
|
||||
pkey = PEM_read_bio_PrivateKey(key, NULL, NULL, NULL);
|
||||
BIO_free(key);
|
||||
}
|
||||
}
|
||||
return (pkey);
|
||||
}
|
||||
|
||||
EVP_PKEY *load_privkey_from_base64(const char *buffer) {
|
||||
unsigned char *key = NULL;
|
||||
size_t key_len;
|
||||
Base64Decode(buffer, &key, &key_len);
|
||||
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
BIO_write(bio, key, key_len);
|
||||
free(key);
|
||||
EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
|
||||
BIO_free(bio);
|
||||
return (pkey);
|
||||
}
|
||||
|
||||
int test_load_pubkey_from_file(const char *file){
|
||||
EVP_PKEY *key = load_pubkey_from_file(file);
|
||||
if (key == NULL){
|
||||
return -1;
|
||||
}
|
||||
EVP_PKEY_free(key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_load_private_key_from_file(const char *file){
|
||||
EVP_PKEY *key = load_privkey_from_file(file);
|
||||
if (key == NULL){
|
||||
return -1;
|
||||
}
|
||||
EVP_PKEY_free(key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int encrypt_rsa_message(const char *plaintext, EVP_PKEY *public_key, unsigned char **encryptedtext, int use_pkcs1_padding) {
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
EVP_PKEY_CTX *ctx;
|
||||
#else
|
||||
RSA *rsa = NULL;
|
||||
#endif
|
||||
unsigned char *rsa_buffer = NULL;
|
||||
size_t encryptedtext_len = 0;
|
||||
int rsa_buffer_len, keysize;
|
||||
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
int rc;
|
||||
ctx = EVP_PKEY_CTX_new_from_pkey(NULL, public_key, "");
|
||||
/* See evp_pkey_rsa(7) and provider-keymgmt(7) */
|
||||
rc = EVP_PKEY_get_int_param(public_key, OSSL_PKEY_PARAM_MAX_SIZE, &keysize); /* XXX not really keysize */
|
||||
if (!rc) {
|
||||
goto errreturn;
|
||||
}
|
||||
#else
|
||||
rsa = EVP_PKEY_get1_RSA(public_key);
|
||||
keysize = RSA_size(rsa);
|
||||
#endif
|
||||
rsa_buffer = OPENSSL_malloc(keysize * 2);
|
||||
*encryptedtext = (unsigned char*)OPENSSL_malloc(keysize);
|
||||
|
||||
BIO *bioBuff = BIO_new_mem_buf((void*)plaintext, (int)strlen(plaintext));
|
||||
rsa_buffer_len = BIO_read(bioBuff, rsa_buffer, keysize * 2);
|
||||
|
||||
int padding = RSA_PKCS1_OAEP_PADDING;
|
||||
if (use_pkcs1_padding){
|
||||
padding = RSA_PKCS1_PADDING;
|
||||
}
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
EVP_PKEY_encrypt_init(ctx);
|
||||
EVP_PKEY_CTX_set_rsa_padding(ctx, padding);
|
||||
|
||||
EVP_PKEY_encrypt(ctx, *encryptedtext, &encryptedtext_len, rsa_buffer, rsa_buffer_len);
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
#else
|
||||
encryptedtext_len = RSA_public_encrypt(rsa_buffer_len, rsa_buffer, *encryptedtext, rsa, padding);
|
||||
RSA_free(rsa);
|
||||
#endif
|
||||
|
||||
OPENSSL_free(rsa_buffer);
|
||||
BIO_free(bioBuff);
|
||||
|
||||
if (encryptedtext_len <= 0) {
|
||||
goto errreturn;
|
||||
}
|
||||
|
||||
return encryptedtext_len;
|
||||
|
||||
errreturn:
|
||||
fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int decrypt_rsa_message(const unsigned char *encryptedtext, const int encryptedtext_len, EVP_PKEY *private_key, unsigned char **plaintext, int use_pkcs1_padding) {
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
EVP_PKEY_CTX *ctx;
|
||||
#else
|
||||
RSA *rsa = NULL;
|
||||
#endif
|
||||
unsigned char *rsa_buffer = NULL;
|
||||
size_t plaintext_len = 0;
|
||||
int rsa_buffer_len, keysize;
|
||||
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
int rc;
|
||||
ctx = EVP_PKEY_CTX_new_from_pkey(NULL, private_key, "");
|
||||
/* See evp_pkey_rsa(7) and provider-keymgmt(7) */
|
||||
rc = EVP_PKEY_get_int_param(private_key, OSSL_PKEY_PARAM_MAX_SIZE, &keysize); /* XXX not really keysize */
|
||||
if (!rc) {
|
||||
goto errreturn;
|
||||
}
|
||||
#else
|
||||
rsa = EVP_PKEY_get1_RSA(private_key);
|
||||
keysize = RSA_size(rsa);
|
||||
#endif
|
||||
rsa_buffer = OPENSSL_malloc(keysize * 2);
|
||||
*plaintext = (unsigned char*)OPENSSL_malloc(keysize);
|
||||
|
||||
BIO *bioBuff = BIO_new_mem_buf((void*)encryptedtext, encryptedtext_len);
|
||||
rsa_buffer_len = BIO_read(bioBuff, rsa_buffer, keysize * 2);
|
||||
|
||||
int padding = RSA_PKCS1_OAEP_PADDING;
|
||||
if (use_pkcs1_padding){
|
||||
padding = RSA_PKCS1_PADDING;
|
||||
}
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
plaintext_len = keysize;
|
||||
EVP_PKEY_decrypt_init(ctx);
|
||||
int ret = EVP_PKEY_CTX_set_rsa_padding(ctx, padding);
|
||||
if (ret < 0){
|
||||
goto errreturn;
|
||||
}
|
||||
EVP_PKEY_decrypt(ctx, *plaintext, &plaintext_len, rsa_buffer, rsa_buffer_len);
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
#else
|
||||
plaintext_len = RSA_private_decrypt(rsa_buffer_len, rsa_buffer, *plaintext, rsa, padding);
|
||||
RSA_free(rsa);
|
||||
#endif
|
||||
|
||||
OPENSSL_free(rsa_buffer);
|
||||
BIO_free(bioBuff);
|
||||
|
||||
/* Treat a decryption error as an empty string. */
|
||||
if (plaintext_len < 0) {
|
||||
plaintext_len = 0;
|
||||
}
|
||||
|
||||
return plaintext_len;
|
||||
|
||||
errreturn:
|
||||
fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int encode_auth_setting(const char *username, const char *password, EVP_PKEY *public_key, char **authtoken, int use_pkcs1_padding){
|
||||
time_t t = time(NULL);
|
||||
time_t utc_seconds = mktime(localtime(&t));
|
||||
|
||||
/*
|
||||
* Compute a pessimistic/conservative estimate of storage required.
|
||||
* It's OK to allocate too much storage but too little is bad.
|
||||
*/
|
||||
const int text_len = strlen(auth_text_format) + strlen(username) + strlen(password) + 32;
|
||||
char *text = (char *) calloc(text_len, sizeof(char));
|
||||
if (text == NULL) {
|
||||
return -1;
|
||||
}
|
||||
snprintf(text, text_len, auth_text_format, username, password, (int64_t)utc_seconds);
|
||||
|
||||
unsigned char *encrypted = NULL;
|
||||
int encrypted_len;
|
||||
encrypted_len = encrypt_rsa_message(text, public_key, &encrypted, use_pkcs1_padding);
|
||||
free(text);
|
||||
if (encrypted_len < 0) {
|
||||
return -1;
|
||||
}
|
||||
Base64Encode(encrypted, encrypted_len, authtoken);
|
||||
OPENSSL_free(encrypted);
|
||||
|
||||
return (0); //success
|
||||
}
|
||||
|
||||
int decode_auth_setting(int enable_debug, const char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts, int use_pkcs1_padding){
|
||||
unsigned char *encrypted_b64 = NULL;
|
||||
size_t encrypted_len_b64;
|
||||
int64_t utc_seconds;
|
||||
Base64Decode(authtoken, &encrypted_b64, &encrypted_len_b64);
|
||||
|
||||
unsigned char *plaintext = NULL;
|
||||
int plaintext_len;
|
||||
plaintext_len = decrypt_rsa_message(encrypted_b64, encrypted_len_b64, private_key, &plaintext, use_pkcs1_padding);
|
||||
free(encrypted_b64);
|
||||
if (plaintext_len < 0) {
|
||||
return -1;
|
||||
}
|
||||
plaintext[plaintext_len] = '\0';
|
||||
|
||||
char *s_username, *s_password;
|
||||
s_username = (char *) calloc(plaintext_len, sizeof(char));
|
||||
if (s_username == NULL) {
|
||||
return -1;
|
||||
}
|
||||
s_password = (char *) calloc(plaintext_len, sizeof(char));
|
||||
if (s_password == NULL) {
|
||||
free(s_username);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rc = sscanf((char *) plaintext, auth_text_format, s_username, s_password, &utc_seconds);
|
||||
if (rc != 3) {
|
||||
free(s_password);
|
||||
free(s_username);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (enable_debug) {
|
||||
printf("Auth Token Content:\n%s\n", plaintext);
|
||||
printf("Auth Token Credentials:\n--> %s %s\n", s_username, s_password);
|
||||
}
|
||||
*username = s_username;
|
||||
*password = s_password;
|
||||
*ts = (time_t)utc_seconds;
|
||||
OPENSSL_free(plaintext);
|
||||
return (0);
|
||||
}
|
||||
|
||||
#endif //HAVE_SSL
|
||||
|
||||
ssize_t iperf_getpass (char **lineptr, size_t *n, FILE *stream) {
|
||||
struct termios old, new;
|
||||
ssize_t nread;
|
||||
|
||||
/* Turn echoing off and fail if we can't. */
|
||||
if (tcgetattr (fileno (stream), &old) != 0)
|
||||
return -1;
|
||||
new = old;
|
||||
new.c_lflag &= ~ECHO;
|
||||
if (tcsetattr (fileno (stream), TCSAFLUSH, &new) != 0)
|
||||
return -1;
|
||||
|
||||
/* Read the password. */
|
||||
printf("Password: ");
|
||||
nread = getline (lineptr, n, stream);
|
||||
|
||||
/* Restore terminal. */
|
||||
(void) tcsetattr (fileno (stream), TCSAFLUSH, &old);
|
||||
|
||||
//strip the \n or \r\n chars
|
||||
char *buf = *lineptr;
|
||||
int i;
|
||||
for (i = 0; buf[i] != '\0'; i++){
|
||||
if (buf[i] == '\n' || buf[i] == '\r'){
|
||||
buf[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2017, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE file
|
||||
* for complete information.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <openssl/bio.h>
|
||||
|
||||
int test_load_pubkey_from_file(const char *public_keyfile);
|
||||
int test_load_private_key_from_file(const char *private_keyfile);
|
||||
EVP_PKEY *load_pubkey_from_file(const char *file);
|
||||
EVP_PKEY *load_pubkey_from_base64(const char *buffer);
|
||||
EVP_PKEY *load_privkey_from_file(const char *file);
|
||||
EVP_PKEY *load_privkey_from_base64(const char *buffer);
|
||||
int encode_auth_setting(const char *username, const char *password, EVP_PKEY *public_key, char **authtoken, int use_pkcs1_padding);
|
||||
int decode_auth_setting(int enable_debug, const char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts, int use_pkcs1_padding);
|
||||
int check_authentication(const char *username, const char *password, const time_t ts, const char *filename, int skew_threshold);
|
||||
ssize_t iperf_getpass (char **lineptr, size_t *n, FILE *stream);
|
||||
@@ -0,0 +1,882 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2023, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/uio.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "iperf.h"
|
||||
#include "iperf_api.h"
|
||||
#include "iperf_util.h"
|
||||
#include "iperf_locale.h"
|
||||
#include "iperf_time.h"
|
||||
#include "net.h"
|
||||
#include "timer.h"
|
||||
|
||||
#if defined(HAVE_TCP_CONGESTION)
|
||||
#if !defined(TCP_CA_NAME_MAX)
|
||||
#define TCP_CA_NAME_MAX 16
|
||||
#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;
|
||||
|
||||
/* Blocking signal to make sure that signal will be handled by main thread */
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
#ifdef SIGTERM
|
||||
sigaddset(&set, SIGTERM);
|
||||
#endif
|
||||
#ifdef SIGHUP
|
||||
sigaddset(&set, SIGHUP);
|
||||
#endif
|
||||
#ifdef SIGINT
|
||||
sigaddset(&set, SIGINT);
|
||||
#endif
|
||||
if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {
|
||||
i_errno = IEPTHREADSIGMASK;
|
||||
goto cleanup_and_fail;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
#if defined(HAVE_TCP_CONGESTION)
|
||||
int saved_errno;
|
||||
#endif /* HAVE_TCP_CONGESTION */
|
||||
struct iperf_stream *sp;
|
||||
|
||||
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;
|
||||
|
||||
#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;
|
||||
}
|
||||
if (test->congestion_used) {
|
||||
if (test->debug)
|
||||
printf("Overriding existing congestion algorithm: %s\n", test->congestion_used);
|
||||
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 */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (test->debug_level >= DEBUG_LEVEL_INFO) {
|
||||
iperf_printf(test, "Reading new State from the Server - current state is %d-%s\n", test->state, state_to_text(test->state));
|
||||
}
|
||||
|
||||
/*!!! 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 (test->debug_level >= DEBUG_LEVEL_INFO) {
|
||||
iperf_printf(test, "State change: client received and changed State to %d-%s\n", test->state, state_to_text(test->state));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
#if defined (HAVE_TCP_KEEPALIVE)
|
||||
// Set Control Connection TCP Keepalive (especially useful for long UDP test sessions)
|
||||
if (iperf_set_control_keepalive(test) < 0)
|
||||
return -1;
|
||||
#endif //HAVE_TCP_KEEPALIVE
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
#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 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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Close control socket */
|
||||
if (test->ctrl_sck >= 0)
|
||||
close(test->ctrl_sck);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* 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->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;
|
||||
}
|
||||
sp->thread_created = 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;
|
||||
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;
|
||||
if (sp->thread_created == 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);
|
||||
}
|
||||
sp->thread_created = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
int rc;
|
||||
sp->done = 1;
|
||||
if (sp->thread_created == 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);
|
||||
}
|
||||
sp->thread_created = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
if (sp->done) {
|
||||
continue;
|
||||
}
|
||||
sp->done = 1;
|
||||
int rc;
|
||||
if (sp->thread_created == 1) {
|
||||
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);
|
||||
}
|
||||
sp->thread_created = 0;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/* src/iperf_config.h.in. Generated for Android JNI build. */
|
||||
|
||||
/* Can bind to device via SO_BINDTODEVICE or IP_BOUND_IF. */
|
||||
/* #undef CAN_BIND_TO_DEVICE */
|
||||
|
||||
/* Define to 1 if you have the 'clock_gettime' function. */
|
||||
#define HAVE_CLOCK_GETTIME 1
|
||||
|
||||
/* Define to 1 if you have the 'clock_nanosleep' function. */
|
||||
/* #undef HAVE_CLOCK_NANOSLEEP */
|
||||
|
||||
/* Define to 1 if you have the 'cpuset_setaffinity' function. */
|
||||
/* #undef HAVE_CPUSET_SETAFFINITY */
|
||||
|
||||
/* Have CPU affinity support. */
|
||||
#define HAVE_CPU_AFFINITY 1
|
||||
|
||||
/* Define to 1 if you have the 'daemon' function. */
|
||||
/* #undef HAVE_DAEMON */
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#define HAVE_DLFCN_H 1
|
||||
|
||||
/* Have IP_MTU_DISCOVER/IP_DONTFRAG/IP_DONTFRAGMENT sockopt. */
|
||||
/* #undef HAVE_DONT_FRAGMENT */
|
||||
|
||||
/* Define to 1 if you have the <endian.h> header file. */
|
||||
#define HAVE_ENDIAN_H 1
|
||||
|
||||
/* Have IPv6 flowlabel support. */
|
||||
/* #undef HAVE_FLOWLABEL */
|
||||
|
||||
/* Define to 1 if you have the 'getline' function. */
|
||||
#define HAVE_GETLINE 1
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Have MPTCP protocol. */
|
||||
/* #undef HAVE_IPPROTO_MPTCP */
|
||||
|
||||
/* Have IP_BOUND_IF sockopt. */
|
||||
/* #undef HAVE_IP_BOUND_IF */
|
||||
|
||||
/* Have IP_DONTFRAG sockopt. */
|
||||
/* #undef HAVE_IP_DONTFRAG */
|
||||
|
||||
/* Have IP_DONTFRAGMENT sockopt. */
|
||||
/* #undef HAVE_IP_DONTFRAGMENT */
|
||||
|
||||
/* Have IP_MTU_DISCOVER sockopt. */
|
||||
/* #undef HAVE_IP_MTU_DISCOVER */
|
||||
|
||||
/* Define to 1 if you have the <linux/tcp.h> header file. */
|
||||
/* #undef HAVE_LINUX_TCP_H */
|
||||
|
||||
/* Have MSG_TRUNC recv option. */
|
||||
#define HAVE_MSG_TRUNC 1
|
||||
|
||||
/* Define to 1 if you have the 'nanosleep' function. */
|
||||
#define HAVE_NANOSLEEP 1
|
||||
|
||||
/* Define to 1 if you have the <netinet/sctp.h> header file. */
|
||||
/* #undef HAVE_NETINET_SCTP_H */
|
||||
|
||||
/* Define to 1 if you have the <poll.h> header file. */
|
||||
#define HAVE_POLL_H 1
|
||||
|
||||
/* Define if you have POSIX threads libraries and header files. */
|
||||
#define HAVE_PTHREAD 1
|
||||
|
||||
/* Have PTHREAD_PRIO_INHERIT. */
|
||||
/* #undef HAVE_PTHREAD_PRIO_INHERIT */
|
||||
|
||||
/* Define to 1 if you have the 'sched_setaffinity' function. */
|
||||
/* #undef HAVE_SCHED_SETAFFINITY */
|
||||
|
||||
/* Have SCTP support. */
|
||||
/* #undef HAVE_SCTP_H */
|
||||
|
||||
/* Define to 1 if you have the 'sendfile' function. */
|
||||
/* #undef HAVE_SENDFILE */
|
||||
|
||||
/* Define to 1 if you have the 'SetProcessAffinityMask' function. */
|
||||
/* #undef HAVE_SETPROCESSAFFINITYMASK */
|
||||
|
||||
/* Have SHUT_WR socket shutdown option. */
|
||||
#define HAVE_SOCKET_SHUTDOWN_SHUT_WR 1
|
||||
|
||||
/* Have SO_BINDTODEVICE sockopt. */
|
||||
/* #undef HAVE_SO_BINDTODEVICE */
|
||||
|
||||
/* Have SO_MAX_PACING_RATE sockopt. */
|
||||
/* #undef HAVE_SO_MAX_PACING_RATE */
|
||||
|
||||
/* OpenSSL Is Available */
|
||||
/* #undef HAVE_SSL */
|
||||
|
||||
/* Define to 1 if you have the <stdatomic.h> header file. */
|
||||
#define HAVE_STDATOMIC_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdio.h> header file. */
|
||||
#define HAVE_STDIO_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if the system has the type 'struct sctp_assoc_value'. */
|
||||
/* #undef HAVE_STRUCT_SCTP_ASSOC_VALUE */
|
||||
|
||||
/* Define to 1 if you have the <sys/endian.h> header file. */
|
||||
/* #undef HAVE_SYS_ENDIAN_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#define HAVE_SYS_SOCKET_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Have TCP_CONGESTION sockopt. */
|
||||
#define HAVE_TCP_CONGESTION 1
|
||||
|
||||
/* Have TCP_KEEPIDLE sockopt. */
|
||||
#define HAVE_TCP_KEEPALIVE 1
|
||||
|
||||
/* Have TCP_USER_TIMEOUT sockopt. */
|
||||
#define HAVE_TCP_USER_TIMEOUT 1
|
||||
|
||||
/* Have UDP_GRO sockopt. */
|
||||
/* #undef HAVE_UDP_GRO */
|
||||
|
||||
/* Have UDP_SEGMENT sockopt. */
|
||||
/* #undef HAVE_UDP_SEGMENT */
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to the sub-directory where libtool stores uninstalled libraries. */
|
||||
#define LT_OBJDIR ".libs/"
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "iperf"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT "iperf-devel@lists.es.net"
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "iperf"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "iperf 3.19"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "iperf"
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define PACKAGE_URL "http://iperf.fr/"
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "3.19"
|
||||
|
||||
/* Define to necessary symbol if this constant uses a non-standard name on your system. */
|
||||
#define PTHREAD_CREATE_JOINABLE 2
|
||||
|
||||
/* Define to 1 if all of the C89 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "3.19"
|
||||
|
||||
/* Define to empty if 'const' does not conform to ANSI C. */
|
||||
/* #undef const */
|
||||
@@ -0,0 +1,175 @@
|
||||
/* src/iperf_config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define to 1 if you have the 'clock_gettime' function. */
|
||||
#undef HAVE_CLOCK_GETTIME
|
||||
|
||||
/* Define to 1 if you have the 'clock_nanosleep' function. */
|
||||
#undef HAVE_CLOCK_NANOSLEEP
|
||||
|
||||
/* Define to 1 if you have the 'cpuset_setaffinity' function. */
|
||||
#undef HAVE_CPUSET_SETAFFINITY
|
||||
|
||||
/* Have CPU affinity support. */
|
||||
#undef HAVE_CPU_AFFINITY
|
||||
|
||||
/* Define to 1 if you have the 'daemon' function. */
|
||||
#undef HAVE_DAEMON
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#undef HAVE_DLFCN_H
|
||||
|
||||
/* Have IP_MTU_DISCOVER/IP_DONTFRAG/IP_DONTFRAGMENT sockopt. */
|
||||
#undef HAVE_DONT_FRAGMENT
|
||||
|
||||
/* Define to 1 if you have the <endian.h> header file. */
|
||||
#undef HAVE_ENDIAN_H
|
||||
|
||||
/* Have IPv6 flowlabel support. */
|
||||
#undef HAVE_FLOWLABEL
|
||||
|
||||
/* Define to 1 if you have the 'getline' function. */
|
||||
#undef HAVE_GETLINE
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Have MPTCP protocol. */
|
||||
#undef HAVE_IPPROTO_MPTCP
|
||||
|
||||
/* Have IP_DONTFRAG sockopt. */
|
||||
#undef HAVE_IP_DONTFRAG
|
||||
|
||||
/* Have IP_DONTFRAGMENT sockopt. */
|
||||
#undef HAVE_IP_DONTFRAGMENT
|
||||
|
||||
/* Have IP_MTU_DISCOVER sockopt. */
|
||||
#undef HAVE_IP_MTU_DISCOVER
|
||||
|
||||
/* Define to 1 if you have the <linux/tcp.h> header file. */
|
||||
#undef HAVE_LINUX_TCP_H
|
||||
|
||||
/* Have MSG_TRUNC recv option. */
|
||||
#undef HAVE_MSG_TRUNC
|
||||
|
||||
/* Define to 1 if you have the 'nanosleep' function. */
|
||||
#undef HAVE_NANOSLEEP
|
||||
|
||||
/* Define to 1 if you have the <netinet/sctp.h> header file. */
|
||||
#undef HAVE_NETINET_SCTP_H
|
||||
|
||||
/* Define to 1 if you have the <poll.h> header file. */
|
||||
#undef HAVE_POLL_H
|
||||
|
||||
/* Define if you have POSIX threads libraries and header files. */
|
||||
#undef HAVE_PTHREAD
|
||||
|
||||
/* Have PTHREAD_PRIO_INHERIT. */
|
||||
#undef HAVE_PTHREAD_PRIO_INHERIT
|
||||
|
||||
/* Define to 1 if you have the 'sched_setaffinity' function. */
|
||||
#undef HAVE_SCHED_SETAFFINITY
|
||||
|
||||
/* Have SCTP support. */
|
||||
#undef HAVE_SCTP_H
|
||||
|
||||
/* Define to 1 if you have the 'sendfile' function. */
|
||||
#undef HAVE_SENDFILE
|
||||
|
||||
/* Define to 1 if you have the 'SetProcessAffinityMask' function. */
|
||||
#undef HAVE_SETPROCESSAFFINITYMASK
|
||||
|
||||
/* Have SO_BINDTODEVICE sockopt. */
|
||||
#undef HAVE_SO_BINDTODEVICE
|
||||
|
||||
/* Have SO_MAX_PACING_RATE sockopt. */
|
||||
#undef HAVE_SO_MAX_PACING_RATE
|
||||
|
||||
/* OpenSSL Is Available */
|
||||
#undef HAVE_SSL
|
||||
|
||||
/* Define to 1 if you have the <stdatomic.h> header file. */
|
||||
#undef HAVE_STDATOMIC_H
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdio.h> header file. */
|
||||
#undef HAVE_STDIO_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if the system has the type 'struct sctp_assoc_value'. */
|
||||
#undef HAVE_STRUCT_SCTP_ASSOC_VALUE
|
||||
|
||||
/* Define to 1 if you have the <sys/endian.h> header file. */
|
||||
#undef HAVE_SYS_ENDIAN_H
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#undef HAVE_SYS_SOCKET_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Have TCP_CONGESTION sockopt. */
|
||||
#undef HAVE_TCP_CONGESTION
|
||||
|
||||
/* Have tcpi_snd_wnd field in tcp_info. */
|
||||
#undef HAVE_TCP_INFO_SND_WND
|
||||
|
||||
/* Have TCP_KEEPIDLE sockopt. */
|
||||
#undef HAVE_TCP_KEEPALIVE
|
||||
|
||||
/* Have TCP_USER_TIMEOUT sockopt. */
|
||||
#undef HAVE_TCP_USER_TIMEOUT
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to the sub-directory where libtool stores uninstalled libraries. */
|
||||
#undef LT_OBJDIR
|
||||
|
||||
/* Name of package */
|
||||
#undef PACKAGE
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#undef PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to necessary symbol if this constant uses a non-standard name on
|
||||
your system. */
|
||||
#undef PTHREAD_CREATE_JOINABLE
|
||||
|
||||
/* Define to 1 if all of the C89 standard headers exist (not just the ones
|
||||
required in a freestanding environment). This macro is provided for
|
||||
backward compatibility; new code need not use it. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Version number of package */
|
||||
#undef VERSION
|
||||
|
||||
/* Define to empty if 'const' does not conform to ANSI C. */
|
||||
#undef const
|
||||
@@ -0,0 +1,184 @@
|
||||
/* src/iperf_config.h.in. Generated for Android JNI build. */
|
||||
|
||||
/* Can bind to device via SO_BINDTODEVICE or IP_BOUND_IF. */
|
||||
/* #undef CAN_BIND_TO_DEVICE */
|
||||
|
||||
/* Define to 1 if you have the 'clock_gettime' function. */
|
||||
#define HAVE_CLOCK_GETTIME 1
|
||||
|
||||
/* Define to 1 if you have the 'clock_nanosleep' function. */
|
||||
/* #undef HAVE_CLOCK_NANOSLEEP */
|
||||
|
||||
/* Define to 1 if you have the 'cpuset_setaffinity' function. */
|
||||
/* #undef HAVE_CPUSET_SETAFFINITY */
|
||||
|
||||
/* Have CPU affinity support. */
|
||||
#define HAVE_CPU_AFFINITY 1
|
||||
|
||||
/* Define to 1 if you have the 'daemon' function. */
|
||||
/* #undef HAVE_DAEMON */
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#define HAVE_DLFCN_H 1
|
||||
|
||||
/* Have IP_MTU_DISCOVER/IP_DONTFRAG/IP_DONTFRAGMENT sockopt. */
|
||||
/* #undef HAVE_DONT_FRAGMENT */
|
||||
|
||||
/* Define to 1 if you have the <endian.h> header file. */
|
||||
#define HAVE_ENDIAN_H 1
|
||||
|
||||
/* Have IPv6 flowlabel support. */
|
||||
/* #undef HAVE_FLOWLABEL */
|
||||
|
||||
/* Define to 1 if you have the 'getline' function. */
|
||||
#define HAVE_GETLINE 1
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Have MPTCP protocol. */
|
||||
/* #undef HAVE_IPPROTO_MPTCP */
|
||||
|
||||
/* Have IP_BOUND_IF sockopt. */
|
||||
/* #undef HAVE_IP_BOUND_IF */
|
||||
|
||||
/* Have IP_DONTFRAG sockopt. */
|
||||
/* #undef HAVE_IP_DONTFRAG */
|
||||
|
||||
/* Have IP_DONTFRAGMENT sockopt. */
|
||||
/* #undef HAVE_IP_DONTFRAGMENT */
|
||||
|
||||
/* Have IP_MTU_DISCOVER sockopt. */
|
||||
/* #undef HAVE_IP_MTU_DISCOVER */
|
||||
|
||||
/* Define to 1 if you have the <linux/tcp.h> header file. */
|
||||
/* #undef HAVE_LINUX_TCP_H */
|
||||
|
||||
/* Have MSG_TRUNC recv option. */
|
||||
#define HAVE_MSG_TRUNC 1
|
||||
|
||||
/* Define to 1 if you have the 'nanosleep' function. */
|
||||
#define HAVE_NANOSLEEP 1
|
||||
|
||||
/* Define to 1 if you have the <netinet/sctp.h> header file. */
|
||||
/* #undef HAVE_NETINET_SCTP_H */
|
||||
|
||||
/* Define to 1 if you have the <poll.h> header file. */
|
||||
#define HAVE_POLL_H 1
|
||||
|
||||
/* Define if you have POSIX threads libraries and header files. */
|
||||
#define HAVE_PTHREAD 1
|
||||
|
||||
/* Have PTHREAD_PRIO_INHERIT. */
|
||||
/* #undef HAVE_PTHREAD_PRIO_INHERIT */
|
||||
|
||||
/* Define to 1 if you have the 'sched_setaffinity' function. */
|
||||
/* #undef HAVE_SCHED_SETAFFINITY */
|
||||
|
||||
/* Have SCTP support. */
|
||||
/* #undef HAVE_SCTP_H */
|
||||
|
||||
/* Define to 1 if you have the 'sendfile' function. */
|
||||
/* #undef HAVE_SENDFILE */
|
||||
|
||||
/* Define to 1 if you have the 'SetProcessAffinityMask' function. */
|
||||
/* #undef HAVE_SETPROCESSAFFINITYMASK */
|
||||
|
||||
/* Have SHUT_WR socket shutdown option. */
|
||||
#define HAVE_SOCKET_SHUTDOWN_SHUT_WR 1
|
||||
|
||||
/* Have SO_BINDTODEVICE sockopt. */
|
||||
/* #undef HAVE_SO_BINDTODEVICE */
|
||||
|
||||
/* Have SO_MAX_PACING_RATE sockopt. */
|
||||
/* #undef HAVE_SO_MAX_PACING_RATE */
|
||||
|
||||
/* OpenSSL Is Available */
|
||||
/* #undef HAVE_SSL */
|
||||
|
||||
/* Define to 1 if you have the <stdatomic.h> header file. */
|
||||
#define HAVE_STDATOMIC_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdio.h> header file. */
|
||||
#define HAVE_STDIO_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if the system has the type 'struct sctp_assoc_value'. */
|
||||
/* #undef HAVE_STRUCT_SCTP_ASSOC_VALUE */
|
||||
|
||||
/* Define to 1 if you have the <sys/endian.h> header file. */
|
||||
/* #undef HAVE_SYS_ENDIAN_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#define HAVE_SYS_SOCKET_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Have TCP_CONGESTION sockopt. */
|
||||
#define HAVE_TCP_CONGESTION 1
|
||||
|
||||
/* Have TCP_KEEPIDLE sockopt. */
|
||||
#define HAVE_TCP_KEEPALIVE 1
|
||||
|
||||
/* Have TCP_USER_TIMEOUT sockopt. */
|
||||
#define HAVE_TCP_USER_TIMEOUT 1
|
||||
|
||||
/* Have UDP_GRO sockopt. */
|
||||
/* #undef HAVE_UDP_GRO */
|
||||
|
||||
/* Have UDP_SEGMENT sockopt. */
|
||||
/* #undef HAVE_UDP_SEGMENT */
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to the sub-directory where libtool stores uninstalled libraries. */
|
||||
#define LT_OBJDIR ".libs/"
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "iperf"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT "iperf-devel@lists.es.net"
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "iperf"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "iperf 3.19"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "iperf"
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define PACKAGE_URL "http://iperf.fr/"
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "3.19"
|
||||
|
||||
/* Define to necessary symbol if this constant uses a non-standard name on your system. */
|
||||
#define PTHREAD_CREATE_JOINABLE 2
|
||||
|
||||
/* Define to 1 if all of the C89 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "3.19"
|
||||
|
||||
/* Define to empty if 'const' does not conform to ANSI C. */
|
||||
/* #undef const */
|
||||
@@ -0,0 +1,568 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2022, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include "iperf.h"
|
||||
#include "iperf_api.h"
|
||||
|
||||
int gerror;
|
||||
|
||||
char iperf_timestrerr[100];
|
||||
|
||||
/* Do a printf to stderr. */
|
||||
void
|
||||
iperf_err(struct iperf_test *test, const char *format, ...)
|
||||
{
|
||||
va_list argp;
|
||||
char str[1000];
|
||||
time_t now;
|
||||
struct tm *ltm = NULL;
|
||||
char *ct = NULL;
|
||||
|
||||
/* Timestamp if requested */
|
||||
if (test != NULL && test->timestamps) {
|
||||
time(&now);
|
||||
ltm = localtime(&now);
|
||||
strftime(iperf_timestrerr, sizeof(iperf_timestrerr), test->timestamp_format, ltm);
|
||||
ct = iperf_timestrerr;
|
||||
}
|
||||
|
||||
va_start(argp, format);
|
||||
vsnprintf(str, sizeof(str), format, argp);
|
||||
if (test != NULL && test->json_output && test->json_top != NULL)
|
||||
cJSON_AddStringToObject(test->json_top, "error", str);
|
||||
else {
|
||||
if (test != NULL && pthread_mutex_lock(&(test->print_mutex)) != 0) {
|
||||
perror("iperf_err: pthread_mutex_lock");
|
||||
}
|
||||
|
||||
if (test != NULL && test->outfile != NULL && test->outfile != stdout) {
|
||||
if (ct) {
|
||||
fprintf(test->outfile, "%s", ct);
|
||||
}
|
||||
fprintf(test->outfile, "iperf3: %s\n", str);
|
||||
}
|
||||
else {
|
||||
if (ct) {
|
||||
fprintf(stderr, "%s", ct);
|
||||
}
|
||||
fprintf(stderr, "iperf3: %s\n", str);
|
||||
}
|
||||
|
||||
if (test != NULL && pthread_mutex_unlock(&(test->print_mutex)) != 0) {
|
||||
perror("iperf_err: pthread_mutex_unlock");
|
||||
}
|
||||
|
||||
}
|
||||
va_end(argp);
|
||||
}
|
||||
|
||||
/* Do a printf to stderr or log file as appropriate, then exit(0). */
|
||||
void
|
||||
iperf_signormalexit(struct iperf_test *test, const char *format, ...)
|
||||
{
|
||||
va_list argp;
|
||||
|
||||
va_start(argp, format);
|
||||
iperf_exit(test, 0, format, argp);
|
||||
}
|
||||
|
||||
/* Do a printf to stderr or log file as appropriate, then exit(1). */
|
||||
void
|
||||
iperf_errexit(struct iperf_test *test, const char *format, ...)
|
||||
{
|
||||
va_list argp;
|
||||
|
||||
va_start(argp, format);
|
||||
iperf_exit(test, 1, format, argp);
|
||||
}
|
||||
|
||||
/* Do a printf to stderr or log file as appropriate, then exit. */
|
||||
void
|
||||
iperf_exit(struct iperf_test *test, int exit_code, const char *format, va_list argp)
|
||||
{
|
||||
char str[1000];
|
||||
time_t now;
|
||||
struct tm *ltm = NULL;
|
||||
char *ct = NULL;
|
||||
|
||||
/* Timestamp if requested */
|
||||
if (test != NULL && test->timestamps) {
|
||||
time(&now);
|
||||
ltm = localtime(&now);
|
||||
strftime(iperf_timestrerr, sizeof(iperf_timestrerr), iperf_get_test_timestamp_format(test), ltm);
|
||||
ct = iperf_timestrerr;
|
||||
}
|
||||
|
||||
vsnprintf(str, sizeof(str), format, argp);
|
||||
if (test != NULL && test->json_output) {
|
||||
if (test->json_top != NULL) {
|
||||
cJSON_AddStringToObject(test->json_top, "error", str);
|
||||
}
|
||||
iperf_json_finish(test);
|
||||
} else {
|
||||
if (test != NULL && pthread_mutex_lock(&(test->print_mutex)) != 0) {
|
||||
perror("iperf_errexit: pthread_mutex_lock");
|
||||
}
|
||||
|
||||
if (test && test->outfile && test->outfile != stdout) {
|
||||
if (ct) {
|
||||
fprintf(test->outfile, "%s", ct);
|
||||
}
|
||||
fprintf(test->outfile, "iperf3: %s\n", str);
|
||||
}
|
||||
else {
|
||||
if (ct) {
|
||||
fprintf(stderr, "%s", ct);
|
||||
}
|
||||
fprintf(stderr, "iperf3: %s\n", str);
|
||||
}
|
||||
|
||||
if (test != NULL && pthread_mutex_unlock(&(test->print_mutex)) != 0) {
|
||||
perror("iperf_errexit: pthread_mutex_unlock");
|
||||
}
|
||||
}
|
||||
|
||||
va_end(argp);
|
||||
if (test)
|
||||
iperf_delete_pidfile(test);
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
int i_errno;
|
||||
|
||||
char *
|
||||
iperf_strerror(int int_errno)
|
||||
{
|
||||
static char errstr[256];
|
||||
int len, perr, herr;
|
||||
perr = herr = 0;
|
||||
|
||||
len = sizeof(errstr);
|
||||
memset(errstr, 0, len);
|
||||
|
||||
switch (int_errno) {
|
||||
case IENONE:
|
||||
snprintf(errstr, len, "no error");
|
||||
break;
|
||||
case IESERVCLIENT:
|
||||
snprintf(errstr, len, "cannot be both server and client");
|
||||
break;
|
||||
case IENOROLE:
|
||||
snprintf(errstr, len, "must either be a client (-c) or server (-s)");
|
||||
break;
|
||||
case IESERVERONLY:
|
||||
snprintf(errstr, len, "some option you are trying to set is server only");
|
||||
break;
|
||||
case IECLIENTONLY:
|
||||
snprintf(errstr, len, "some option you are trying to set is client only");
|
||||
break;
|
||||
case IEDURATION:
|
||||
snprintf(errstr, len, "test duration valid values are 0 to %d seconds", MAX_TIME);
|
||||
break;
|
||||
case IENUMSTREAMS:
|
||||
snprintf(errstr, len, "number of parallel streams too large (maximum = %d)", MAX_STREAMS);
|
||||
break;
|
||||
case IEBLOCKSIZE:
|
||||
snprintf(errstr, len, "block size too large (maximum = %d bytes)", MAX_BLOCKSIZE);
|
||||
break;
|
||||
case IEBUFSIZE:
|
||||
snprintf(errstr, len, "socket buffer size too large (maximum = %d bytes)", MAX_TCP_BUFFER);
|
||||
break;
|
||||
case IEINTERVAL:
|
||||
snprintf(errstr, len, "invalid report interval (min = %g, max = %g seconds)", MIN_INTERVAL, MAX_INTERVAL);
|
||||
break;
|
||||
case IEBIND: /* UNUSED */
|
||||
snprintf(errstr, len, "--bind must be specified to use --cport");
|
||||
break;
|
||||
case IEUDPBLOCKSIZE:
|
||||
snprintf(errstr, len, "block size invalid (minimum = %d bytes, maximum = %d bytes)", MIN_UDP_BLOCKSIZE, MAX_UDP_BLOCKSIZE);
|
||||
break;
|
||||
case IEBADTOS:
|
||||
snprintf(errstr, len, "bad TOS value (must be between 0 and 255 inclusive)");
|
||||
break;
|
||||
case IESETCLIENTAUTH:
|
||||
snprintf(errstr, len, "you must specify a username, password, and path to a valid RSA public key");
|
||||
break;
|
||||
case IESETSERVERAUTH:
|
||||
snprintf(errstr, len, "you must specify a path to a valid RSA private key and a user credential file");
|
||||
break;
|
||||
case IESERVERAUTHUSERS:
|
||||
snprintf(errstr, len, "cannot access authorized users file");
|
||||
break;
|
||||
case IEBADFORMAT:
|
||||
snprintf(errstr, len, "bad format specifier (valid formats are in the set [kmgtKMGT])");
|
||||
break;
|
||||
case IEBADPORT:
|
||||
snprintf(errstr, len, "port number must be between 1 and 65535 inclusive");
|
||||
break;
|
||||
case IEMSS:
|
||||
snprintf(errstr, len, "TCP MSS too large (maximum = %d bytes)", MAX_MSS);
|
||||
break;
|
||||
case IENOSENDFILE:
|
||||
snprintf(errstr, len, "this OS does not support sendfile");
|
||||
break;
|
||||
case IEOMIT:
|
||||
snprintf(errstr, len, "bogus value for --omit (maximum = %d seconds)", MAX_OMIT_TIME);
|
||||
break;
|
||||
case IEUNIMP:
|
||||
snprintf(errstr, len, "an option you are trying to set is not implemented yet");
|
||||
break;
|
||||
case IEFILE:
|
||||
snprintf(errstr, len, "unable to open -F file");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEBURST:
|
||||
snprintf(errstr, len, "invalid burst count (maximum = %d)", MAX_BURST);
|
||||
break;
|
||||
case IEENDCONDITIONS:
|
||||
snprintf(errstr, len, "only one test end condition (-t, -n, -k) may be specified");
|
||||
break;
|
||||
case IELOGFILE:
|
||||
snprintf(errstr, len, "unable to open log file");
|
||||
perr = 1;
|
||||
break;
|
||||
case IENOSCTP:
|
||||
snprintf(errstr, len, "no SCTP support available");
|
||||
break;
|
||||
case IENEWTEST:
|
||||
snprintf(errstr, len, "unable to create a new test");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEINITTEST:
|
||||
snprintf(errstr, len, "test initialization failed");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEAUTHTEST:
|
||||
snprintf(errstr, len, "test authorization failed");
|
||||
break;
|
||||
case IELISTEN:
|
||||
snprintf(errstr, len, "unable to start listener for connections");
|
||||
herr = 1;
|
||||
perr = 1;
|
||||
break;
|
||||
case IECONNECT:
|
||||
snprintf(errstr, len, "unable to connect to server - server may have stopped running or use a different port, firewall issue, etc.");
|
||||
perr = 1;
|
||||
herr = 1;
|
||||
break;
|
||||
case IEACCEPT:
|
||||
snprintf(errstr, len, "unable to accept connection from client");
|
||||
herr = 1;
|
||||
perr = 1;
|
||||
break;
|
||||
case IESENDCOOKIE:
|
||||
snprintf(errstr, len, "unable to send cookie to server");
|
||||
perr = 1;
|
||||
break;
|
||||
case IERECVCOOKIE:
|
||||
snprintf(errstr, len, "unable to receive cookie at server");
|
||||
perr = 1;
|
||||
break;
|
||||
case IECTRLWRITE:
|
||||
snprintf(errstr, len, "unable to write to the control socket");
|
||||
perr = 1;
|
||||
break;
|
||||
case IECTRLREAD:
|
||||
snprintf(errstr, len, "unable to read from the control socket");
|
||||
perr = 1;
|
||||
break;
|
||||
case IECTRLCLOSE:
|
||||
snprintf(errstr, len, "control socket has closed unexpectedly");
|
||||
break;
|
||||
case IEMESSAGE:
|
||||
snprintf(errstr, len, "received an unknown control message (ensure other side is iperf3 and not iperf)");
|
||||
break;
|
||||
case IESENDMESSAGE:
|
||||
snprintf(errstr, len, "unable to send control message - port may not be available, the other side may have stopped running, etc.");
|
||||
perr = 1;
|
||||
break;
|
||||
case IERECVMESSAGE:
|
||||
snprintf(errstr, len, "unable to receive control message - port may not be available, the other side may have stopped running, etc.");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESENDPARAMS:
|
||||
snprintf(errstr, len, "unable to send parameters to server");
|
||||
perr = 1;
|
||||
break;
|
||||
case IERECVPARAMS:
|
||||
snprintf(errstr, len, "unable to receive parameters from client");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEPACKAGERESULTS:
|
||||
snprintf(errstr, len, "unable to package results");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESENDRESULTS:
|
||||
snprintf(errstr, len, "unable to send results");
|
||||
perr = 1;
|
||||
break;
|
||||
case IERECVRESULTS:
|
||||
snprintf(errstr, len, "unable to receive results");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESELECT:
|
||||
snprintf(errstr, len, "select failed");
|
||||
perr = 1;
|
||||
break;
|
||||
case IECLIENTTERM:
|
||||
snprintf(errstr, len, "the client has terminated");
|
||||
break;
|
||||
case IESERVERTERM:
|
||||
snprintf(errstr, len, "the server has terminated");
|
||||
break;
|
||||
case IEACCESSDENIED:
|
||||
snprintf(errstr, len, "the server is busy running a test. try again later");
|
||||
break;
|
||||
case IESETNODELAY:
|
||||
snprintf(errstr, len, "unable to set TCP/SCTP NODELAY");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETMSS:
|
||||
snprintf(errstr, len, "unable to set TCP/SCTP MSS");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETBUF:
|
||||
snprintf(errstr, len, "unable to set socket buffer size");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETTOS:
|
||||
snprintf(errstr, len, "unable to set IP TOS");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETCOS:
|
||||
snprintf(errstr, len, "unable to set IPv6 traffic class");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETFLOW:
|
||||
snprintf(errstr, len, "unable to set IPv6 flow label");
|
||||
break;
|
||||
case IEREUSEADDR:
|
||||
snprintf(errstr, len, "unable to reuse address on socket");
|
||||
perr = 1;
|
||||
break;
|
||||
case IENONBLOCKING:
|
||||
snprintf(errstr, len, "unable to set socket to non-blocking");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETWINDOWSIZE:
|
||||
snprintf(errstr, len, "unable to set socket window size");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEPROTOCOL:
|
||||
snprintf(errstr, len, "protocol does not exist");
|
||||
break;
|
||||
case IEAFFINITY:
|
||||
snprintf(errstr, len, "unable to set CPU affinity");
|
||||
perr = 1;
|
||||
break;
|
||||
case IERCVTIMEOUT:
|
||||
snprintf(errstr, len, "receive timeout value is incorrect or not in range");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESNDTIMEOUT:
|
||||
snprintf(errstr, len, "send timeout value is incorrect or not in range");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEUDPFILETRANSFER:
|
||||
snprintf(errstr, len, "cannot transfer file using UDP");
|
||||
break;
|
||||
case IERVRSONLYRCVTIMEOUT:
|
||||
snprintf(errstr, len, "client receive timeout is valid only in receiving mode");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEDAEMON:
|
||||
snprintf(errstr, len, "unable to become a daemon");
|
||||
perr = 1;
|
||||
break;
|
||||
case IECREATESTREAM:
|
||||
snprintf(errstr, len, "unable to create a new stream");
|
||||
herr = 1;
|
||||
perr = 1;
|
||||
break;
|
||||
case IEINITSTREAM:
|
||||
snprintf(errstr, len, "unable to initialize stream");
|
||||
herr = 1;
|
||||
perr = 1;
|
||||
break;
|
||||
case IESTREAMLISTEN:
|
||||
snprintf(errstr, len, "unable to start stream listener");
|
||||
herr = 1;
|
||||
perr = 1;
|
||||
break;
|
||||
case IESTREAMCONNECT:
|
||||
snprintf(errstr, len, "unable to connect stream");
|
||||
herr = 1;
|
||||
perr = 1;
|
||||
break;
|
||||
case IESTREAMACCEPT:
|
||||
snprintf(errstr, len, "unable to accept stream connection");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESTREAMWRITE:
|
||||
snprintf(errstr, len, "unable to write to stream socket");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESTREAMREAD:
|
||||
snprintf(errstr, len, "unable to read from stream socket");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESTREAMCLOSE:
|
||||
snprintf(errstr, len, "stream socket has closed unexpectedly");
|
||||
break;
|
||||
case IESTREAMID:
|
||||
snprintf(errstr, len, "stream has an invalid id");
|
||||
break;
|
||||
case IENEWTIMER:
|
||||
snprintf(errstr, len, "unable to create new timer");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEUPDATETIMER:
|
||||
snprintf(errstr, len, "unable to update timer");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETCONGESTION:
|
||||
snprintf(errstr, len, "unable to set TCP_CONGESTION: "
|
||||
"Supplied congestion control algorithm not supported on this host");
|
||||
break;
|
||||
case IEPIDFILE:
|
||||
snprintf(errstr, len, "unable to write PID file");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEV6ONLY:
|
||||
snprintf(errstr, len, "Unable to set/reset IPV6_V6ONLY");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETSCTPDISABLEFRAG:
|
||||
snprintf(errstr, len, "unable to set SCTP_DISABLE_FRAGMENTS");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETSCTPNSTREAM:
|
||||
snprintf(errstr, len, "unable to set SCTP_INIT num of SCTP streams\n");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETPACING:
|
||||
snprintf(errstr, len, "unable to set socket pacing");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETBUF2:
|
||||
snprintf(errstr, len, "socket buffer size not set correctly");
|
||||
break;
|
||||
case IEREVERSEBIDIR:
|
||||
snprintf(errstr, len, "cannot be both reverse and bidirectional");
|
||||
break;
|
||||
case IETOTALRATE:
|
||||
snprintf(errstr, len, "total required bandwidth is larger than server limit");
|
||||
break;
|
||||
case IESKEWTHRESHOLD:
|
||||
snprintf(errstr, len, "skew threshold must be a positive number");
|
||||
break;
|
||||
case IEIDLETIMEOUT:
|
||||
snprintf(errstr, len, "idle timeout parameter is not positive or larger than allowed limit");
|
||||
break;
|
||||
case IEBINDDEV:
|
||||
snprintf(errstr, len, "Unable to bind-to-device (check perror, maybe permissions?)");
|
||||
break;
|
||||
case IEBINDDEVNOSUPPORT:
|
||||
snprintf(errstr, len, "`<ip>%%<dev>` is not supported as system does not support bind to device");
|
||||
break;
|
||||
case IEHOSTDEV:
|
||||
snprintf(errstr, len, "host device name (ip%%<dev>) is supported (and required) only for IPv6 link-local address");
|
||||
break;
|
||||
case IENOMSG:
|
||||
snprintf(errstr, len, "idle timeout for receiving data");
|
||||
break;
|
||||
case IESETDONTFRAGMENT:
|
||||
snprintf(errstr, len, "unable to set IP Do-Not-Fragment flag");
|
||||
break;
|
||||
case IESETUSERTIMEOUT:
|
||||
snprintf(errstr, len, "unable to set TCP USER_TIMEOUT");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEPTHREADCREATE:
|
||||
snprintf(errstr, len, "unable to create thread");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEPTHREADCANCEL:
|
||||
snprintf(errstr, len, "unable to cancel thread");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEPTHREADJOIN:
|
||||
snprintf(errstr, len, "unable to join thread");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEPTHREADATTRINIT:
|
||||
snprintf(errstr, len, "unable to create thread attributes");
|
||||
perr = 1;
|
||||
break;
|
||||
case IEPTHREADSIGMASK:
|
||||
snprintf(errstr, len, "unable to change mask of blocked signals");
|
||||
break;
|
||||
case IEPTHREADATTRDESTROY:
|
||||
snprintf(errstr, len, "unable to destroy thread attributes");
|
||||
case IECNTLKA:
|
||||
snprintf(errstr, len, "control connection Keepalive period should be larger than the full retry period (interval * count)");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETCNTLKA:
|
||||
snprintf(errstr, len, "unable to set socket keepalive (SO_KEEPALIVE) option");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETCNTLKAKEEPIDLE:
|
||||
snprintf(errstr, len, "unable to set socket keepalive TCP period (TCP_KEEPIDLE) option");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETCNTLKAINTERVAL:
|
||||
snprintf(errstr, len, "unable to set/get socket keepalive TCP retry interval (TCP_KEEPINTVL) option");
|
||||
perr = 1;
|
||||
break;
|
||||
case IESETCNTLKACOUNT:
|
||||
snprintf(errstr, len, "unable to set/get socket keepalive TCP number of retries (TCP_KEEPCNT) option");
|
||||
perr = 1;
|
||||
break;
|
||||
default:
|
||||
snprintf(errstr, len, "int_errno=%d", int_errno);
|
||||
perr = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Append the result of strerror() or gai_strerror() if appropriate */
|
||||
if (herr || perr)
|
||||
strncat(errstr, ": ", len - strlen(errstr) - 1);
|
||||
if (errno && perr)
|
||||
strncat(errstr, strerror(errno), len - strlen(errstr) - 1);
|
||||
else if (herr && gerror) {
|
||||
strncat(errstr, gai_strerror(gerror), len - strlen(errstr) - 1);
|
||||
gerror = 0;
|
||||
}
|
||||
|
||||
return errstr;
|
||||
}
|
||||
@@ -0,0 +1,574 @@
|
||||
/*---------------------------------------------------------------
|
||||
* iperf, Copyright (c) 2014-2022, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*
|
||||
* Based on code that is:
|
||||
*
|
||||
* Copyright (c) 1999,2000,2001,2002,2003
|
||||
* The Board of Trustees of the University of Illinois
|
||||
* All Rights Reserved.
|
||||
*---------------------------------------------------------------
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software (Iperf) and associated
|
||||
* documentation files (the "Software"), to deal in the Software
|
||||
* without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
*
|
||||
* Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and
|
||||
* the following disclaimers.
|
||||
*
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimers in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
*
|
||||
* Neither the names of the University of Illinois, NCSA,
|
||||
* nor the names of its contributors may be used to endorse
|
||||
* or promote products derived from this Software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ________________________________________________________________
|
||||
* National Laboratory for Applied Network Research
|
||||
* National Center for Supercomputing Applications
|
||||
* University of Illinois at Urbana-Champaign
|
||||
* http://www.ncsa.uiuc.edu
|
||||
* ________________________________________________________________
|
||||
*
|
||||
* Locale.c
|
||||
* by Ajay Tirumala <tirumala@ncsa.uiuc.edu>
|
||||
* & Mark Gates <mgates@nlanr.net>
|
||||
* -------------------------------------------------------------------
|
||||
* Strings and other stuff that is locale specific.
|
||||
* ------------------------------------------------------------------- */
|
||||
#include "iperf_config.h"
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* usage
|
||||
* ------------------------------------------------------------------- */
|
||||
|
||||
const char usage_shortstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
|
||||
"Try `iperf3 --help' for more information.\n";
|
||||
|
||||
const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
|
||||
" iperf3 [-h|--help] [-v|--version]\n\n"
|
||||
"Server or Client:\n"
|
||||
" -p, --port # server port to listen on/connect to\n"
|
||||
" -f, --format [kmgtKMGT] format to report: Kbits, Mbits, Gbits, Tbits\n"
|
||||
" -i, --interval # seconds between periodic throughput reports\n"
|
||||
" -I, --pidfile file write PID file\n"
|
||||
" -F, --file name xmit/recv the specified file\n"
|
||||
#if defined(HAVE_CPU_AFFINITY)
|
||||
" -A, --affinity n[,m] set CPU affinity core number to n (the core the process will use)\n"
|
||||
" (optional Client only m - the Server's core number for this test)\n"
|
||||
#endif /* HAVE_CPU_AFFINITY */
|
||||
#if defined(HAVE_SO_BINDTODEVICE)
|
||||
" -B, --bind <host>[%%<dev>] bind to the interface associated with the address <host>\n"
|
||||
" (optional <dev> equivalent to `--bind-dev <dev>`)\n"
|
||||
" --bind-dev <dev> bind to the network interface with SO_BINDTODEVICE\n"
|
||||
#else /* HAVE_SO_BINDTODEVICE */
|
||||
" -B, --bind <host> bind to the interface associated with the address <host>\n"
|
||||
#endif /* HAVE_SO_BINDTODEVICE */
|
||||
" -V, --verbose more detailed output\n"
|
||||
" -J, --json output in JSON format\n"
|
||||
" --json-stream output in line-delimited JSON format\n"
|
||||
" --logfile f send output to a log file\n"
|
||||
" --forceflush force flushing output at every interval\n"
|
||||
" --timestamps<=format> emit a timestamp at the start of each output line\n"
|
||||
" (optional \"=\" and format string as per strftime(3))\n"
|
||||
|
||||
" --rcv-timeout # idle timeout for receiving data (default %d ms)\n"
|
||||
#if defined(HAVE_TCP_USER_TIMEOUT)
|
||||
" --snd-timeout # timeout for unacknowledged TCP data\n"
|
||||
" (in ms, default is system settings)\n"
|
||||
#endif /* HAVE_TCP_USER_TIMEOUT */
|
||||
#if defined(HAVE_TCP_KEEPALIVE)
|
||||
" --cntl-ka[=#/#/#] use control connection TCP keepalive - KEEPIDLE/KEEPINTV/KEEPCNT\n"
|
||||
" each value is optional with system settings default\n"
|
||||
#endif //HAVE_TCP_KEEPALIVE
|
||||
#if defined(HAVE_IPPROTO_MPTCP)
|
||||
" -m, --mptcp use MPTCP rather than plain TCP\n"
|
||||
#endif
|
||||
" -d, --debug[=#] emit debugging output\n"
|
||||
" (optional optional \"=\" and debug level: 1-4. Default is 4 - all messages)\n"
|
||||
" -v, --version show version information and quit\n"
|
||||
" -h, --help show this message and quit\n"
|
||||
"Server specific:\n"
|
||||
" -s, --server run in server mode\n"
|
||||
" -D, --daemon run the server as a daemon\n"
|
||||
" -1, --one-off handle one client connection then exit\n"
|
||||
" --server-bitrate-limit #[KMG][/#] server's total bit rate limit (default 0 = no limit)\n"
|
||||
" (optional slash and number of secs interval for averaging\n"
|
||||
" total data rate. Default is 5 seconds)\n"
|
||||
" --idle-timeout # restart idle server after # seconds in case it\n"
|
||||
" got stuck (default - no timeout)\n"
|
||||
#if defined(HAVE_SSL)
|
||||
" --rsa-private-key-path path to the RSA private key used to decrypt\n"
|
||||
" authentication credentials\n"
|
||||
" --authorized-users-path path to the configuration file containing user\n"
|
||||
" credentials\n"
|
||||
" --time-skew-threshold time skew threshold (in seconds) between the server\n"
|
||||
" and client during the authentication process\n"
|
||||
" --use-pkcs1-padding use pkcs1 padding at your own risk\n"
|
||||
#endif //HAVE_SSL
|
||||
"Client specific:\n"
|
||||
" -c, --client <host>[%%<dev>] run in client mode, connecting to <host>\n"
|
||||
" (option <dev> equivalent to `--bind-dev <dev>`)\n"
|
||||
#if defined(HAVE_SCTP_H)
|
||||
" --sctp use SCTP rather than TCP\n"
|
||||
" -X, --xbind <name> bind SCTP association to links\n"
|
||||
" --nstreams # number of SCTP streams\n"
|
||||
#endif /* HAVE_SCTP_H */
|
||||
" -u, --udp use UDP rather than TCP\n"
|
||||
" --connect-timeout # timeout for control connection setup (ms)\n"
|
||||
" -b, --bitrate #[KMG][/#] target bitrate in bits/sec (0 for unlimited)\n"
|
||||
" (default %d Mbit/sec for UDP, unlimited for TCP)\n"
|
||||
" (optional slash and packet count for burst mode)\n"
|
||||
" --pacing-timer #[KMG] set the Server timing for pacing, in microseconds (default %d)\n"
|
||||
" (deprecated - for servers using older versions ackward compatibility)\n"
|
||||
#if defined(HAVE_SO_MAX_PACING_RATE)
|
||||
" --fq-rate #[KMG] enable fair-queuing based socket pacing in\n"
|
||||
" bits/sec (Linux only)\n"
|
||||
#endif
|
||||
" -t, --time # time in seconds to transmit for (default %d secs)\n"
|
||||
" -n, --bytes #[KMG] transmit until the end of the interval when the client sent or received\n"
|
||||
" (per direction) at least this number of bytes (instead of -t or -k)\n"
|
||||
" -k, --blockcount #[KMG] transmit until the end of the interval when the client sent or received\n"
|
||||
" (per direction) at least this number of blocks (instead of -t or -n)\n"
|
||||
" -l, --length #[KMG] length of buffer to read or write\n"
|
||||
" (default %d KB for TCP, dynamic or %d for UDP)\n"
|
||||
" --cport <port> bind to a specific client port (TCP and UDP, default: ephemeral port)\n"
|
||||
" -P, --parallel # number of parallel client streams to run\n"
|
||||
" -R, --reverse run in reverse mode (server sends, client receives)\n"
|
||||
" --bidir run in bidirectional mode.\n"
|
||||
" Client and server send and receive data.\n"
|
||||
" -w, --window #[KMG] set send/receive socket buffer sizes\n"
|
||||
" (indirectly sets TCP window size)\n"
|
||||
|
||||
#if defined(HAVE_TCP_CONGESTION)
|
||||
" -C, --congestion <algo> set TCP congestion control algorithm (Linux and FreeBSD only)\n"
|
||||
#endif /* HAVE_TCP_CONGESTION */
|
||||
" -M, --set-mss # set TCP/SCTP maximum segment size (MTU - 40 bytes)\n"
|
||||
" -N, --no-delay set TCP/SCTP no delay, disabling Nagle's Algorithm\n"
|
||||
" -4, --version4 only use IPv4\n"
|
||||
" -6, --version6 only use IPv6\n"
|
||||
" -S, --tos N set the IP type of service, 0-255.\n"
|
||||
" The usual prefixes for octal and hex can be used,\n"
|
||||
" i.e. 52, 064 and 0x34 all specify the same value.\n"
|
||||
|
||||
" --dscp N or --dscp val set the IP dscp value, either 0-63 or symbolic.\n"
|
||||
" Numeric values can be specified in decimal,\n"
|
||||
" octal and hex (see --tos above).\n"
|
||||
#if defined(HAVE_FLOWLABEL)
|
||||
" -L, --flowlabel N set the IPv6 flow label (only supported on Linux)\n"
|
||||
#endif /* HAVE_FLOWLABEL */
|
||||
" -Z, --zerocopy use a 'zero copy' method of sending data\n"
|
||||
#if defined(HAVE_MSG_TRUNC)
|
||||
" --skip-rx-copy ignore received messages using MSG_TRUNC option\n"
|
||||
#endif /* HAVE_MSG_TRUNC */
|
||||
" -O, --omit N perform pre-test for N seconds and omit the pre-test statistics\n"
|
||||
" -T, --title str prefix every output line with this string\n"
|
||||
" --extra-data str data string to include in client and server JSON\n"
|
||||
" --get-server-output get results from server\n"
|
||||
" --udp-counters-64bit use 64-bit counters in UDP test packets\n"
|
||||
" --repeating-payload use repeating pattern in payload, instead of\n"
|
||||
" randomized payload (like in iperf2)\n"
|
||||
#if defined(HAVE_DONT_FRAGMENT)
|
||||
" --dont-fragment set IPv4 Don't Fragment flag\n"
|
||||
#endif /* HAVE_DONT_FRAGMENT */
|
||||
#if defined(HAVE_SSL)
|
||||
" --username username for authentication\n"
|
||||
" --rsa-public-key-path path to the RSA public key used to encrypt\n"
|
||||
" authentication credentials\n"
|
||||
#endif //HAVESSL
|
||||
|
||||
#ifdef NOT_YET_SUPPORTED /* still working on these */
|
||||
#endif
|
||||
|
||||
"\n"
|
||||
"[KMG] indicates options that support a K/M/G suffix for kilo-, mega-, or giga-\n"
|
||||
"\n"
|
||||
#ifdef PACKAGE_URL
|
||||
"iperf3 homepage at: " PACKAGE_URL "\n"
|
||||
#endif /* PACKAGE_URL */
|
||||
#ifdef PACKAGE_BUGREPORT
|
||||
"Report bugs to: " PACKAGE_BUGREPORT "\n"
|
||||
#endif /* PACKAGE_BUGREPORT */
|
||||
;
|
||||
|
||||
#ifdef OBSOLETE /* from old iperf: no longer supported. Add some of these back someday */
|
||||
"-d, --dualtest Do a bidirectional test simultaneously\n"
|
||||
"-L, --listenport # port to receive bidirectional tests back on\n"
|
||||
"-I, --stdin input the data to be transmitted from stdin\n"
|
||||
"-F, --fileinput <name> input the data to be transmitted from a file\n"
|
||||
"-r, --tradeoff Do a bidirectional test individually\n"
|
||||
"-T, --ttl # time-to-live, for multicast (default 1)\n"
|
||||
"-x, --reportexclude [CDMSV] exclude C(connection) D(data) M(multicast) S(settings) V(server) reports\n"
|
||||
"-y, --reportstyle C report as a Comma-Separated Values\n"
|
||||
#endif
|
||||
|
||||
const char version[] = PACKAGE_STRING;
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* settings
|
||||
* ------------------------------------------------------------------- */
|
||||
|
||||
const char seperator_line[] =
|
||||
"------------------------------------------------------------\n";
|
||||
|
||||
const char server_port[] =
|
||||
"Server listening on %s port %d\n";
|
||||
|
||||
const char client_port[] =
|
||||
"Client connecting to %s, %s port %d\n";
|
||||
|
||||
const char bind_address[] =
|
||||
"Binding to local address %s\n";
|
||||
|
||||
const char bind_dev[] =
|
||||
"Binding to local network device %s\n";
|
||||
|
||||
const char bind_port[] =
|
||||
"Binding to local port %s\n";
|
||||
|
||||
const char multicast_ttl[] =
|
||||
"Setting multicast TTL to %d\n";
|
||||
|
||||
const char join_multicast[] =
|
||||
"Joining multicast group %s\n";
|
||||
|
||||
const char client_datagram_size[] =
|
||||
"Sending %d byte datagrams\n";
|
||||
|
||||
const char server_datagram_size[] =
|
||||
"Receiving %d byte datagrams\n";
|
||||
|
||||
const char tcp_window_size[] =
|
||||
"TCP window size";
|
||||
|
||||
const char udp_buffer_size[] =
|
||||
"UDP buffer size";
|
||||
|
||||
const char window_default[] =
|
||||
"(default)";
|
||||
|
||||
const char wait_server_threads[] =
|
||||
"Waiting for server threads to complete. Interrupt again to force quit.\n";
|
||||
|
||||
const char test_start_time[] =
|
||||
"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %d second test, tos %d\n";
|
||||
|
||||
const char test_start_bytes[] =
|
||||
"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %"PRIuFAST64" bytes to send, tos %d\n";
|
||||
|
||||
const char test_start_blocks[] =
|
||||
"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %"PRIuFAST64" bytes to send, tos %d\n";
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* reports
|
||||
* ------------------------------------------------------------------- */
|
||||
|
||||
const char report_time[] =
|
||||
"Time: %s\n";
|
||||
|
||||
const char report_connecting[] =
|
||||
"Connecting to host %s, port %d\n";
|
||||
|
||||
const char report_authentication_succeeded[] =
|
||||
"Authentication succeeded for user '%s' ts %" PRIu64 "\n";
|
||||
|
||||
const char report_authentication_failed[] =
|
||||
"Authentication failed with return code %d for user '%s' ts %" PRIu64 "\n";
|
||||
|
||||
const char report_reverse[] =
|
||||
"Reverse mode, remote host %s is sending\n";
|
||||
|
||||
const char report_accepted[] =
|
||||
"Accepted connection from %s, port %d\n";
|
||||
|
||||
const char report_cookie[] =
|
||||
" Cookie: %s\n";
|
||||
|
||||
const char report_connected[] =
|
||||
"[%3d] local %s port %d connected to %s port %d\n";
|
||||
|
||||
const char report_window[] =
|
||||
"TCP window size: %s\n";
|
||||
|
||||
const char report_autotune[] =
|
||||
"Using TCP Autotuning\n";
|
||||
|
||||
const char report_omit_done[] =
|
||||
"Finished omit period, starting real test\n";
|
||||
|
||||
const char report_diskfile[] =
|
||||
" Sent %s / %s (%d%%) of %s\n";
|
||||
|
||||
const char report_done[] =
|
||||
"iperf Done.\n";
|
||||
|
||||
const char report_read_lengths[] =
|
||||
"[%3d] Read lengths occurring in more than 5%% of reads:\n";
|
||||
|
||||
const char report_read_length_times[] =
|
||||
"[%3d] %5d bytes read %5d times (%.3g%%)\n";
|
||||
|
||||
const char report_bw_header[] =
|
||||
"[ ID] Interval Transfer Bitrate\n";
|
||||
|
||||
const char report_bw_header_bidir[] =
|
||||
"[ ID][Role] Interval Transfer Bitrate\n";
|
||||
|
||||
const char report_bw_retrans_header[] =
|
||||
"[ ID] Interval Transfer Bitrate Retr\n";
|
||||
|
||||
const char report_bw_retrans_header_bidir[] =
|
||||
"[ ID][Role] Interval Transfer Bitrate Retr\n";
|
||||
|
||||
const char report_bw_retrans_cwnd_header[] =
|
||||
"[ ID] Interval Transfer Bitrate Retr Cwnd\n";
|
||||
|
||||
const char report_bw_retrans_cwnd_header_bidir[] =
|
||||
"[ ID][Role] Interval Transfer Bitrate Retr Cwnd\n";
|
||||
|
||||
const char report_bw_udp_header[] =
|
||||
"[ ID] Interval Transfer Bitrate Jitter Lost/Total Datagrams\n";
|
||||
|
||||
const char report_bw_udp_header_bidir[] =
|
||||
"[ ID][Role] Interval Transfer Bitrate Jitter Lost/Total Datagrams\n";
|
||||
|
||||
const char report_bw_udp_sender_header[] =
|
||||
"[ ID] Interval Transfer Bitrate Total Datagrams\n";
|
||||
|
||||
const char report_bw_udp_sender_header_bidir[] =
|
||||
"[ ID][Role] Interval Transfer Bitrate Total Datagrams\n";
|
||||
|
||||
const char report_bw_format[] =
|
||||
"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %s\n";
|
||||
|
||||
const char report_bw_retrans_format[] =
|
||||
"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %3ld %s\n";
|
||||
|
||||
const char report_bw_retrans_cwnd_format[] =
|
||||
"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %3ld %ss %s\n";
|
||||
|
||||
const char report_bw_udp_format[] =
|
||||
"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms %" PRId64 "/%" PRId64 " (%.2g%%) %s\n";
|
||||
|
||||
const char report_bw_udp_format_no_omitted_error[] =
|
||||
"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms Unknown/%" PRId64 " %s\n";
|
||||
|
||||
const char report_bw_udp_sender_format[] =
|
||||
"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %s %" PRId64 " %s\n";
|
||||
|
||||
const char report_summary[] =
|
||||
"Test Complete. Summary Results:\n";
|
||||
|
||||
const char report_sum_bw_format[] =
|
||||
"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %s\n";
|
||||
|
||||
const char report_sum_bw_retrans_format[] =
|
||||
"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %3"PRId64" %s\n";
|
||||
|
||||
const char report_sum_bw_udp_format[] =
|
||||
"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms %" PRId64 "/%" PRId64 " (%.2g%%) %s\n";
|
||||
|
||||
const char report_sum_bw_udp_sender_format[] =
|
||||
"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %s %" PRId64 " %s\n";
|
||||
|
||||
const char report_omitted[] = "(omitted)";
|
||||
|
||||
const char report_bw_separator[] =
|
||||
"- - - - - - - - - - - - - - - - - - - - - - - - -\n";
|
||||
|
||||
const char report_outoforder[] =
|
||||
"[%3d]%s %4.1f-%4.1f sec %d datagrams received out-of-order\n";
|
||||
|
||||
const char report_sum_outoforder[] =
|
||||
"[SUM]%s %4.1f-%4.1f sec %"PRIu64" datagrams received out-of-order\n";
|
||||
|
||||
const char report_peer[] =
|
||||
"[%3d] local %s port %u connected with %s port %u\n";
|
||||
|
||||
const char report_mss_unsupported[] =
|
||||
"[%3d] MSS and MTU size unknown (TCP_MAXSEG not supported by OS?)\n";
|
||||
|
||||
const char report_mss[] =
|
||||
"[%3d] MSS size %d bytes (MTU %d bytes, %s)\n";
|
||||
|
||||
const char report_datagrams[] =
|
||||
"[%3d] Sent %d datagrams\n";
|
||||
|
||||
const char report_sum_datagrams[] =
|
||||
"[SUM] Sent %d datagrams\n";
|
||||
|
||||
const char server_reporting[] =
|
||||
"[%3d] Server Report:\n";
|
||||
|
||||
const char reportCSV_peer[] =
|
||||
"%s,%u,%s,%u";
|
||||
|
||||
const char report_cpu[] =
|
||||
"CPU Utilization: %s/%s %.1f%% (%.1f%%u/%.1f%%s), %s/%s %.1f%% (%.1f%%u/%.1f%%s)\n";
|
||||
|
||||
const char report_local[] = "local";
|
||||
const char report_remote[] = "remote";
|
||||
const char report_sender[] = "sender";
|
||||
const char report_receiver[] = "receiver";
|
||||
const char report_sender_not_available_format[] = "[%3d] (sender statistics not available)\n";
|
||||
const char report_sender_not_available_summary_format[] = "[%3s] (sender statistics not available)\n";
|
||||
const char report_receiver_not_available_format[] = "[%3d] (receiver statistics not available)\n";
|
||||
const char report_receiver_not_available_summary_format[] = "[%3s] (receiver statistics not available)\n";
|
||||
|
||||
#if defined(linux)
|
||||
const char report_tcpInfo[] =
|
||||
"event=TCP_Info CWND=%u SND_SSTHRESH=%u RCV_SSTHRESH=%u UNACKED=%u SACK=%u LOST=%u RETRANS=%u FACK=%u RTT=%u REORDERING=%u\n";
|
||||
#endif
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
const char report_tcpInfo[] =
|
||||
"event=TCP_Info CWND=%u RCV_WIND=%u SND_SSTHRESH=%u RTT=%u\n";
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_QUAD_SUPPORT
|
||||
#ifdef HAVE_PRINTF_QD
|
||||
const char reportCSV_bw_format[] =
|
||||
"%s,%s,%d,%.1f-%.1f,%qd,%qd\n";
|
||||
|
||||
const char reportCSV_bw_udp_format[] =
|
||||
"%s,%s,%d,%.1f-%.1f,%qd,%qd,%.3f,%d,%d,%.3f,%d\n";
|
||||
#else // HAVE_PRINTF_QD
|
||||
const char reportCSV_bw_format[] =
|
||||
"%s,%s,%d,%.1f-%.1f,%lld,%lld\n";
|
||||
|
||||
const char reportCSV_bw_udp_format[] =
|
||||
"%s,%s,%d,%.1f-%.1f,%lld,%lld,%.3f,%d,%d,%.3f,%d\n";
|
||||
#endif // HAVE_PRINTF_QD
|
||||
#else // HAVE_QUAD_SUPPORT
|
||||
#ifdef WIN32
|
||||
const char reportCSV_bw_format[] =
|
||||
"%s,%s,%d,%.1f-%.1f,%I64d,%I64d\n";
|
||||
|
||||
const char reportCSV_bw_udp_format[] =
|
||||
"%s,%s,%d,%.1f-%.1f,%I64d,%I64d,%.3f,%d,%d,%.3f,%d\n";
|
||||
#else
|
||||
const char reportCSV_bw_format[] =
|
||||
"%s,%s,%d,%.1f-%.1f,%d,%d\n";
|
||||
|
||||
const char reportCSV_bw_udp_format[] =
|
||||
"%s,%s,%d,%.1f-%.1f,%d,%d,%.3f,%d,%d,%.3f,%d\n";
|
||||
#endif //WIN32
|
||||
#endif //HAVE_QUAD_SUPPORT
|
||||
/* -------------------------------------------------------------------
|
||||
* warnings
|
||||
* ------------------------------------------------------------------- */
|
||||
|
||||
const char warn_window_requested[] =
|
||||
" (WARNING: requested %s)";
|
||||
|
||||
const char warn_window_small[] =
|
||||
"WARNING: TCP window size set to %d bytes. A small window size\n\
|
||||
will give poor performance. See the Iperf documentation.\n";
|
||||
|
||||
const char warn_delay_large[] =
|
||||
"WARNING: delay too large, reducing from %.1f to 1.0 seconds.\n";
|
||||
|
||||
const char warn_no_pathmtu[] =
|
||||
"WARNING: Path MTU Discovery may not be enabled.\n";
|
||||
|
||||
const char warn_no_ack[]=
|
||||
"[%3d] WARNING: did not receive ack of last datagram after %d tries.\n";
|
||||
|
||||
const char warn_ack_failed[]=
|
||||
"[%3d] WARNING: ack of last datagram failed after %d tries.\n";
|
||||
|
||||
const char warn_fileopen_failed[]=
|
||||
"WARNING: Unable to open file stream for transfer\n\
|
||||
Using default data stream. \n";
|
||||
|
||||
const char unable_to_change_win[]=
|
||||
"WARNING: Unable to change the window size\n";
|
||||
|
||||
const char opt_estimate[]=
|
||||
"Optimal Estimate\n";
|
||||
|
||||
const char report_interval_small[] =
|
||||
"WARNING: interval too small, increasing from %3.2f to 0.5 seconds.\n";
|
||||
|
||||
const char warn_invalid_server_option[] =
|
||||
"WARNING: option -%c is not valid for server mode\n";
|
||||
|
||||
const char warn_invalid_client_option[] =
|
||||
"WARNING: option -%c is not valid for client mode\n";
|
||||
|
||||
const char warn_invalid_compatibility_option[] =
|
||||
"WARNING: option -%c is not valid in compatibility mode\n";
|
||||
|
||||
const char warn_implied_udp[] =
|
||||
"WARNING: option -%c implies udp testing\n";
|
||||
|
||||
const char warn_implied_compatibility[] =
|
||||
"WARNING: option -%c has implied compatibility mode\n";
|
||||
|
||||
const char warn_buffer_too_small[] =
|
||||
"WARNING: the UDP buffer was increased to %d for proper operation\n";
|
||||
|
||||
const char warn_invalid_single_threaded[] =
|
||||
"WARNING: option -%c is not valid in single threaded versions\n";
|
||||
|
||||
const char warn_invalid_report_style[] =
|
||||
"WARNING: unknown reporting style \"%s\", switching to default\n";
|
||||
|
||||
const char warn_invalid_report[] =
|
||||
"WARNING: unknown reporting type \"%c\", ignored\n valid options are:\n\t exclude: C(connection) D(data) M(multicast) S(settings) V(server) report\n\n";
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end extern "C" */
|
||||
#endif
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2020, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#ifndef IPERF_LOCALE_H
|
||||
#define IPERF_LOCALE_H
|
||||
|
||||
extern const char usage_shortstr[];
|
||||
extern const char usage_longstr[];
|
||||
extern const char version[];
|
||||
|
||||
extern const char seperator_line[];
|
||||
|
||||
extern const char server_port[] ;
|
||||
extern const char client_port[] ;
|
||||
extern const char bind_address[] ;
|
||||
extern const char bind_dev[] ;
|
||||
extern const char multicast_ttl[] ;
|
||||
extern const char join_multicast[] ;
|
||||
extern const char client_datagram_size[] ;
|
||||
extern const char server_datagram_size[] ;
|
||||
extern const char tcp_window_size[] ;
|
||||
extern const char udp_buffer_size[] ;
|
||||
extern const char window_default[] ;
|
||||
extern const char wait_server_threads[] ;
|
||||
extern const char test_start_time[];
|
||||
extern const char test_start_bytes[];
|
||||
extern const char test_start_blocks[];
|
||||
|
||||
extern const char report_time[] ;
|
||||
extern const char report_connecting[] ;
|
||||
extern const char report_reverse[] ;
|
||||
extern const char report_accepted[] ;
|
||||
extern const char report_cookie[] ;
|
||||
extern const char report_connected[] ;
|
||||
extern const char report_authentication_succeeded[] ;
|
||||
extern const char report_authentication_failed[] ;
|
||||
extern const char report_window[] ;
|
||||
extern const char report_autotune[] ;
|
||||
extern const char report_omit_done[] ;
|
||||
extern const char report_diskfile[] ;
|
||||
extern const char report_done[] ;
|
||||
extern const char report_read_lengths[] ;
|
||||
extern const char report_read_length_times[] ;
|
||||
extern const char report_bw_header[] ;
|
||||
extern const char report_bw_header_bidir[] ;
|
||||
extern const char report_bw_retrans_header[] ;
|
||||
extern const char report_bw_retrans_header_bidir[] ;
|
||||
extern const char report_bw_retrans_cwnd_header[] ;
|
||||
extern const char report_bw_retrans_cwnd_header_bidir[] ;
|
||||
extern const char report_bw_udp_header[] ;
|
||||
extern const char report_bw_udp_header_bidir[] ;
|
||||
extern const char report_bw_udp_sender_header[] ;
|
||||
extern const char report_bw_udp_sender_header_bidir[] ;
|
||||
extern const char report_bw_format[] ;
|
||||
extern const char report_bw_retrans_format[] ;
|
||||
extern const char report_bw_retrans_cwnd_format[] ;
|
||||
extern const char report_bw_udp_format[] ;
|
||||
extern const char report_bw_udp_format_no_omitted_error[] ;
|
||||
extern const char report_bw_udp_sender_format[] ;
|
||||
extern const char report_summary[] ;
|
||||
extern const char report_sum_bw_format[] ;
|
||||
extern const char report_sum_bw_retrans_format[] ;
|
||||
extern const char report_sum_bw_udp_format[] ;
|
||||
extern const char report_sum_bw_udp_sender_format[] ;
|
||||
extern const char report_omitted[] ;
|
||||
extern const char report_bw_separator[] ;
|
||||
extern const char report_outoforder[] ;
|
||||
extern const char report_sum_outoforder[] ;
|
||||
extern const char report_peer[] ;
|
||||
extern const char report_mss_unsupported[] ;
|
||||
extern const char report_mss[] ;
|
||||
extern const char report_datagrams[] ;
|
||||
extern const char report_sum_datagrams[] ;
|
||||
extern const char server_reporting[] ;
|
||||
extern const char reportCSV_peer[] ;
|
||||
|
||||
extern const char report_cpu[] ;
|
||||
extern const char report_local[] ;
|
||||
extern const char report_remote[] ;
|
||||
extern const char report_sender[] ;
|
||||
extern const char report_receiver[] ;
|
||||
extern const char report_sender_not_available_format[];
|
||||
extern const char report_sender_not_available_summary_format[];
|
||||
extern const char report_receiver_not_available_format[];
|
||||
extern const char report_receiver_not_available_summary_format[];
|
||||
|
||||
extern const char report_tcpInfo[] ;
|
||||
extern const char report_tcpInfo[] ;
|
||||
|
||||
|
||||
extern const char warn_window_requested[] ;
|
||||
extern const char warn_window_small[] ;
|
||||
extern const char warn_delay_large[] ;
|
||||
extern const char warn_no_pathmtu[] ;
|
||||
extern const char warn_no_ack[];
|
||||
extern const char warn_ack_failed[];
|
||||
extern const char warn_fileopen_failed[];
|
||||
extern const char unable_to_change_win[];
|
||||
extern const char opt_estimate[];
|
||||
extern const char report_interval_small[] ;
|
||||
extern const char warn_invalid_server_option[] ;
|
||||
extern const char warn_invalid_client_option[] ;
|
||||
extern const char warn_invalid_compatibility_option[] ;
|
||||
extern const char warn_implied_udp[] ;
|
||||
extern const char warn_implied_compatibility[] ;
|
||||
extern const char warn_buffer_too_small[] ;
|
||||
extern const char warn_invalid_single_threaded[] ;
|
||||
extern const char warn_invalid_report_style[] ;
|
||||
extern const char warn_invalid_report[] ;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "iperf_config.h"
|
||||
|
||||
#if defined(HAVE_PTHREAD) && defined(__ANDROID__)
|
||||
|
||||
/* Workaround for `pthread_cancel()` in Android, using `pthread_kill()` instead,
|
||||
* as Android NDK does not support `pthread_cancel()`.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include "iperf_pthread.h"
|
||||
|
||||
void iperf_thread_exit_handler(int sig)
|
||||
{
|
||||
pthread_exit(0);
|
||||
}
|
||||
|
||||
int iperf_set_thread_exit_handler() {
|
||||
int rc;
|
||||
struct sigaction actions;
|
||||
|
||||
memset(&actions, 0, sizeof(actions));
|
||||
sigemptyset(&actions.sa_mask);
|
||||
actions.sa_flags = 0;
|
||||
actions.sa_handler = iperf_thread_exit_handler;
|
||||
|
||||
rc = sigaction(SIGUSR1, &actions, NULL);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int pthread_setcanceltype(int type, int *oldtype) { return 0; }
|
||||
int pthread_setcancelstate(int state, int *oldstate) { return 0; }
|
||||
int pthread_cancel(pthread_t thread_id) {
|
||||
int status;
|
||||
if ((status = iperf_set_thread_exit_handler()) == 0) {
|
||||
status = pthread_kill(thread_id, SIGUSR1);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
#endif // defined(HAVE_PTHREAD) && defined(__ANDROID__)
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "iperf_config.h"
|
||||
|
||||
#if defined(HAVE_PTHREAD)
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
/* Adding missing `pthread` related definitions in Android.
|
||||
*/
|
||||
|
||||
#define PTHREAD_CANCEL_ASYNCHRONOUS 0
|
||||
#define PTHREAD_CANCEL_ENABLE 0
|
||||
|
||||
int pthread_setcanceltype(int type, int *oldtype);
|
||||
int pthread_setcancelstate(int state, int *oldstate);
|
||||
int pthread_cancel(pthread_t thread_id);
|
||||
|
||||
#endif // defined(__ANDROID__)
|
||||
|
||||
#endif // defined(HAVE_PTHREAD)
|
||||
@@ -0,0 +1,778 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2019, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#include "iperf_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/select.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "iperf.h"
|
||||
#include "iperf_api.h"
|
||||
#include "iperf_sctp.h"
|
||||
#include "net.h"
|
||||
|
||||
|
||||
|
||||
/* iperf_sctp_recv
|
||||
*
|
||||
* receives the data for SCTP
|
||||
*/
|
||||
int
|
||||
iperf_sctp_recv(struct iperf_stream *sp)
|
||||
{
|
||||
#if defined(HAVE_SCTP_H)
|
||||
int r;
|
||||
|
||||
r = Nread_no_select(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Only count bytes received while we're in the correct state. */
|
||||
if (sp->test->state == TEST_RUNNING) {
|
||||
sp->result->bytes_received += r;
|
||||
sp->result->bytes_received_this_interval += r;
|
||||
}
|
||||
else {
|
||||
if (sp->test->debug)
|
||||
printf("Late receive, state = %d\n", sp->test->state);
|
||||
}
|
||||
|
||||
return r;
|
||||
#else
|
||||
i_errno = IENOSCTP;
|
||||
return -1;
|
||||
#endif /* HAVE_SCTP_H */
|
||||
}
|
||||
|
||||
|
||||
/* iperf_sctp_send
|
||||
*
|
||||
* sends the data for SCTP
|
||||
*/
|
||||
int
|
||||
iperf_sctp_send(struct iperf_stream *sp)
|
||||
{
|
||||
#if defined(HAVE_SCTP_H)
|
||||
int r;
|
||||
|
||||
r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
sp->result->bytes_sent += r;
|
||||
sp->result->bytes_sent_this_interval += r;
|
||||
|
||||
return r;
|
||||
#else
|
||||
i_errno = IENOSCTP;
|
||||
return -1;
|
||||
#endif /* HAVE_SCTP_H */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* iperf_sctp_accept
|
||||
*
|
||||
* accept a new SCTP stream connection
|
||||
*/
|
||||
int
|
||||
iperf_sctp_accept(struct iperf_test * test)
|
||||
{
|
||||
#if defined(HAVE_SCTP_H)
|
||||
int s;
|
||||
signed char rbuf = ACCESS_DENIED;
|
||||
char cookie[COOKIE_SIZE];
|
||||
socklen_t len;
|
||||
struct sockaddr_storage addr;
|
||||
|
||||
len = sizeof(addr);
|
||||
s = accept(test->listener, (struct sockaddr *) &addr, &len);
|
||||
if (s < 0) {
|
||||
i_errno = IESTREAMCONNECT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (Nread(s, cookie, COOKIE_SIZE, Psctp) < 0) {
|
||||
i_errno = IERECVCOOKIE;
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strncmp(test->cookie, cookie, COOKIE_SIZE) != 0) {
|
||||
if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Psctp) < 0) {
|
||||
i_errno = IESENDMESSAGE;
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
close(s);
|
||||
}
|
||||
|
||||
return s;
|
||||
#else
|
||||
i_errno = IENOSCTP;
|
||||
return -1;
|
||||
#endif /* HAVE_SCTP_H */
|
||||
}
|
||||
|
||||
|
||||
/* iperf_sctp_listen
|
||||
*
|
||||
* start up a listener for SCTP stream connections
|
||||
*/
|
||||
int
|
||||
iperf_sctp_listen(struct iperf_test *test)
|
||||
{
|
||||
#if defined(HAVE_SCTP_H)
|
||||
struct addrinfo hints, *res;
|
||||
char portstr[6];
|
||||
int s, opt, saved_errno;
|
||||
|
||||
close(test->listener);
|
||||
test->listener = -1;
|
||||
|
||||
snprintf(portstr, 6, "%d", test->server_port);
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
/*
|
||||
* If binding to the wildcard address with no explicit address
|
||||
* family specified, then force us to get an AF_INET6 socket.
|
||||
* More details in the comments in netanounce().
|
||||
*/
|
||||
if (test->settings->domain == AF_UNSPEC && !test->bind_address) {
|
||||
hints.ai_family = AF_INET6;
|
||||
} else {
|
||||
hints.ai_family = test->settings->domain;
|
||||
}
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
if ((gerror = getaddrinfo(test->bind_address, portstr, &hints, &res)) != 0) {
|
||||
i_errno = IESTREAMLISTEN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((s = socket(res->ai_family, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
|
||||
freeaddrinfo(res);
|
||||
i_errno = IESTREAMLISTEN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((opt = test->settings->socket_bufsize)) {
|
||||
int saved_errno;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (test->bind_dev) {
|
||||
#if defined(SO_BINDTODEVICE)
|
||||
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
|
||||
test->bind_dev, IFNAMSIZ) < 0)
|
||||
#endif // SO_BINDTODEVICE
|
||||
{
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
i_errno = IEBINDDEV;
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
|
||||
if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC ||
|
||||
test->settings->domain == AF_INET6)) {
|
||||
if (test->settings->domain == AF_UNSPEC)
|
||||
opt = 0;
|
||||
else
|
||||
opt = 1;
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
(char *) &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
i_errno = IEPROTOCOL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif /* IPV6_V6ONLY */
|
||||
|
||||
opt = 1;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
i_errno = IEREUSEADDR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* servers must call sctp_bindx() _instead_ of bind() */
|
||||
if (!TAILQ_EMPTY(&test->xbind_addrs)) {
|
||||
if (iperf_sctp_bindx(test, s, IPERF_SCTP_SERVER)) {
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
return -1;
|
||||
}
|
||||
} else
|
||||
if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESTREAMLISTEN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
if (listen(s, INT_MAX) < 0) {
|
||||
i_errno = IESTREAMLISTEN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
test->listener = s;
|
||||
|
||||
return s;
|
||||
#else
|
||||
i_errno = IENOSCTP;
|
||||
return -1;
|
||||
#endif /* HAVE_SCTP_H */
|
||||
}
|
||||
|
||||
|
||||
/* iperf_sctp_connect
|
||||
*
|
||||
* connect to a SCTP stream listener
|
||||
*/
|
||||
int
|
||||
iperf_sctp_connect(struct iperf_test *test)
|
||||
{
|
||||
#if defined(HAVE_SCTP_H)
|
||||
int s, opt, saved_errno;
|
||||
char portstr[6];
|
||||
struct addrinfo hints, *local_res = NULL, *server_res = NULL;
|
||||
|
||||
if (test->bind_address) {
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = test->settings->domain;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
if ((gerror = getaddrinfo(test->bind_address, NULL, &hints, &local_res)) != 0) {
|
||||
i_errno = IESTREAMCONNECT;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = test->settings->domain;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
snprintf(portstr, sizeof(portstr), "%d", test->server_port);
|
||||
if ((gerror = getaddrinfo(test->server_hostname, portstr, &hints, &server_res)) != 0) {
|
||||
if (test->bind_address)
|
||||
freeaddrinfo(local_res);
|
||||
i_errno = IESTREAMCONNECT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = socket(server_res->ai_family, SOCK_STREAM, IPPROTO_SCTP);
|
||||
if (s < 0) {
|
||||
freeaddrinfo(local_res);
|
||||
freeaddrinfo(server_res);
|
||||
i_errno = IESTREAMCONNECT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((opt = test->settings->socket_bufsize)) {
|
||||
int saved_errno;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (test->bind_dev) {
|
||||
#if defined(SO_BINDTODEVICE)
|
||||
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
|
||||
test->bind_dev, IFNAMSIZ) < 0)
|
||||
#endif // SO_BINDTODEVICE
|
||||
{
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(local_res);
|
||||
freeaddrinfo(server_res);
|
||||
i_errno = IEBINDDEV;
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Various ways to bind the local end of the connection.
|
||||
* 1. --bind (with or without --cport).
|
||||
*/
|
||||
if (test->bind_address) {
|
||||
struct sockaddr_in *lcladdr;
|
||||
lcladdr = (struct sockaddr_in *)local_res->ai_addr;
|
||||
lcladdr->sin_port = htons(test->bind_port);
|
||||
|
||||
if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(local_res);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESTREAMCONNECT;
|
||||
return -1;
|
||||
}
|
||||
freeaddrinfo(local_res);
|
||||
}
|
||||
/* --cport, no --bind */
|
||||
else if (test->bind_port) {
|
||||
size_t addrlen;
|
||||
struct sockaddr_storage lcl;
|
||||
|
||||
/* IPv4 */
|
||||
if (server_res->ai_family == AF_INET) {
|
||||
struct sockaddr_in *lcladdr = (struct sockaddr_in *) &lcl;
|
||||
lcladdr->sin_family = AF_INET;
|
||||
lcladdr->sin_port = htons(test->bind_port);
|
||||
lcladdr->sin_addr.s_addr = INADDR_ANY;
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
}
|
||||
/* IPv6 */
|
||||
else if (server_res->ai_family == AF_INET6) {
|
||||
struct sockaddr_in6 *lcladdr = (struct sockaddr_in6 *) &lcl;
|
||||
lcladdr->sin6_family = AF_INET6;
|
||||
lcladdr->sin6_port = htons(test->bind_port);
|
||||
lcladdr->sin6_addr = in6addr_any;
|
||||
addrlen = sizeof(struct sockaddr_in6);
|
||||
}
|
||||
/* Unknown protocol */
|
||||
else {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IEPROTOCOL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESTREAMCONNECT;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (test->no_delay != 0) {
|
||||
opt = 1;
|
||||
if (setsockopt(s, IPPROTO_SCTP, SCTP_NODELAY, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETNODELAY;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((test->settings->mss >= 512 && test->settings->mss <= 131072)) {
|
||||
|
||||
/*
|
||||
* Some platforms use a struct sctp_assoc_value as the
|
||||
* argument to SCTP_MAXSEG. Other (older API implementations)
|
||||
* take an int. FreeBSD 10 and CentOS 6 support SCTP_MAXSEG,
|
||||
* but OpenSolaris 11 doesn't.
|
||||
*/
|
||||
#ifdef HAVE_STRUCT_SCTP_ASSOC_VALUE
|
||||
struct sctp_assoc_value av;
|
||||
|
||||
/*
|
||||
* Some platforms support SCTP_FUTURE_ASSOC, others need to
|
||||
* (equivalently) do 0 here. FreeBSD 10 is an example of the
|
||||
* former, CentOS 6 Linux is an example of the latter.
|
||||
*/
|
||||
#ifdef SCTP_FUTURE_ASSOC
|
||||
av.assoc_id = SCTP_FUTURE_ASSOC;
|
||||
#else
|
||||
av.assoc_id = 0;
|
||||
#endif
|
||||
av.assoc_value = test->settings->mss;
|
||||
|
||||
if (setsockopt(s, IPPROTO_SCTP, SCTP_MAXSEG, &av, sizeof(av)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETMSS;
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
opt = test->settings->mss;
|
||||
|
||||
/*
|
||||
* Solaris might not support this option. If it doesn't work,
|
||||
* ignore the error (at least for now).
|
||||
*/
|
||||
if (setsockopt(s, IPPROTO_SCTP, SCTP_MAXSEG, &opt, sizeof(opt)) < 0 &&
|
||||
errno != ENOPROTOOPT) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETMSS;
|
||||
return -1;
|
||||
}
|
||||
#endif /* HAVE_STRUCT_SCTP_ASSOC_VALUE */
|
||||
}
|
||||
|
||||
if (test->settings->num_ostreams > 0) {
|
||||
struct sctp_initmsg initmsg;
|
||||
|
||||
memset(&initmsg, 0, sizeof(struct sctp_initmsg));
|
||||
initmsg.sinit_num_ostreams = test->settings->num_ostreams;
|
||||
|
||||
if (setsockopt(s, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(struct sctp_initmsg)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETSCTPNSTREAM;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* clients must call bind() followed by sctp_bindx() before connect() */
|
||||
if (!TAILQ_EMPTY(&test->xbind_addrs)) {
|
||||
if (iperf_sctp_bindx(test, s, IPERF_SCTP_CLIENT)) {
|
||||
freeaddrinfo(server_res);
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO support sctp_connectx() to avoid heartbeating. */
|
||||
if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESTREAMCONNECT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Send cookie for verification */
|
||||
if (Nwrite(s, test->cookie, COOKIE_SIZE, Psctp) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESENDCOOKIE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We want to allow fragmentation. But there's at least one
|
||||
* implementation (Solaris) that doesn't support this option,
|
||||
* even though it defines SCTP_DISABLE_FRAGMENTS. So we have to
|
||||
* try setting the option and ignore the error, if it doesn't
|
||||
* work.
|
||||
*/
|
||||
opt = 0;
|
||||
if (setsockopt(s, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &opt, sizeof(opt)) < 0 &&
|
||||
errno != ENOPROTOOPT) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETSCTPDISABLEFRAG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(server_res);
|
||||
return s;
|
||||
#else
|
||||
i_errno = IENOSCTP;
|
||||
return -1;
|
||||
#endif /* HAVE_SCTP_H */
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
iperf_sctp_init(struct iperf_test *test)
|
||||
{
|
||||
#if defined(HAVE_SCTP_H)
|
||||
return 0;
|
||||
#else
|
||||
i_errno = IENOSCTP;
|
||||
return -1;
|
||||
#endif /* HAVE_SCTP_H */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* iperf_sctp_bindx
|
||||
*
|
||||
* handle binding to multiple endpoints (-X parameters)
|
||||
*/
|
||||
int
|
||||
iperf_sctp_bindx(struct iperf_test *test, int s, int is_server)
|
||||
{
|
||||
#if defined(HAVE_SCTP_H)
|
||||
struct addrinfo hints;
|
||||
char portstr[6];
|
||||
char *servname;
|
||||
struct addrinfo *ai, *ai0;
|
||||
struct sockaddr *xaddrs;
|
||||
struct xbind_entry *xbe, *xbe0;
|
||||
char *bp;
|
||||
size_t xaddrlen;
|
||||
int nxaddrs;
|
||||
int retval;
|
||||
int domain;
|
||||
int saved_errno;
|
||||
|
||||
domain = test->settings->domain;
|
||||
xbe0 = NULL;
|
||||
retval = 0;
|
||||
|
||||
if (TAILQ_EMPTY(&test->xbind_addrs))
|
||||
return retval; /* nothing to do */
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = (domain == AF_UNSPEC ? AF_INET6 : domain);
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
servname = NULL;
|
||||
if (is_server) {
|
||||
hints.ai_flags |= AI_PASSIVE;
|
||||
snprintf(portstr, 6, "%d", test->server_port);
|
||||
servname = portstr;
|
||||
}
|
||||
|
||||
/* client: must pop first -X address and call bind().
|
||||
* sctp_bindx() must see the ephemeral port chosen by bind().
|
||||
* we deliberately ignore the -B argument in this case.
|
||||
*/
|
||||
if (!is_server) {
|
||||
struct sockaddr *sa;
|
||||
struct sockaddr_in *sin;
|
||||
struct sockaddr_in6 *sin6;
|
||||
int eport;
|
||||
|
||||
xbe0 = TAILQ_FIRST(&test->xbind_addrs);
|
||||
TAILQ_REMOVE(&test->xbind_addrs, xbe0, link);
|
||||
|
||||
if ((gerror = getaddrinfo(xbe0->name, servname, &hints, &xbe0->ai)) != 0) {
|
||||
i_errno = IESETSCTPBINDX;
|
||||
retval = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ai = xbe0->ai;
|
||||
if (domain != AF_UNSPEC && domain != ai->ai_family) {
|
||||
i_errno = IESETSCTPBINDX;
|
||||
retval = -1;
|
||||
goto out;
|
||||
}
|
||||
if (bind(s, (struct sockaddr *)ai->ai_addr, ai->ai_addrlen) < 0) {
|
||||
i_errno = IESETSCTPBINDX;
|
||||
retval = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* if only one -X argument, nothing more to do */
|
||||
if (TAILQ_EMPTY(&test->xbind_addrs))
|
||||
goto out;
|
||||
|
||||
sa = (struct sockaddr *)ai->ai_addr;
|
||||
if (sa->sa_family == AF_INET) {
|
||||
sin = (struct sockaddr_in *)ai->ai_addr;
|
||||
eport = sin->sin_port;
|
||||
} else if (sa->sa_family == AF_INET6) {
|
||||
sin6 = (struct sockaddr_in6 *)ai->ai_addr;
|
||||
eport = sin6->sin6_port;
|
||||
} else {
|
||||
i_errno = IESETSCTPBINDX;
|
||||
retval = -1;
|
||||
goto out;
|
||||
}
|
||||
snprintf(portstr, 6, "%d", ntohs(eport));
|
||||
servname = portstr;
|
||||
}
|
||||
|
||||
/* pass 1: resolve and compute lengths. */
|
||||
nxaddrs = 0;
|
||||
xaddrlen = 0;
|
||||
TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
|
||||
if (xbe->ai != NULL)
|
||||
freeaddrinfo(xbe->ai);
|
||||
if ((gerror = getaddrinfo(xbe->name, servname, &hints, &xbe->ai)) != 0) {
|
||||
i_errno = IESETSCTPBINDX;
|
||||
retval = -1;
|
||||
goto out;
|
||||
}
|
||||
ai0 = xbe->ai;
|
||||
for (ai = ai0; ai; ai = ai->ai_next) {
|
||||
if (domain != AF_UNSPEC && domain != ai->ai_family)
|
||||
continue;
|
||||
xaddrlen += ai->ai_addrlen;
|
||||
++nxaddrs;
|
||||
}
|
||||
}
|
||||
|
||||
/* pass 2: copy into flat buffer. */
|
||||
xaddrs = (struct sockaddr *)malloc(xaddrlen);
|
||||
if (!xaddrs) {
|
||||
i_errno = IESETSCTPBINDX;
|
||||
retval = -1;
|
||||
goto out;
|
||||
}
|
||||
bp = (char *)xaddrs;
|
||||
TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
|
||||
ai0 = xbe->ai;
|
||||
for (ai = ai0; ai; ai = ai->ai_next) {
|
||||
if (domain != AF_UNSPEC && domain != ai->ai_family)
|
||||
continue;
|
||||
memcpy(bp, ai->ai_addr, ai->ai_addrlen);
|
||||
bp += ai->ai_addrlen;
|
||||
}
|
||||
}
|
||||
|
||||
if (sctp_bindx(s, xaddrs, nxaddrs, SCTP_BINDX_ADD_ADDR) == -1) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
free(xaddrs);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETSCTPBINDX;
|
||||
retval = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
free(xaddrs);
|
||||
retval = 0;
|
||||
|
||||
out:
|
||||
/* client: put head node back. */
|
||||
if (!is_server && xbe0)
|
||||
TAILQ_INSERT_HEAD(&test->xbind_addrs, xbe0, link);
|
||||
|
||||
TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
|
||||
if (xbe->ai) {
|
||||
freeaddrinfo(xbe->ai);
|
||||
xbe->ai = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
#else
|
||||
i_errno = IENOSCTP;
|
||||
return -1;
|
||||
#endif /* HAVE_SCTP_H */
|
||||
}
|
||||
|
||||
|
||||
/* iperf_sctp_get_rtt
|
||||
*
|
||||
* Get SCTP stream RTT.
|
||||
* Assuming that iperf3 supports only one-toone SCTP associassion, and not one-to-many associassion.
|
||||
*
|
||||
* Main resouses used are RFC-6458, man pages for SCTP,
|
||||
* https://docs.oracle.com/cd/E19253-01/817-4415/sockets-199/index.html.
|
||||
*
|
||||
*/
|
||||
int
|
||||
iperf_sctp_get_info(struct iperf_stream *sp, struct iperf_sctp_info *sctp_info)
|
||||
{
|
||||
#if defined(HAVE_SCTP_H)
|
||||
struct sctp_status status;
|
||||
socklen_t len;
|
||||
sctp_assoc_t assoc_id;
|
||||
int rc = 0;
|
||||
|
||||
if (sp->test->protocol->id != Psctp) {
|
||||
rc = -1;
|
||||
} else {
|
||||
#ifdef SCTP_FUTURE_ASSOC
|
||||
assoc_id = SCTP_FUTURE_ASSOC;
|
||||
#else
|
||||
assoc_id = 0;
|
||||
#endif
|
||||
len = sizeof(status);
|
||||
rc = sctp_opt_info(sp->socket, assoc_id, SCTP_STATUS, &status, &len);
|
||||
if (rc < 0) {
|
||||
if (sp->test->debug_level >= DEBUG_LEVEL_ERROR)
|
||||
iperf_err(sp->test, "sctp_opt_info get SCTP_STATUS for socket %d failed with errno %d - %s", sp->socket, errno, strerror(errno));
|
||||
} else {
|
||||
sctp_info->wnd = status.sstat_rwnd;
|
||||
sctp_info->rtt = status.sstat_primary.spinfo_srtt;
|
||||
sctp_info->pmtu = status.sstat_primary.spinfo_mtu;
|
||||
sctp_info->cwnd = status.sstat_primary.spinfo_cwnd;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
#else
|
||||
return -1;
|
||||
#endif /* HAVE_SCTP_H */
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#ifndef IPERF_SCTP_H
|
||||
#define IPERF_SCTP_H
|
||||
|
||||
#ifdef HAVE_NETINET_SCTP_H
|
||||
#include <netinet/sctp.h>
|
||||
#endif /* HAVE_NETINET_SCTP_H */
|
||||
|
||||
/**
|
||||
* iperf_sctp_accept -- accepts a new SCTP connection
|
||||
* on sctp_listener_socket for SCTP data and param/result
|
||||
* exchange messages
|
||||
*returns 0 on success
|
||||
*
|
||||
*/
|
||||
int iperf_sctp_accept(struct iperf_test *);
|
||||
|
||||
/**
|
||||
* iperf_sctp_recv -- receives the data for sctp
|
||||
* and the Param/result message exchange
|
||||
*returns state of packet received
|
||||
*
|
||||
*/
|
||||
int iperf_sctp_recv(struct iperf_stream *);
|
||||
|
||||
|
||||
/**
|
||||
* iperf_sctp_send -- sends the client data for sctp
|
||||
* and the Param/result message exchanges
|
||||
* returns: bytes sent
|
||||
*
|
||||
*/
|
||||
int iperf_sctp_send(struct iperf_stream *);
|
||||
|
||||
|
||||
int iperf_sctp_listen(struct iperf_test *);
|
||||
|
||||
int iperf_sctp_connect(struct iperf_test *);
|
||||
|
||||
int iperf_sctp_init(struct iperf_test *test);
|
||||
|
||||
#define IPERF_SCTP_CLIENT 0
|
||||
#define IPERF_SCTP_SERVER 1
|
||||
|
||||
int iperf_sctp_bindx(struct iperf_test *test, int s, int is_server);
|
||||
|
||||
int iperf_sctp_get_info(struct iperf_stream *sp, struct iperf_sctp_info *sctp_info);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,959 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2023 The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
/* iperf_server_api.c: Functions to be used by an iperf server
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sched.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "iperf.h"
|
||||
#include "iperf_api.h"
|
||||
#include "iperf_udp.h"
|
||||
#include "iperf_tcp.h"
|
||||
#include "iperf_util.h"
|
||||
#include "timer.h"
|
||||
#include "iperf_time.h"
|
||||
#include "net.h"
|
||||
#include "units.h"
|
||||
#include "iperf_util.h"
|
||||
#include "iperf_locale.h"
|
||||
|
||||
#if defined(HAVE_TCP_CONGESTION)
|
||||
#if !defined(TCP_CA_NAME_MAX)
|
||||
#define TCP_CA_NAME_MAX 16
|
||||
#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;
|
||||
|
||||
/* Blocking signal to make sure that signal will be handled by main thread */
|
||||
sigset_t set;
|
||||
sigemptyset(&set);
|
||||
#ifdef SIGTERM
|
||||
sigaddset(&set, SIGTERM);
|
||||
#endif
|
||||
#ifdef SIGHUP
|
||||
sigaddset(&set, SIGHUP);
|
||||
#endif
|
||||
#ifdef SIGINT
|
||||
sigaddset(&set, SIGINT);
|
||||
#endif
|
||||
if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {
|
||||
i_errno = IEPTHREADSIGMASK;
|
||||
goto cleanup_and_fail;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_TCP_USER_TIMEOUT */
|
||||
|
||||
#if defined (HAVE_TCP_KEEPALIVE)
|
||||
// Set Control Connection TCP Keepalive (especially useful for long UDP test sessions)
|
||||
if (iperf_set_control_keepalive(test) < 0)
|
||||
return -1;
|
||||
#endif //HAVE_TCP_KEEPALIVE
|
||||
|
||||
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 (iperf_set_send_state(test, PARAM_EXCHANGE) != 0)
|
||||
goto error_handling;
|
||||
if (iperf_exchange_parameters(test) < 0)
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
return 0;
|
||||
error_handling:
|
||||
close(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
int
|
||||
iperf_handle_message_server(struct iperf_test *test)
|
||||
{
|
||||
int rval;
|
||||
struct iperf_stream *sp;
|
||||
|
||||
if (test->debug_level >= DEBUG_LEVEL_INFO) {
|
||||
iperf_printf(test, "Reading new State from the Client - current state is %d-%s\n", test->state, state_to_text(test->state));
|
||||
}
|
||||
|
||||
// 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;
|
||||
iperf_set_test_state(test, IPERF_DONE);
|
||||
return 0;
|
||||
} else {
|
||||
i_errno = IERECVMESSAGE;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (test->debug_level >= DEBUG_LEVEL_INFO) {
|
||||
iperf_printf(test, "State change: server received and changed State to %d-%s\n", test->state, state_to_text(test->state));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
iperf_set_test_state(test, 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_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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (sp->thread_created == 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);
|
||||
}
|
||||
sp->thread_created = 0;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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->json_output) {
|
||||
if (iperf_json_start(test) < 0) {
|
||||
cleanup_server(test);
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
iperf_set_test_state(test, 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
if (test->state == CREATE_STREAMS) {
|
||||
if (FD_ISSET(test->prot_listener, &read_set)) {
|
||||
|
||||
if ((s = test->protocol->accept(test)) < 0) {
|
||||
cleanup_server(test);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* apply other common socket options */
|
||||
if (iperf_common_sockopts(test, s) < 0)
|
||||
{
|
||||
cleanup_server(test);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!is_closed(s)) {
|
||||
|
||||
#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 */
|
||||
|
||||
#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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
sp->thread_created = 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;
|
||||
}
|
||||
|
||||
iflush(test);
|
||||
cleanup_server(test);
|
||||
|
||||
if (test->server_affinity != -1)
|
||||
if (iperf_clearaffinity(test) != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,613 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2022, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/select.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "iperf.h"
|
||||
#include "iperf_api.h"
|
||||
#include "iperf_tcp.h"
|
||||
#include "iperf_util.h"
|
||||
#include "net.h"
|
||||
#include "cjson.h"
|
||||
|
||||
#if defined(HAVE_FLOWLABEL)
|
||||
#include "flowlabel.h"
|
||||
#endif /* HAVE_FLOWLABEL */
|
||||
|
||||
/* iperf_tcp_recv
|
||||
*
|
||||
* receives the data for TCP
|
||||
*/
|
||||
int
|
||||
iperf_tcp_recv(struct iperf_stream *sp)
|
||||
{
|
||||
int r;
|
||||
int sock_opt;
|
||||
|
||||
#if defined(HAVE_MSG_TRUNC)
|
||||
sock_opt = sp->test->settings->skip_rx_copy ? MSG_TRUNC : 0;
|
||||
#else
|
||||
sock_opt = 0;
|
||||
#endif /* HAVE_MSG_TRUNC */
|
||||
|
||||
r = Nrecv_no_select(sp->socket, sp->buffer, sp->settings->blksize, Ptcp, sock_opt);
|
||||
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Only count bytes received while we're in the correct state. */
|
||||
if (sp->test->state == TEST_RUNNING) {
|
||||
sp->result->bytes_received += r;
|
||||
sp->result->bytes_received_this_interval += r;
|
||||
}
|
||||
else {
|
||||
if (sp->test->debug)
|
||||
printf("Late receive, state = %d-%s\n", sp->test->state, state_to_text(sp->test->state));
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/* iperf_tcp_send
|
||||
*
|
||||
* sends the data for TCP
|
||||
*/
|
||||
int
|
||||
iperf_tcp_send(struct iperf_stream *sp)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (!sp->pending_size)
|
||||
sp->pending_size = sp->settings->blksize;
|
||||
|
||||
if (sp->test->zerocopy)
|
||||
r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->pending_size);
|
||||
else
|
||||
r = Nwrite(sp->socket, sp->buffer, sp->pending_size, Ptcp);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
sp->pending_size -= r;
|
||||
sp->result->bytes_sent += r;
|
||||
sp->result->bytes_sent_this_interval += r;
|
||||
|
||||
if (sp->test->debug_level >= DEBUG_LEVEL_DEBUG)
|
||||
printf("sent %d bytes of %d, pending %d, total %" PRIu64 "\n",
|
||||
r, sp->settings->blksize, sp->pending_size, sp->result->bytes_sent);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/* iperf_tcp_accept
|
||||
*
|
||||
* accept a new TCP stream connection
|
||||
*/
|
||||
int
|
||||
iperf_tcp_accept(struct iperf_test * test)
|
||||
{
|
||||
int s;
|
||||
signed char rbuf = ACCESS_DENIED;
|
||||
char cookie[COOKIE_SIZE] = {0};
|
||||
socklen_t len;
|
||||
struct sockaddr_storage addr;
|
||||
|
||||
len = sizeof(addr);
|
||||
if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) {
|
||||
i_errno = IESTREAMCONNECT;
|
||||
return -1;
|
||||
}
|
||||
#if defined(HAVE_SO_MAX_PACING_RATE)
|
||||
/* If fq socket pacing is specified, enable it. */
|
||||
|
||||
if (test->settings->fqrate) {
|
||||
/* Convert bits per second to bytes per second */
|
||||
uint64_t fqrate = test->settings->fqrate / 8;
|
||||
if (fqrate > 0) {
|
||||
if (test->debug) {
|
||||
printf("Setting fair-queue socket pacing to %"PRIu64"\n", fqrate);
|
||||
}
|
||||
if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) {
|
||||
warning("Unable to set socket pacing");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_SO_MAX_PACING_RATE */
|
||||
|
||||
if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) {
|
||||
i_errno = IERECVCOOKIE;
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strncmp(test->cookie, cookie, COOKIE_SIZE) != 0) {
|
||||
if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp) < 0) {
|
||||
iperf_err(test, "failed to send access denied from busy server to new connecting client, errno = %d\n", errno);
|
||||
}
|
||||
close(s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/* iperf_tcp_listen
|
||||
*
|
||||
* start up a listener for TCP stream connections
|
||||
*/
|
||||
int
|
||||
iperf_tcp_listen(struct iperf_test *test)
|
||||
{
|
||||
int s, opt;
|
||||
socklen_t optlen;
|
||||
int saved_errno;
|
||||
int rcvbuf_actual, sndbuf_actual;
|
||||
|
||||
s = test->listener;
|
||||
|
||||
/*
|
||||
* If certain parameters are specified (such as socket buffer
|
||||
* size), then throw away the listening socket (the one for which
|
||||
* we just accepted the control connection) and recreate it with
|
||||
* those parameters. That way, when new data connections are
|
||||
* set, they'll have all the correct parameters in place.
|
||||
*
|
||||
* It's not clear whether this is a requirement or a convenience.
|
||||
*/
|
||||
if (test->no_delay || test->mptcp || test->settings->mss || test->settings->socket_bufsize) {
|
||||
struct addrinfo hints, *res;
|
||||
char portstr[6];
|
||||
int proto = 0;
|
||||
|
||||
FD_CLR(s, &test->read_set);
|
||||
close(s);
|
||||
|
||||
snprintf(portstr, 6, "%d", test->server_port);
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
|
||||
/*
|
||||
* If binding to the wildcard address with no explicit address
|
||||
* family specified, then force us to get an AF_INET6 socket.
|
||||
* More details in the comments in netanounce().
|
||||
*/
|
||||
if (test->settings->domain == AF_UNSPEC && !test->bind_address) {
|
||||
hints.ai_family = AF_INET6;
|
||||
}
|
||||
else {
|
||||
hints.ai_family = test->settings->domain;
|
||||
}
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
if ((gerror = getaddrinfo(test->bind_address, portstr, &hints, &res)) != 0) {
|
||||
i_errno = IESTREAMLISTEN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(HAVE_IPPROTO_MPTCP)
|
||||
if (test->mptcp)
|
||||
proto = IPPROTO_MPTCP;
|
||||
#endif
|
||||
|
||||
if ((s = socket(res->ai_family, SOCK_STREAM, proto)) < 0) {
|
||||
freeaddrinfo(res);
|
||||
i_errno = IESTREAMLISTEN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (test->no_delay) {
|
||||
opt = 1;
|
||||
if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETNODELAY;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// XXX: Setting MSS is very buggy!
|
||||
if ((opt = test->settings->mss)) {
|
||||
if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETMSS;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if ((opt = test->settings->socket_bufsize)) {
|
||||
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
{
|
||||
unsigned int rate = test->settings->rate / 8;
|
||||
if (rate > 0) {
|
||||
if (test->debug) {
|
||||
printf("Setting application pacing to %u\n", rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
opt = 1;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
i_errno = IEREUSEADDR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we got an IPv6 socket, figure out if it should accept IPv4
|
||||
* connections as well. See documentation in netannounce() for
|
||||
* more details.
|
||||
*/
|
||||
#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
|
||||
if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC || test->settings->domain == AF_INET)) {
|
||||
if (test->settings->domain == AF_UNSPEC)
|
||||
opt = 0;
|
||||
else
|
||||
opt = 1;
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
(char *) &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
i_errno = IEV6ONLY;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif /* IPV6_V6ONLY */
|
||||
|
||||
if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESTREAMLISTEN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
if (listen(s, INT_MAX) < 0) {
|
||||
i_errno = IESTREAMLISTEN;
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
test->listener = s;
|
||||
}
|
||||
|
||||
/* Read back and verify the sender socket buffer size */
|
||||
optlen = sizeof(sndbuf_actual);
|
||||
if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
if (test->debug) {
|
||||
printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize);
|
||||
}
|
||||
if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) {
|
||||
i_errno = IESETBUF2;
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read back and verify the receiver socket buffer size */
|
||||
optlen = sizeof(rcvbuf_actual);
|
||||
if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
if (test->debug) {
|
||||
printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize);
|
||||
}
|
||||
if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) {
|
||||
i_errno = IESETBUF2;
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (test->json_output) {
|
||||
cJSON_AddNumberToObject(test->json_start, "sock_bufsize", test->settings->socket_bufsize);
|
||||
cJSON_AddNumberToObject(test->json_start, "sndbuf_actual", sndbuf_actual);
|
||||
cJSON_AddNumberToObject(test->json_start, "rcvbuf_actual", rcvbuf_actual);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/* iperf_tcp_connect
|
||||
*
|
||||
* connect to a TCP stream listener
|
||||
* This function is roughly similar to netdial(), and may indeed have
|
||||
* been derived from it at some point, but it sets many TCP-specific
|
||||
* options between socket creation and connection.
|
||||
*/
|
||||
int
|
||||
iperf_tcp_connect(struct iperf_test *test)
|
||||
{
|
||||
struct addrinfo *server_res;
|
||||
int s, opt;
|
||||
socklen_t optlen;
|
||||
int saved_errno;
|
||||
int rcvbuf_actual, sndbuf_actual;
|
||||
int proto = 0;
|
||||
|
||||
#if defined(HAVE_IPPROTO_MPTCP)
|
||||
if (test->mptcp)
|
||||
proto = IPPROTO_MPTCP;
|
||||
#endif
|
||||
|
||||
s = create_socket(test->settings->domain, SOCK_STREAM, proto, test->bind_address, test->bind_dev, test->bind_port, test->server_hostname, test->server_port, &server_res);
|
||||
if (s < 0) {
|
||||
i_errno = IESTREAMCONNECT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set socket options */
|
||||
if (test->no_delay) {
|
||||
opt = 1;
|
||||
if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETNODELAY;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if ((opt = test->settings->mss)) {
|
||||
if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETMSS;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if ((opt = test->settings->socket_bufsize)) {
|
||||
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#if defined(HAVE_TCP_USER_TIMEOUT)
|
||||
if ((opt = test->settings->snd_timeout)) {
|
||||
if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETUSERTIMEOUT;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_TCP_USER_TIMEOUT */
|
||||
|
||||
/* Read back and verify the sender socket buffer size */
|
||||
optlen = sizeof(sndbuf_actual);
|
||||
if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
if (test->debug) {
|
||||
printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize);
|
||||
}
|
||||
if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) {
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
i_errno = IESETBUF2;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read back and verify the receiver socket buffer size */
|
||||
optlen = sizeof(rcvbuf_actual);
|
||||
if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
if (test->debug) {
|
||||
printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize);
|
||||
}
|
||||
if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) {
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
i_errno = IESETBUF2;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (test->json_output) {
|
||||
cJSON *sock_bufsize_item = cJSON_GetObjectItem(test->json_start, "sock_bufsize");
|
||||
if (sock_bufsize_item == NULL) {
|
||||
cJSON_AddNumberToObject(test->json_start, "sock_bufsize", test->settings->socket_bufsize);
|
||||
}
|
||||
|
||||
cJSON *sndbuf_actual_item = cJSON_GetObjectItem(test->json_start, "sndbuf_actual");
|
||||
if (sndbuf_actual_item == NULL) {
|
||||
cJSON_AddNumberToObject(test->json_start, "sndbuf_actual", sndbuf_actual);
|
||||
}
|
||||
|
||||
cJSON *rcvbuf_actual_item = cJSON_GetObjectItem(test->json_start, "rcvbuf_actual");
|
||||
if (rcvbuf_actual_item == NULL) {
|
||||
cJSON_AddNumberToObject(test->json_start, "rcvbuf_actual", rcvbuf_actual);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HAVE_FLOWLABEL)
|
||||
if (test->settings->flowlabel) {
|
||||
if (server_res->ai_addr->sa_family != AF_INET6) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETFLOW;
|
||||
return -1;
|
||||
} else {
|
||||
struct sockaddr_in6* sa6P = (struct sockaddr_in6*) server_res->ai_addr;
|
||||
char freq_buf[sizeof(struct in6_flowlabel_req)];
|
||||
struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf;
|
||||
int freq_len = sizeof(*freq);
|
||||
|
||||
memset(freq, 0, sizeof(*freq));
|
||||
freq->flr_label = htonl(test->settings->flowlabel & IPV6_FLOWINFO_FLOWLABEL);
|
||||
freq->flr_action = IPV6_FL_A_GET;
|
||||
freq->flr_flags = IPV6_FL_F_CREATE;
|
||||
freq->flr_share = IPV6_FL_S_ANY;
|
||||
memcpy(&freq->flr_dst, &sa6P->sin6_addr, 16);
|
||||
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETFLOW;
|
||||
return -1;
|
||||
}
|
||||
sa6P->sin6_flowinfo = freq->flr_label;
|
||||
|
||||
opt = 1;
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESETFLOW;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_FLOWLABEL */
|
||||
|
||||
#if defined(HAVE_SO_MAX_PACING_RATE)
|
||||
/* If socket pacing is specified try to enable it. */
|
||||
if (test->settings->fqrate) {
|
||||
/* Convert bits per second to bytes per second */
|
||||
uint64_t fqrate = test->settings->fqrate / 8;
|
||||
if (fqrate > 0) {
|
||||
if (test->debug) {
|
||||
printf("Setting fair-queue socket pacing to %"PRIu64"\n", fqrate);
|
||||
}
|
||||
if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) {
|
||||
warning("Unable to set socket pacing");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_SO_MAX_PACING_RATE */
|
||||
{
|
||||
unsigned int rate = test->settings->rate / 8;
|
||||
if (rate > 0) {
|
||||
if (test->debug) {
|
||||
printf("Setting application pacing to %u\n", rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set common socket options */
|
||||
iperf_common_sockopts(test, s);
|
||||
|
||||
if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
i_errno = IESTREAMCONNECT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(server_res);
|
||||
|
||||
/* Send cookie for verification */
|
||||
if (Nwrite(s, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
errno = saved_errno;
|
||||
i_errno = IESENDCOOKIE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#ifndef IPERF_TCP_H
|
||||
#define IPERF_TCP_H
|
||||
|
||||
|
||||
/**
|
||||
* iperf_tcp_accept -- accepts a new TCP connection
|
||||
* on tcp_listener_socket for TCP data and param/result
|
||||
* exchange messages
|
||||
*returns 0 on success
|
||||
*
|
||||
*/
|
||||
int iperf_tcp_accept(struct iperf_test *);
|
||||
|
||||
/**
|
||||
* iperf_tcp_recv -- receives the data for TCP
|
||||
* and the Param/result message exchange
|
||||
*returns state of packet received
|
||||
*
|
||||
*/
|
||||
int iperf_tcp_recv(struct iperf_stream *);
|
||||
|
||||
|
||||
/**
|
||||
* iperf_tcp_send -- sends the client data for TCP
|
||||
* and the Param/result message exchanges
|
||||
* returns: bytes sent
|
||||
*
|
||||
*/
|
||||
int iperf_tcp_send(struct iperf_stream *) /* __attribute__((hot)) */;
|
||||
|
||||
|
||||
int iperf_tcp_listen(struct iperf_test *);
|
||||
|
||||
int iperf_tcp_connect(struct iperf_test *);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2018, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "iperf_config.h"
|
||||
#include "iperf_time.h"
|
||||
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
|
||||
#include <time.h>
|
||||
|
||||
int
|
||||
iperf_time_now(struct iperf_time *time1)
|
||||
{
|
||||
struct timespec ts;
|
||||
int result;
|
||||
result = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (result == 0) {
|
||||
time1->secs = (uint32_t) ts.tv_sec;
|
||||
time1->usecs = (uint32_t) ts.tv_nsec / 1000;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
int
|
||||
iperf_time_now(struct iperf_time *time1)
|
||||
{
|
||||
struct timeval tv;
|
||||
int result;
|
||||
result = gettimeofday(&tv, NULL);
|
||||
time1->secs = tv.tv_sec;
|
||||
time1->usecs = tv.tv_usec;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* iperf_time_add_usecs
|
||||
*
|
||||
* Add a number of microseconds to a iperf_time.
|
||||
*/
|
||||
void
|
||||
iperf_time_add_usecs(struct iperf_time *time1, uint64_t usecs)
|
||||
{
|
||||
uint64_t total_usecs;
|
||||
|
||||
total_usecs = time1->usecs + usecs;
|
||||
time1->secs += total_usecs / 1000000L;
|
||||
time1->usecs = total_usecs % 1000000L;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
iperf_time_in_usecs(struct iperf_time *time)
|
||||
{
|
||||
return time->secs * 1000000LL + time->usecs;
|
||||
}
|
||||
|
||||
double
|
||||
iperf_time_in_secs(struct iperf_time *time)
|
||||
{
|
||||
return time->secs + time->usecs / 1000000.0;
|
||||
}
|
||||
|
||||
/* iperf_time_compare
|
||||
*
|
||||
* Compare two timestamps
|
||||
*
|
||||
* Returns -1 if time1 is earlier, 1 if time1 is later,
|
||||
* or 0 if the timestamps are equal.
|
||||
*/
|
||||
int
|
||||
iperf_time_compare(struct iperf_time *time1, struct iperf_time *time2)
|
||||
{
|
||||
if (time1->secs < time2->secs)
|
||||
return -1;
|
||||
if (time1->secs > time2->secs)
|
||||
return 1;
|
||||
if (time1->usecs < time2->usecs)
|
||||
return -1;
|
||||
if (time1->usecs > time2->usecs)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* iperf_time_diff
|
||||
*
|
||||
* Calculates the time from time2 to time1, assuming time1 is later than time2.
|
||||
* The diff will always be positive, so the return value should be checked
|
||||
* to determine if time1 was earlier than time2.
|
||||
*
|
||||
* Returns 1 if the time1 is less than or equal to time2, otherwise 0.
|
||||
*/
|
||||
int
|
||||
iperf_time_diff(struct iperf_time *time1, struct iperf_time *time2, struct iperf_time *diff)
|
||||
{
|
||||
int past = 0;
|
||||
int cmp = 0;
|
||||
|
||||
cmp = iperf_time_compare(time1, time2);
|
||||
if (cmp == 0) {
|
||||
diff->secs = 0;
|
||||
diff->usecs = 0;
|
||||
past = 1;
|
||||
}
|
||||
else if (cmp == 1) {
|
||||
diff->secs = time1->secs - time2->secs;
|
||||
diff->usecs = time1->usecs;
|
||||
if (diff->usecs < time2->usecs) {
|
||||
diff->secs -= 1;
|
||||
diff->usecs += 1000000;
|
||||
}
|
||||
diff->usecs = diff->usecs - time2->usecs;
|
||||
} else {
|
||||
diff->secs = time2->secs - time1->secs;
|
||||
diff->usecs = time2->usecs;
|
||||
if (diff->usecs < time1->usecs) {
|
||||
diff->secs -= 1;
|
||||
diff->usecs += 1000000;
|
||||
}
|
||||
diff->usecs = diff->usecs - time1->usecs;
|
||||
past = 1;
|
||||
}
|
||||
|
||||
return past;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2018, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#ifndef __IPERF_TIME_H
|
||||
#define __IPERF_TIME_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct iperf_time {
|
||||
uint32_t secs;
|
||||
uint32_t usecs;
|
||||
};
|
||||
|
||||
int iperf_time_now(struct iperf_time *time1);
|
||||
|
||||
void iperf_time_add_usecs(struct iperf_time *time1, uint64_t usecs);
|
||||
|
||||
int iperf_time_compare(struct iperf_time *time1, struct iperf_time *time2);
|
||||
|
||||
int iperf_time_diff(struct iperf_time *time1, struct iperf_time *time2, struct iperf_time *diff);
|
||||
|
||||
uint64_t iperf_time_in_usecs(struct iperf_time *time);
|
||||
|
||||
double iperf_time_in_secs(struct iperf_time *time);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,634 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2022, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "iperf.h"
|
||||
#include "iperf_api.h"
|
||||
#include "iperf_util.h"
|
||||
#include "iperf_udp.h"
|
||||
#include "timer.h"
|
||||
#include "net.h"
|
||||
#include "cjson.h"
|
||||
|
||||
/* iperf_udp_recv
|
||||
*
|
||||
* receives the data for UDP
|
||||
*/
|
||||
int
|
||||
iperf_udp_recv(struct iperf_stream *sp)
|
||||
{
|
||||
uint32_t sec, usec;
|
||||
uint64_t pcount;
|
||||
int r;
|
||||
int size = sp->settings->blksize;
|
||||
int first_packet = 0;
|
||||
double transit = 0, d = 0;
|
||||
struct iperf_time sent_time, arrival_time, temp_time;
|
||||
struct iperf_test *test = sp->test;
|
||||
int sock_opt = 0;
|
||||
|
||||
#if defined(HAVE_MSG_TRUNC)
|
||||
// UDP recv() with MSG_TRUNC reads only the size bytes, but return the length of the full packet
|
||||
if (sp->test->settings->skip_rx_copy) {
|
||||
sock_opt = MSG_TRUNC;
|
||||
size = sizeof(sec) + sizeof(usec) + sizeof(pcount);
|
||||
}
|
||||
#endif /* HAVE_MSG_TRUNC */
|
||||
|
||||
r = Nrecv_no_select(sp->socket, sp->buffer, size, Pudp, sock_opt);
|
||||
|
||||
/*
|
||||
* If we got an error in the read, or if we didn't read anything
|
||||
* because the underlying read(2) got a EAGAIN, then skip packet
|
||||
* processing.
|
||||
*/
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
/* Only count bytes received while we're in the correct state. */
|
||||
if (test->state == TEST_RUNNING) {
|
||||
|
||||
/*
|
||||
* For jitter computation below, it's important to know if this
|
||||
* packet is the first packet received.
|
||||
*/
|
||||
if (sp->result->bytes_received == 0) {
|
||||
first_packet = 1;
|
||||
}
|
||||
|
||||
sp->result->bytes_received += r;
|
||||
sp->result->bytes_received_this_interval += r;
|
||||
|
||||
/* Dig the various counters out of the incoming UDP packet */
|
||||
if (test->udp_counters_64bit) {
|
||||
memcpy(&sec, sp->buffer, sizeof(sec));
|
||||
memcpy(&usec, sp->buffer+4, sizeof(usec));
|
||||
memcpy(&pcount, sp->buffer+8, sizeof(pcount));
|
||||
sec = ntohl(sec);
|
||||
usec = ntohl(usec);
|
||||
pcount = be64toh(pcount);
|
||||
sent_time.secs = sec;
|
||||
sent_time.usecs = usec;
|
||||
}
|
||||
else {
|
||||
uint32_t pc;
|
||||
memcpy(&sec, sp->buffer, sizeof(sec));
|
||||
memcpy(&usec, sp->buffer+4, sizeof(usec));
|
||||
memcpy(&pc, sp->buffer+8, sizeof(pc));
|
||||
sec = ntohl(sec);
|
||||
usec = ntohl(usec);
|
||||
pcount = ntohl(pc);
|
||||
sent_time.secs = sec;
|
||||
sent_time.usecs = usec;
|
||||
}
|
||||
|
||||
if (test->debug_level >= DEBUG_LEVEL_DEBUG)
|
||||
fprintf(stderr, "pcount %" PRIu64 " packet_count %" PRIu64 "\n", pcount, sp->packet_count);
|
||||
|
||||
/*
|
||||
* Try to handle out of order packets. The way we do this
|
||||
* uses a constant amount of storage but might not be
|
||||
* correct in all cases. In particular we seem to have the
|
||||
* assumption that packets can't be duplicated in the network,
|
||||
* because duplicate packets will possibly cause some problems here.
|
||||
*
|
||||
* First figure out if the sequence numbers are going forward.
|
||||
* Note that pcount is the sequence number read from the packet,
|
||||
* and sp->packet_count is the highest sequence number seen so
|
||||
* far (so we're expecting to see the packet with sequence number
|
||||
* sp->packet_count + 1 arrive next).
|
||||
*/
|
||||
if (pcount >= sp->packet_count + 1) {
|
||||
|
||||
/* Forward, but is there a gap in sequence numbers? */
|
||||
if (pcount > sp->packet_count + 1) {
|
||||
/* There's a gap so count that as a loss. */
|
||||
sp->cnt_error += (pcount - 1) - sp->packet_count;
|
||||
if (test->debug_level >= DEBUG_LEVEL_INFO)
|
||||
fprintf(stderr, "LOST %" PRIu64 " PACKETS - received packet %" PRIu64 " but expected sequence %" PRIu64 " on stream %d\n", (pcount - sp->packet_count + 1), pcount, sp->packet_count + 1, sp->socket);
|
||||
}
|
||||
/* Update the highest sequence number seen so far. */
|
||||
sp->packet_count = pcount;
|
||||
} else {
|
||||
|
||||
/*
|
||||
* Sequence number went backward (or was stationary?!?).
|
||||
* This counts as an out-of-order packet.
|
||||
*/
|
||||
sp->outoforder_packets++;
|
||||
|
||||
/*
|
||||
* If we have lost packets, then the fact that we are now
|
||||
* seeing an out-of-order packet offsets a prior sequence
|
||||
* number gap that was counted as a loss. So we can take
|
||||
* away a loss.
|
||||
*/
|
||||
if (sp->cnt_error > 0)
|
||||
sp->cnt_error--;
|
||||
|
||||
/* Log the out-of-order packet */
|
||||
if (test->debug_level >= DEBUG_LEVEL_INFO)
|
||||
fprintf(stderr, "OUT OF ORDER - received packet %" PRIu64 " but expected sequence %" PRIu64 " on stream %d\n", pcount, sp->packet_count + 1, sp->socket);
|
||||
}
|
||||
|
||||
/*
|
||||
* jitter measurement
|
||||
*
|
||||
* This computation is based on RFC 1889 (specifically
|
||||
* sections 6.3.1 and A.8).
|
||||
*
|
||||
* Note that synchronized clocks are not required since
|
||||
* the source packet delta times are known. Also this
|
||||
* computation does not require knowing the round-trip
|
||||
* time.
|
||||
*/
|
||||
iperf_time_now(&arrival_time);
|
||||
|
||||
iperf_time_diff(&arrival_time, &sent_time, &temp_time);
|
||||
transit = iperf_time_in_secs(&temp_time);
|
||||
|
||||
/* Hack to handle the first packet by initializing prev_transit. */
|
||||
if (first_packet)
|
||||
sp->prev_transit = transit;
|
||||
|
||||
d = transit - sp->prev_transit;
|
||||
if (d < 0)
|
||||
d = -d;
|
||||
sp->prev_transit = transit;
|
||||
sp->jitter += (d - sp->jitter) / 16.0;
|
||||
}
|
||||
else {
|
||||
if (test->debug_level >= DEBUG_LEVEL_INFO)
|
||||
printf("Late receive, state = %d\n", test->state);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/* iperf_udp_send
|
||||
*
|
||||
* sends the data for UDP
|
||||
*/
|
||||
int
|
||||
iperf_udp_send(struct iperf_stream *sp)
|
||||
{
|
||||
int r;
|
||||
int size = sp->settings->blksize;
|
||||
struct iperf_time before;
|
||||
|
||||
iperf_time_now(&before);
|
||||
|
||||
++sp->packet_count;
|
||||
|
||||
if (sp->test->udp_counters_64bit) {
|
||||
|
||||
uint32_t sec, usec;
|
||||
uint64_t pcount;
|
||||
|
||||
sec = htonl(before.secs);
|
||||
usec = htonl(before.usecs);
|
||||
pcount = htobe64(sp->packet_count);
|
||||
|
||||
memcpy(sp->buffer, &sec, sizeof(sec));
|
||||
memcpy(sp->buffer+4, &usec, sizeof(usec));
|
||||
memcpy(sp->buffer+8, &pcount, sizeof(pcount));
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
uint32_t sec, usec, pcount;
|
||||
|
||||
sec = htonl(before.secs);
|
||||
usec = htonl(before.usecs);
|
||||
pcount = htonl(sp->packet_count);
|
||||
|
||||
memcpy(sp->buffer, &sec, sizeof(sec));
|
||||
memcpy(sp->buffer+4, &usec, sizeof(usec));
|
||||
memcpy(sp->buffer+8, &pcount, sizeof(pcount));
|
||||
|
||||
}
|
||||
|
||||
r = Nwrite(sp->socket, sp->buffer, size, Pudp);
|
||||
|
||||
if (r <= 0) {
|
||||
--sp->packet_count; /* Don't count messages that no data was sent from them.
|
||||
* Allows "resending" a massage with the same numbering */
|
||||
if (r < 0) {
|
||||
if (r == NET_SOFTERROR && sp->test->debug_level >= DEBUG_LEVEL_INFO)
|
||||
printf("UDP send failed on NET_SOFTERROR. errno=%s\n", strerror(errno));
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
sp->result->bytes_sent += r;
|
||||
sp->result->bytes_sent_this_interval += r;
|
||||
|
||||
if (sp->test->debug_level >= DEBUG_LEVEL_DEBUG)
|
||||
printf("sent %d bytes of %d, total %" PRIu64 "\n", r, sp->settings->blksize, sp->result->bytes_sent);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
/*
|
||||
* The following functions all have to do with managing UDP data sockets.
|
||||
* UDP of course is connectionless, so there isn't really a concept of
|
||||
* setting up a connection, although connect(2) can (and is) used to
|
||||
* bind the remote end of sockets. We need to simulate some of the
|
||||
* connection management that is built-in to TCP so that each side of the
|
||||
* connection knows about each other before the real data transfers begin.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Set and verify socket buffer sizes.
|
||||
* Return 0 if no error, -1 if an error, +1 if socket buffers are
|
||||
* potentially too small to hold a message.
|
||||
*/
|
||||
int
|
||||
iperf_udp_buffercheck(struct iperf_test *test, int s)
|
||||
{
|
||||
int rc = 0;
|
||||
int sndbuf_actual, rcvbuf_actual;
|
||||
|
||||
/*
|
||||
* Set socket buffer size if requested. Do this for both sending and
|
||||
* receiving so that we can cover both normal and --reverse operation.
|
||||
*/
|
||||
int opt;
|
||||
socklen_t optlen;
|
||||
|
||||
if ((opt = test->settings->socket_bufsize)) {
|
||||
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read back and verify the sender socket buffer size */
|
||||
optlen = sizeof(sndbuf_actual);
|
||||
if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) {
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
if (test->debug) {
|
||||
printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize);
|
||||
}
|
||||
if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) {
|
||||
i_errno = IESETBUF2;
|
||||
return -1;
|
||||
}
|
||||
if (test->settings->blksize > sndbuf_actual) {
|
||||
char str[WARN_STR_LEN];
|
||||
snprintf(str, sizeof(str),
|
||||
"Block size %d > sending socket buffer size %d",
|
||||
test->settings->blksize, sndbuf_actual);
|
||||
warning(str);
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
/* Read back and verify the receiver socket buffer size */
|
||||
optlen = sizeof(rcvbuf_actual);
|
||||
if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) {
|
||||
i_errno = IESETBUF;
|
||||
return -1;
|
||||
}
|
||||
if (test->debug) {
|
||||
printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize);
|
||||
}
|
||||
if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) {
|
||||
i_errno = IESETBUF2;
|
||||
return -1;
|
||||
}
|
||||
if (test->settings->blksize > rcvbuf_actual) {
|
||||
char str[WARN_STR_LEN];
|
||||
snprintf(str, sizeof(str),
|
||||
"Block size %d > receiving socket buffer size %d",
|
||||
test->settings->blksize, rcvbuf_actual);
|
||||
warning(str);
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
if (test->json_output) {
|
||||
cJSON *sock_bufsize_item = cJSON_GetObjectItem(test->json_start, "sock_bufsize");
|
||||
if (sock_bufsize_item == NULL) {
|
||||
cJSON_AddNumberToObject(test->json_start, "sock_bufsize", test->settings->socket_bufsize);
|
||||
}
|
||||
|
||||
cJSON *sndbuf_actual_item = cJSON_GetObjectItem(test->json_start, "sndbuf_actual");
|
||||
if (sndbuf_actual_item == NULL) {
|
||||
cJSON_AddNumberToObject(test->json_start, "sndbuf_actual", sndbuf_actual);
|
||||
}
|
||||
|
||||
cJSON *rcvbuf_actual_item = cJSON_GetObjectItem(test->json_start, "rcvbuf_actual");
|
||||
if (rcvbuf_actual_item == NULL) {
|
||||
cJSON_AddNumberToObject(test->json_start, "rcvbuf_actual", rcvbuf_actual);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* iperf_udp_accept
|
||||
*
|
||||
* Accepts a new UDP "connection"
|
||||
*/
|
||||
int
|
||||
iperf_udp_accept(struct iperf_test *test)
|
||||
{
|
||||
struct sockaddr_storage sa_peer;
|
||||
unsigned int buf;
|
||||
socklen_t len;
|
||||
int sz, s;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Get the current outstanding socket. This socket will be used to handle
|
||||
* data transfers and a new "listening" socket will be created.
|
||||
*/
|
||||
s = test->prot_listener;
|
||||
|
||||
/*
|
||||
* Grab the UDP packet sent by the client. From that we can extract the
|
||||
* client's address, and then use that information to bind the remote side
|
||||
* of the socket to the client.
|
||||
*/
|
||||
len = sizeof(sa_peer);
|
||||
if ((sz = recvfrom(test->prot_listener, &buf, sizeof(buf), 0, (struct sockaddr *) &sa_peer, &len)) < 0) {
|
||||
i_errno = IESTREAMACCEPT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (connect(s, (struct sockaddr *) &sa_peer, len) < 0) {
|
||||
i_errno = IESTREAMACCEPT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check and set socket buffer sizes */
|
||||
rc = iperf_udp_buffercheck(test, s);
|
||||
if (rc < 0)
|
||||
/* error */
|
||||
return rc;
|
||||
/*
|
||||
* If the socket buffer was too small, but it was the default
|
||||
* size, then try explicitly setting it to something larger.
|
||||
*/
|
||||
if (rc > 0) {
|
||||
if (test->settings->socket_bufsize == 0) {
|
||||
char str[WARN_STR_LEN];
|
||||
int bufsize = test->settings->blksize + UDP_BUFFER_EXTRA;
|
||||
snprintf(str, sizeof(str), "Increasing socket buffer size to %d",
|
||||
bufsize);
|
||||
warning(str);
|
||||
test->settings->socket_bufsize = bufsize;
|
||||
rc = iperf_udp_buffercheck(test, s);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HAVE_SO_MAX_PACING_RATE)
|
||||
/* If socket pacing is specified, try it. */
|
||||
if (test->settings->fqrate) {
|
||||
/* Convert bits per second to bytes per second */
|
||||
uint64_t fqrate = test->settings->fqrate / 8;
|
||||
if (fqrate > 0) {
|
||||
if (test->debug) {
|
||||
printf("Setting fair-queue socket pacing to %"PRIu64"\n", fqrate);
|
||||
}
|
||||
if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) {
|
||||
warning("Unable to set socket pacing");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_SO_MAX_PACING_RATE */
|
||||
{
|
||||
unsigned int rate = test->settings->rate / 8;
|
||||
if (rate > 0) {
|
||||
if (test->debug) {
|
||||
printf("Setting application pacing to %u\n", rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new "listening" socket to replace the one we were using before.
|
||||
*/
|
||||
FD_CLR(test->prot_listener, &test->read_set); // No control messages from old listener
|
||||
test->prot_listener = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->server_port);
|
||||
if (test->prot_listener < 0) {
|
||||
i_errno = IESTREAMLISTEN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
FD_SET(test->prot_listener, &test->read_set);
|
||||
test->max_fd = (test->max_fd < test->prot_listener) ? test->prot_listener : test->max_fd;
|
||||
|
||||
/* Let the client know we're ready "accept" another UDP "stream" */
|
||||
buf = UDP_CONNECT_REPLY;
|
||||
if (write(s, &buf, sizeof(buf)) < 0) {
|
||||
i_errno = IESTREAMWRITE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* iperf_udp_listen
|
||||
*
|
||||
* Start up a listener for UDP stream connections. Unlike for TCP,
|
||||
* there is no listen(2) for UDP. This socket will however accept
|
||||
* a UDP datagram from a client (indicating the client's presence).
|
||||
*/
|
||||
int
|
||||
iperf_udp_listen(struct iperf_test *test)
|
||||
{
|
||||
int s;
|
||||
|
||||
if ((s = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->server_port)) < 0) {
|
||||
i_errno = IESTREAMLISTEN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The caller will put this value into test->prot_listener.
|
||||
*/
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* iperf_udp_connect
|
||||
*
|
||||
* "Connect" to a UDP stream listener.
|
||||
*/
|
||||
int
|
||||
iperf_udp_connect(struct iperf_test *test)
|
||||
{
|
||||
int s, sz;
|
||||
unsigned int buf;
|
||||
#ifdef SO_RCVTIMEO
|
||||
struct timeval tv;
|
||||
#endif
|
||||
int rc;
|
||||
int i, max_len_wait_for_reply;
|
||||
|
||||
/* Create and bind our local socket. */
|
||||
if ((s = netdial(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->bind_port, test->server_hostname, test->server_port, -1)) < 0) {
|
||||
i_errno = IESTREAMCONNECT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check and set socket buffer sizes */
|
||||
rc = iperf_udp_buffercheck(test, s);
|
||||
if (rc < 0)
|
||||
/* error */
|
||||
return rc;
|
||||
/*
|
||||
* If the socket buffer was too small, but it was the default
|
||||
* size, then try explicitly setting it to something larger.
|
||||
*/
|
||||
if (rc > 0) {
|
||||
if (test->settings->socket_bufsize == 0) {
|
||||
char str[WARN_STR_LEN];
|
||||
int bufsize = test->settings->blksize + UDP_BUFFER_EXTRA;
|
||||
snprintf(str, sizeof(str), "Increasing socket buffer size to %d",
|
||||
bufsize);
|
||||
warning(str);
|
||||
test->settings->socket_bufsize = bufsize;
|
||||
rc = iperf_udp_buffercheck(test, s);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HAVE_SO_MAX_PACING_RATE)
|
||||
/* If socket pacing is available and not disabled, try it. */
|
||||
if (test->settings->fqrate) {
|
||||
/* Convert bits per second to bytes per second */
|
||||
uint64_t fqrate = test->settings->fqrate / 8;
|
||||
if (fqrate > 0) {
|
||||
if (test->debug) {
|
||||
printf("Setting fair-queue socket pacing to %"PRIu64"\n", fqrate);
|
||||
}
|
||||
if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) {
|
||||
warning("Unable to set socket pacing");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_SO_MAX_PACING_RATE */
|
||||
{
|
||||
unsigned int rate = test->settings->rate / 8;
|
||||
if (rate > 0) {
|
||||
if (test->debug) {
|
||||
printf("Setting application pacing to %u\n", rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set common socket options */
|
||||
iperf_common_sockopts(test, s);
|
||||
|
||||
#ifdef SO_RCVTIMEO
|
||||
/* 30 sec timeout for a case when there is a network problem. */
|
||||
tv.tv_sec = 30;
|
||||
tv.tv_usec = 0;
|
||||
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Write a datagram to the UDP stream to let the server know we're here.
|
||||
* The server learns our address by obtaining its peer's address.
|
||||
*/
|
||||
buf = UDP_CONNECT_MSG;
|
||||
if (test->debug) {
|
||||
printf("Sending Connect message to Socket %d\n", s);
|
||||
}
|
||||
if (write(s, &buf, sizeof(buf)) < 0) {
|
||||
// XXX: Should this be changed to IESTREAMCONNECT?
|
||||
i_errno = IESTREAMWRITE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait until the server replies back to us with the "accept" response.
|
||||
*/
|
||||
i = 0;
|
||||
max_len_wait_for_reply = sizeof(buf);
|
||||
if (test->reverse) /* In reverse mode allow few packets to have the "accept" response - to handle out of order packets */
|
||||
max_len_wait_for_reply += MAX_REVERSE_OUT_OF_ORDER_PACKETS * test->settings->blksize;
|
||||
do {
|
||||
if ((sz = recv(s, &buf, sizeof(buf), 0)) < 0) {
|
||||
i_errno = IESTREAMREAD;
|
||||
return -1;
|
||||
}
|
||||
if (test->debug) {
|
||||
printf("Connect received for Socket %d, sz=%d, buf=%x, i=%d, max_len_wait_for_reply=%d\n", s, sz, buf, i, max_len_wait_for_reply);
|
||||
}
|
||||
i += sz;
|
||||
} while (buf != UDP_CONNECT_REPLY && buf != LEGACY_UDP_CONNECT_REPLY && i < max_len_wait_for_reply);
|
||||
|
||||
if (buf != UDP_CONNECT_REPLY && buf != LEGACY_UDP_CONNECT_REPLY) {
|
||||
i_errno = IESTREAMREAD;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/* iperf_udp_init
|
||||
*
|
||||
* initializer for UDP streams in TEST_START
|
||||
*/
|
||||
int
|
||||
iperf_udp_init(struct iperf_test *test)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#ifndef __IPERF_UDP_H
|
||||
#define __IPERF_UDP_H
|
||||
|
||||
|
||||
/**
|
||||
* iperf_udp_recv -- receives the client data for UDP
|
||||
*
|
||||
*returns state of packet received
|
||||
*
|
||||
*/
|
||||
int iperf_udp_recv(struct iperf_stream *);
|
||||
|
||||
/**
|
||||
* iperf_udp_send -- sends the client data for UDP
|
||||
*
|
||||
* returns: bytes sent
|
||||
*
|
||||
*/
|
||||
int iperf_udp_send(struct iperf_stream *) /* __attribute__((hot)) */;
|
||||
|
||||
|
||||
/**
|
||||
* iperf_udp_accept -- accepts a new UDP connection
|
||||
* on udp_listener_socket
|
||||
*returns 0 on success
|
||||
*
|
||||
*/
|
||||
int iperf_udp_accept(struct iperf_test *);
|
||||
|
||||
|
||||
int iperf_udp_listen(struct iperf_test *);
|
||||
|
||||
int iperf_udp_connect(struct iperf_test *);
|
||||
|
||||
int iperf_udp_init(struct iperf_test *);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,660 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, 2016, 2017, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
/* iperf_util.c
|
||||
*
|
||||
* Iperf utility functions
|
||||
*
|
||||
*/
|
||||
#include "iperf_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "cjson.h"
|
||||
#include "iperf.h"
|
||||
#include "iperf_api.h"
|
||||
|
||||
/*
|
||||
* Read entropy from /dev/urandom
|
||||
* Errors are fatal.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int readentropy(void *out, size_t outsize)
|
||||
{
|
||||
static FILE *frandom;
|
||||
static const char rndfile[] = "/dev/urandom";
|
||||
|
||||
if (!outsize) return 0;
|
||||
|
||||
if (frandom == NULL) {
|
||||
frandom = fopen(rndfile, "rb");
|
||||
if (frandom == NULL) {
|
||||
iperf_errexit(NULL, "error - failed to open %s: %s\n",
|
||||
rndfile, strerror(errno));
|
||||
}
|
||||
setbuf(frandom, NULL);
|
||||
}
|
||||
if (fread(out, 1, outsize, frandom) != outsize) {
|
||||
iperf_errexit(NULL, "error - failed to read %s: %s\n",
|
||||
rndfile,
|
||||
feof(frandom) ? "EOF" : strerror(errno));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fills buffer with repeating pattern (similar to pattern that used in iperf2)
|
||||
*/
|
||||
void fill_with_repeating_pattern(void *out, size_t outsize)
|
||||
{
|
||||
size_t i;
|
||||
int counter = 0;
|
||||
char *buf = (char *)out;
|
||||
|
||||
if (!outsize) return;
|
||||
|
||||
for (i = 0; i < outsize; i++) {
|
||||
buf[i] = (char)('0' + counter);
|
||||
if (counter >= 9)
|
||||
counter = 0;
|
||||
else
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* make_cookie
|
||||
*
|
||||
* Generate and return a cookie string
|
||||
*
|
||||
* Iperf uses this function to create test "cookies" which
|
||||
* serve as unique test identifiers. These cookies are also
|
||||
* used for the authentication of stream connections.
|
||||
* Assumes cookie has size (COOKIE_SIZE + 1) char's.
|
||||
*/
|
||||
|
||||
void
|
||||
make_cookie(const char *cookie)
|
||||
{
|
||||
unsigned char *out = (unsigned char*)cookie;
|
||||
size_t pos;
|
||||
static const unsigned char rndchars[] = "abcdefghijklmnopqrstuvwxyz234567";
|
||||
|
||||
readentropy(out, COOKIE_SIZE);
|
||||
for (pos = 0; pos < (COOKIE_SIZE - 1); pos++) {
|
||||
out[pos] = rndchars[out[pos] % (sizeof(rndchars) - 1)];
|
||||
}
|
||||
out[pos] = '\0';
|
||||
}
|
||||
|
||||
|
||||
/* is_closed
|
||||
*
|
||||
* Test if the file descriptor fd is closed.
|
||||
*
|
||||
* Iperf uses this function to test whether a TCP stream socket
|
||||
* is closed, because accepting and denying an invalid connection
|
||||
* in iperf_tcp_accept is not considered an error.
|
||||
*/
|
||||
|
||||
int
|
||||
is_closed(int fd)
|
||||
{
|
||||
struct timeval tv;
|
||||
fd_set readset;
|
||||
|
||||
FD_ZERO(&readset);
|
||||
FD_SET(fd, &readset);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
if (select(fd+1, &readset, NULL, NULL, &tv) < 0) {
|
||||
if (errno == EBADF)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
timeval_to_double(struct timeval * tv)
|
||||
{
|
||||
double d;
|
||||
|
||||
d = tv->tv_sec + tv->tv_usec / 1000000;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
int
|
||||
timeval_equals(struct timeval * tv0, struct timeval * tv1)
|
||||
{
|
||||
if ( tv0->tv_sec == tv1->tv_sec && tv0->tv_usec == tv1->tv_usec )
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
double
|
||||
timeval_diff(struct timeval * tv0, struct timeval * tv1)
|
||||
{
|
||||
double time1, time2;
|
||||
|
||||
time1 = tv0->tv_sec + (tv0->tv_usec / 1000000.0);
|
||||
time2 = tv1->tv_sec + (tv1->tv_usec / 1000000.0);
|
||||
|
||||
time1 = time1 - time2;
|
||||
if (time1 < 0)
|
||||
time1 = -time1;
|
||||
return time1;
|
||||
}
|
||||
|
||||
void
|
||||
cpu_util(double pcpu[3])
|
||||
{
|
||||
static struct iperf_time last;
|
||||
static clock_t clast;
|
||||
static struct rusage rlast;
|
||||
struct iperf_time now, temp_time;
|
||||
clock_t ctemp;
|
||||
struct rusage rtemp;
|
||||
double timediff;
|
||||
double userdiff;
|
||||
double systemdiff;
|
||||
|
||||
if (pcpu == NULL) {
|
||||
iperf_time_now(&last);
|
||||
clast = clock();
|
||||
getrusage(RUSAGE_SELF, &rlast);
|
||||
return;
|
||||
}
|
||||
|
||||
iperf_time_now(&now);
|
||||
ctemp = clock();
|
||||
getrusage(RUSAGE_SELF, &rtemp);
|
||||
|
||||
iperf_time_diff(&now, &last, &temp_time);
|
||||
timediff = iperf_time_in_usecs(&temp_time);
|
||||
|
||||
userdiff = ((rtemp.ru_utime.tv_sec * 1000000.0 + rtemp.ru_utime.tv_usec) -
|
||||
(rlast.ru_utime.tv_sec * 1000000.0 + rlast.ru_utime.tv_usec));
|
||||
systemdiff = ((rtemp.ru_stime.tv_sec * 1000000.0 + rtemp.ru_stime.tv_usec) -
|
||||
(rlast.ru_stime.tv_sec * 1000000.0 + rlast.ru_stime.tv_usec));
|
||||
|
||||
pcpu[0] = (((ctemp - clast) * 1000000.0 / CLOCKS_PER_SEC) / timediff) * 100;
|
||||
pcpu[1] = (userdiff / timediff) * 100;
|
||||
pcpu[2] = (systemdiff / timediff) * 100;
|
||||
}
|
||||
|
||||
const char *
|
||||
get_system_info(void)
|
||||
{
|
||||
static char buf[1024];
|
||||
struct utsname uts;
|
||||
|
||||
memset(buf, 0, 1024);
|
||||
uname(&uts);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s %s %s %s %s", uts.sysname, uts.nodename,
|
||||
uts.release, uts.version, uts.machine);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
get_optional_features(void)
|
||||
{
|
||||
static char features[1024];
|
||||
unsigned int numfeatures = 0;
|
||||
|
||||
snprintf(features, sizeof(features), "Optional features available: ");
|
||||
|
||||
#if defined(HAVE_CPU_AFFINITY)
|
||||
if (numfeatures > 0) {
|
||||
strncat(features, ", ",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
}
|
||||
strncat(features, "CPU affinity setting",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
numfeatures++;
|
||||
#endif /* HAVE_CPU_AFFINITY */
|
||||
|
||||
#if defined(HAVE_FLOWLABEL)
|
||||
if (numfeatures > 0) {
|
||||
strncat(features, ", ",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
}
|
||||
strncat(features, "IPv6 flow label",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
numfeatures++;
|
||||
#endif /* HAVE_FLOWLABEL */
|
||||
|
||||
#if defined(HAVE_SCTP_H)
|
||||
if (numfeatures > 0) {
|
||||
strncat(features, ", ",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
}
|
||||
strncat(features, "SCTP",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
numfeatures++;
|
||||
#endif /* HAVE_SCTP_H */
|
||||
|
||||
#if defined(HAVE_TCP_CONGESTION)
|
||||
if (numfeatures > 0) {
|
||||
strncat(features, ", ",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
}
|
||||
strncat(features, "TCP congestion algorithm setting",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
numfeatures++;
|
||||
#endif /* HAVE_TCP_CONGESTION */
|
||||
|
||||
#if defined(HAVE_SENDFILE)
|
||||
if (numfeatures > 0) {
|
||||
strncat(features, ", ",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
}
|
||||
strncat(features, "sendfile / zerocopy",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
numfeatures++;
|
||||
#endif /* HAVE_SENDFILE */
|
||||
|
||||
#if defined(HAVE_SO_MAX_PACING_RATE)
|
||||
if (numfeatures > 0) {
|
||||
strncat(features, ", ",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
}
|
||||
strncat(features, "socket pacing",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
numfeatures++;
|
||||
#endif /* HAVE_SO_MAX_PACING_RATE */
|
||||
|
||||
#if defined(HAVE_SSL)
|
||||
if (numfeatures > 0) {
|
||||
strncat(features, ", ",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
}
|
||||
strncat(features, "authentication",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
numfeatures++;
|
||||
#endif /* HAVE_SSL */
|
||||
|
||||
#if defined(HAVE_SO_BINDTODEVICE)
|
||||
if (numfeatures > 0) {
|
||||
strncat(features, ", ",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
}
|
||||
strncat(features, "bind to device",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
numfeatures++;
|
||||
#endif /* HAVE_SO_BINDTODEVICE */
|
||||
|
||||
#if defined(HAVE_DONT_FRAGMENT)
|
||||
if (numfeatures > 0) {
|
||||
strncat(features, ", ",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
}
|
||||
strncat(features, "support IPv4 don't fragment",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
numfeatures++;
|
||||
#endif /* HAVE_DONT_FRAGMENT */
|
||||
|
||||
#if defined(HAVE_PTHREAD)
|
||||
if (numfeatures > 0) {
|
||||
strncat(features, ", ",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
}
|
||||
strncat(features, "POSIX threads",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
numfeatures++;
|
||||
#endif /* HAVE_PTHREAD */
|
||||
|
||||
if (numfeatures == 0) {
|
||||
strncat(features, "None",
|
||||
sizeof(features) - strlen(features) - 1);
|
||||
}
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
/* Helper routine for building cJSON objects in a printf-like manner.
|
||||
**
|
||||
** Sample call:
|
||||
** j = iperf_json_printf("foo: %b bar: %d bletch: %f eep: %s", b, i, f, s);
|
||||
**
|
||||
** The four formatting characters and the types they expect are:
|
||||
** %b boolean int
|
||||
** %d integer int64_t
|
||||
** %f floating point double
|
||||
** %s string char *
|
||||
** If the values you're passing in are not these exact types, you must
|
||||
** cast them, there is no automatic type coercion/widening here.
|
||||
**
|
||||
** The colons mark the end of field names, and blanks are ignored.
|
||||
**
|
||||
** This routine is not particularly robust, but it's not part of the API,
|
||||
** it's just for internal iperf3 use.
|
||||
*/
|
||||
cJSON*
|
||||
iperf_json_printf(const char *format, ...)
|
||||
{
|
||||
cJSON* o;
|
||||
va_list argp;
|
||||
const char *cp;
|
||||
char name[100];
|
||||
char* np;
|
||||
cJSON* j;
|
||||
|
||||
o = cJSON_CreateObject();
|
||||
if (o == NULL)
|
||||
return NULL;
|
||||
va_start(argp, format);
|
||||
np = name;
|
||||
for (cp = format; *cp != '\0'; ++cp) {
|
||||
switch (*cp) {
|
||||
case ' ':
|
||||
break;
|
||||
case ':':
|
||||
*np = '\0';
|
||||
break;
|
||||
case '%':
|
||||
++cp;
|
||||
switch (*cp) {
|
||||
case 'b':
|
||||
j = cJSON_CreateBool(va_arg(argp, int));
|
||||
break;
|
||||
case 'd':
|
||||
j = cJSON_CreateNumber(va_arg(argp, int64_t));
|
||||
break;
|
||||
case 'f':
|
||||
j = cJSON_CreateNumber(va_arg(argp, double));
|
||||
break;
|
||||
case 's':
|
||||
j = cJSON_CreateString(va_arg(argp, char *));
|
||||
break;
|
||||
default:
|
||||
va_end(argp);
|
||||
return NULL;
|
||||
}
|
||||
if (j == NULL) {
|
||||
va_end(argp);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddItemToObject(o, name, j);
|
||||
np = name;
|
||||
break;
|
||||
default:
|
||||
*np++ = *cp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end(argp);
|
||||
return o;
|
||||
}
|
||||
|
||||
/********************** cJSON GetObjectItem w/ Type Helper ********************/
|
||||
cJSON * iperf_cJSON_GetObjectItemType(cJSON * j, char * item_string, int expected_type){
|
||||
cJSON *j_p;
|
||||
if((j_p = cJSON_GetObjectItem(j, item_string)) != NULL)
|
||||
switch(expected_type){
|
||||
case cJSON_True:
|
||||
if(cJSON_IsBool(j_p))
|
||||
return j_p;
|
||||
else
|
||||
iperf_err(NULL, "iperf_cJSON_GetObjectItemType mismatch %s", item_string);
|
||||
break;
|
||||
case cJSON_String:
|
||||
if(cJSON_IsString(j_p))
|
||||
return j_p;
|
||||
else
|
||||
iperf_err(NULL, "iperf_cJSON_GetObjectItemType mismatch %s", item_string);
|
||||
break;
|
||||
case cJSON_Number:
|
||||
if(cJSON_IsNumber(j_p))
|
||||
return j_p;
|
||||
else
|
||||
iperf_err(NULL, "iperf_cJSON_GetObjectItemType mismatch %s", item_string);
|
||||
break;
|
||||
case cJSON_Array:
|
||||
if(cJSON_IsArray(j_p))
|
||||
return j_p;
|
||||
else
|
||||
iperf_err(NULL, "iperf_cJSON_GetObjectItemType mismatch %s", item_string);
|
||||
break;
|
||||
default:
|
||||
iperf_err(NULL, "unsupported type");
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Debugging routine to dump out an fd_set. */
|
||||
void
|
||||
iperf_dump_fdset(FILE *fp, const char *str, int nfds, fd_set *fds)
|
||||
{
|
||||
int fd;
|
||||
int comma;
|
||||
|
||||
fprintf(fp, "%s: [", str);
|
||||
comma = 0;
|
||||
for (fd = 0; fd < nfds; ++fd) {
|
||||
if (FD_ISSET(fd, fds)) {
|
||||
if (comma)
|
||||
fprintf(fp, ", ");
|
||||
fprintf(fp, "%d", fd);
|
||||
comma = 1;
|
||||
}
|
||||
}
|
||||
fprintf(fp, "]\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* daemon(3) implementation for systems lacking one.
|
||||
* Cobbled together from various daemon(3) implementations,
|
||||
* not intended to be general-purpose. */
|
||||
#ifndef HAVE_DAEMON
|
||||
int daemon(int nochdir, int noclose)
|
||||
{
|
||||
pid_t pid = 0;
|
||||
pid_t sid = 0;
|
||||
int fd;
|
||||
|
||||
/*
|
||||
* Ignore any possible SIGHUP when the parent process exits.
|
||||
* Note that the iperf3 server process will eventually install
|
||||
* its own signal handler for SIGHUP, so we can be a little
|
||||
* sloppy about not restoring the prior value. This does not
|
||||
* generalize.
|
||||
*/
|
||||
signal(SIGHUP, SIG_IGN);
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (pid > 0) {
|
||||
/* Use _exit() to avoid doing atexit() stuff. */
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
sid = setsid();
|
||||
if (sid < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fork again to avoid becoming a session leader.
|
||||
* This might only matter on old SVr4-derived OSs.
|
||||
* Note in particular that glibc and FreeBSD libc
|
||||
* only fork once.
|
||||
*/
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
return -1;
|
||||
} else if (pid != 0) {
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
if (!nochdir) {
|
||||
chdir("/");
|
||||
}
|
||||
|
||||
if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
if (fd > 2) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
#endif /* HAVE_DAEMON */
|
||||
|
||||
/* Compatibility version of getline(3) for systems that don't have it.. */
|
||||
#ifndef HAVE_GETLINE
|
||||
/* The following code adopted from NetBSD's getline.c, which is: */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2011 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Christos Zoulas.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
ssize_t
|
||||
getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
|
||||
{
|
||||
char *ptr, *eptr;
|
||||
|
||||
|
||||
if (*buf == NULL || *bufsiz == 0) {
|
||||
*bufsiz = BUFSIZ;
|
||||
if ((*buf = malloc(*bufsiz)) == NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (ptr = *buf, eptr = *buf + *bufsiz;;) {
|
||||
int c = fgetc(fp);
|
||||
if (c == -1) {
|
||||
if (feof(fp)) {
|
||||
ssize_t diff = (ssize_t)(ptr - *buf);
|
||||
if (diff != 0) {
|
||||
*ptr = '\0';
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
*ptr++ = c;
|
||||
if (c == delimiter) {
|
||||
*ptr = '\0';
|
||||
return ptr - *buf;
|
||||
}
|
||||
if (ptr + 2 >= eptr) {
|
||||
char *nbuf;
|
||||
size_t nbufsiz = *bufsiz * 2;
|
||||
ssize_t d = ptr - *buf;
|
||||
if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
|
||||
return -1;
|
||||
*buf = nbuf;
|
||||
*bufsiz = nbufsiz;
|
||||
eptr = nbuf + nbufsiz;
|
||||
ptr = nbuf + d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t
|
||||
getline(char **buf, size_t *bufsiz, FILE *fp)
|
||||
{
|
||||
return getdelim(buf, bufsiz, '\n', fp);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Translate numeric State to text - for debugging pupposes */
|
||||
char *
|
||||
state_to_text(signed char state)
|
||||
{
|
||||
char *txt;
|
||||
|
||||
switch (state) {
|
||||
case 0: txt = "Test reset"; break;
|
||||
case TEST_START: txt = "TEST_START - starting a new test"; break;
|
||||
case TEST_RUNNING: txt = "TEST_RUNNING"; break;
|
||||
case TEST_END: txt = "TEST_END"; break;
|
||||
case PARAM_EXCHANGE: txt = "PARAM_EXCHANGE - Client to Server Parameters Exchange"; break;
|
||||
case CREATE_STREAMS: txt = "CREATE_STREAMS"; break;
|
||||
case SERVER_TERMINATE: txt = "SERVER_TERMINATE"; break;
|
||||
case CLIENT_TERMINATE: txt = "CLIENT_TERMINATE"; break;
|
||||
case EXCHANGE_RESULTS: txt = "EXCHANGE_RESULTS"; break;
|
||||
case DISPLAY_RESULTS: txt = "DISPLAY_RESULTS"; break;
|
||||
case IPERF_START: txt = "IPERF_START - waiting for a new test"; break;
|
||||
case IPERF_DONE: txt = "IPERF_DONE"; break;
|
||||
case ACCESS_DENIED: txt = "ACCESS_DENIED - Server is busy"; break;
|
||||
case SERVER_ERROR: txt = "SERVER_ERROR"; break;
|
||||
default: txt = "Unknown State";
|
||||
}
|
||||
|
||||
return txt;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2017, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#ifndef __IPERF_UTIL_H
|
||||
#define __IPERF_UTIL_H
|
||||
|
||||
#include "iperf_config.h"
|
||||
#include "cjson.h"
|
||||
#include <sys/select.h>
|
||||
#include <stddef.h>
|
||||
|
||||
int readentropy(void *out, size_t outsize);
|
||||
|
||||
void fill_with_repeating_pattern(void *out, size_t outsize);
|
||||
|
||||
void make_cookie(char *);
|
||||
|
||||
int is_closed(int);
|
||||
|
||||
double timeval_to_double(struct timeval *tv);
|
||||
|
||||
int timeval_equals(struct timeval *tv0, struct timeval *tv1);
|
||||
|
||||
double timeval_diff(struct timeval *tv0, struct timeval *tv1);
|
||||
|
||||
void cpu_util(double pcpu[3]);
|
||||
|
||||
const char* get_system_info(void);
|
||||
|
||||
const char* get_optional_features(void);
|
||||
|
||||
cJSON* iperf_json_printf(const char *format, ...);
|
||||
cJSON * iperf_cJSON_GetObjectItemType(cJSON * j_p, char * item_string, int expected_type);
|
||||
|
||||
void iperf_dump_fdset(FILE *fp, const char *str, int nfds, fd_set *fds);
|
||||
|
||||
#ifndef HAVE_DAEMON
|
||||
extern int daemon(int nochdir, int noclose);
|
||||
#endif /* HAVE_DAEMON */
|
||||
|
||||
#ifndef HAVE_GETLINE
|
||||
ssize_t getline(char **buf, size_t *bufsiz, FILE *fp);
|
||||
#endif /* HAVE_GETLINE */
|
||||
|
||||
char * state_to_text(signed char state);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,115 @@
|
||||
.TH LIBIPERF 3 "January 2022" ESnet "User Manuals"
|
||||
.SH NAME
|
||||
libiperf \- API for iperf3 network throughput tester
|
||||
|
||||
.SH SYNOPSIS
|
||||
#include <iperf_api.h>
|
||||
.br
|
||||
\-liperf
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
Libiperf gives you access to all the functionality of the iperf3
|
||||
network testing tool.
|
||||
You can build it directly into your own program, instead of having
|
||||
to run it as a shell command.
|
||||
|
||||
.SH CALLS
|
||||
Initialization / termination:
|
||||
.nf
|
||||
struct iperf_test *iperf_new_test();
|
||||
int iperf_defaults(struct iperf_test *t);
|
||||
void iperf_free_test(struct iperf_test *t);
|
||||
.fi
|
||||
Setting test parameters:
|
||||
.nf
|
||||
void iperf_set_test_role( struct iperf_test *pt, char role );
|
||||
void iperf_set_test_bind_address( struct iperf_test *t, char *bind_address );
|
||||
void iperf_set_test_bind_dev( struct iperf_test *t, char *bind_dev );
|
||||
void iperf_set_test_server_hostname( struct iperf_test *t, char *server_host );
|
||||
void iperf_set_test_server_port( struct iperf_test *t, int server_port );
|
||||
void iperf_set_test_duration( struct iperf_test *t, int duration );
|
||||
void iperf_set_test_blksize( struct iperf_test *t, int blksize );
|
||||
void iperf_set_test_num_streams( struct iperf_test *t, int num_streams );
|
||||
void iperf_set_test_json_output( struct iperf_test *t, int json_output );
|
||||
void iperf_set_test_json_stream( struct iperf_test *t, int json_stream );
|
||||
int iperf_has_zerocopy( void );
|
||||
void iperf_set_test_zerocopy( struct iperf_test* t, int zerocopy );
|
||||
void iperf_set_test_tos( struct iperf_test* t, int tos );
|
||||
.fi
|
||||
Authentication functions:
|
||||
.nf
|
||||
void iperf_set_test_client_username(struct iperf_test *ipt, char *client_username)
|
||||
void iperf_set_test_client_password(struct iperf_test *ipt, char *client_password)
|
||||
void iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, char *client_rsa_pubkey_base64)
|
||||
.fi
|
||||
Running a test:
|
||||
.nf
|
||||
int iperf_run_client(struct iperf_test *);
|
||||
int iperf_run_server(struct iperf_test *);
|
||||
void iperf_reset_test(struct iperf_test *);
|
||||
.fi
|
||||
Output:
|
||||
.nf
|
||||
FILE *iperf_get_test_outfile(struct iperf_test *);
|
||||
char* iperf_get_test_json_output_string(struct iperf_test *);
|
||||
.fi
|
||||
Error reporting:
|
||||
.nf
|
||||
void iperf_err(struct iperf_test *t, const char *format, ...);
|
||||
char *iperf_strerror(int);
|
||||
extern int i_errno;
|
||||
.fi
|
||||
This is not a complete list of the available calls.
|
||||
See the include file for more.
|
||||
|
||||
.SH EXAMPLES
|
||||
Here's some sample code that runs an iperf client:
|
||||
.nf
|
||||
struct iperf_test *test;
|
||||
test = iperf_new_test();
|
||||
if ( test == NULL ) {
|
||||
fprintf( stderr, "%s: failed to create test\n", argv0 );
|
||||
exit( EXIT_FAILURE );
|
||||
}
|
||||
iperf_defaults( test );
|
||||
iperf_set_test_role( test, 'c' );
|
||||
iperf_set_test_server_hostname( test, host );
|
||||
iperf_set_test_server_port( test, port );
|
||||
if ( iperf_run_client( test ) < 0 ) {
|
||||
fprintf( stderr, "%s: error - %s\n", argv0, iperf_strerror( i_errno ) );
|
||||
exit( EXIT_FAILURE );
|
||||
}
|
||||
iperf_free_test( test );
|
||||
.fi
|
||||
And here's a server:
|
||||
.nf
|
||||
struct iperf_test *test;
|
||||
test = iperf_new_test();
|
||||
if ( test == NULL ) {
|
||||
fprintf( stderr, "%s: failed to create test\n", argv0 );
|
||||
exit( EXIT_FAILURE );
|
||||
}
|
||||
iperf_defaults( test );
|
||||
iperf_set_test_role( test, 's' );
|
||||
iperf_set_test_server_port( test, port );
|
||||
for (;;) {
|
||||
if ( iperf_run_server( test ) < 0 )
|
||||
fprintf( stderr, "%s: error - %s\n\n", argv0, iperf_strerror( i_errn
|
||||
o ) );
|
||||
iperf_reset_test( test );
|
||||
}
|
||||
iperf_free_test( test );
|
||||
.fi
|
||||
These are not complete programs, just excerpts.
|
||||
The full runnable source code can be found in the examples subdirectory
|
||||
of the iperf3 source tree.
|
||||
|
||||
.SH AUTHORS
|
||||
A list of the contributors to iperf3 can be found within the
|
||||
documentation located at
|
||||
\fChttps://software.es.net/iperf/dev.html#authors\fR.
|
||||
|
||||
.SH "SEE ALSO"
|
||||
iperf3(1),
|
||||
https://software.es.net/iperf/
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2023, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#include "iperf_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include "iperf.h"
|
||||
#include "iperf_api.h"
|
||||
#include "iperf_util.h"
|
||||
#include "iperf_locale.h"
|
||||
#include "net.h"
|
||||
#include "units.h"
|
||||
|
||||
|
||||
static int run(struct iperf_test *test);
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct iperf_test *test;
|
||||
|
||||
/*
|
||||
* Atomics check. We prefer to have atomic types (which is
|
||||
* basically on any compiler supporting C11 or better). If we
|
||||
* don't have them, we try to approximate the type we need with a
|
||||
* regular integer, but complain if they're not lock-free. We only
|
||||
* know how to check this on GCC. GCC on CentOS 7 / RHEL 7 is the
|
||||
* targeted use case for these check.
|
||||
*/
|
||||
#ifndef HAVE_STDATOMIC_H
|
||||
#ifdef __GNUC__
|
||||
if (! __atomic_always_lock_free (sizeof (u_int64_t), 0)) {
|
||||
#endif // __GNUC__
|
||||
fprintf(stderr, "Warning: Cannot guarantee lock-free operation with 64-bit data types\n");
|
||||
#ifdef __GNUC__
|
||||
}
|
||||
#endif // __GNUC__
|
||||
#endif // HAVE_STDATOMIC_H
|
||||
|
||||
// XXX: Setting the process affinity requires root on most systems.
|
||||
// Is this a feature we really need?
|
||||
#ifdef TEST_PROC_AFFINITY
|
||||
/* didn't seem to work.... */
|
||||
/*
|
||||
* increasing the priority of the process to minimise packet generation
|
||||
* delay
|
||||
*/
|
||||
int rc = setpriority(PRIO_PROCESS, 0, -15);
|
||||
|
||||
if (rc < 0) {
|
||||
perror("setpriority:");
|
||||
fprintf(stderr, "setting priority to valid level\n");
|
||||
rc = setpriority(PRIO_PROCESS, 0, 0);
|
||||
}
|
||||
|
||||
/* setting the affinity of the process */
|
||||
cpu_set_t cpu_set;
|
||||
int affinity = -1;
|
||||
int ncores = 1;
|
||||
|
||||
sched_getaffinity(0, sizeof(cpu_set_t), &cpu_set);
|
||||
if (errno)
|
||||
perror("couldn't get affinity:");
|
||||
|
||||
if ((ncores = sysconf(_SC_NPROCESSORS_CONF)) <= 0)
|
||||
err("sysconf: couldn't get _SC_NPROCESSORS_CONF");
|
||||
|
||||
CPU_ZERO(&cpu_set);
|
||||
CPU_SET(affinity, &cpu_set);
|
||||
if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set) != 0)
|
||||
err("couldn't change CPU affinity");
|
||||
#endif
|
||||
|
||||
test = iperf_new_test();
|
||||
if (!test)
|
||||
iperf_errexit(NULL, "create new test error - %s", iperf_strerror(i_errno));
|
||||
iperf_defaults(test); /* sets defaults */
|
||||
|
||||
if (iperf_parse_arguments(test, argc, argv) < 0) {
|
||||
iperf_err(test, "parameter error - %s", iperf_strerror(i_errno));
|
||||
fprintf(stderr, "\n");
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (run(test) < 0)
|
||||
iperf_errexit(test, "error - %s", iperf_strerror(i_errno));
|
||||
|
||||
iperf_free_test(test);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static jmp_buf sigend_jmp_buf;
|
||||
static int signed_sig;
|
||||
|
||||
static void __attribute__ ((noreturn))
|
||||
sigend_handler(int sig)
|
||||
{
|
||||
signed_sig = sig;
|
||||
longjmp(sigend_jmp_buf, 1);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
static int
|
||||
run(struct iperf_test *test)
|
||||
{
|
||||
/* Termination signals. */
|
||||
iperf_catch_sigend(sigend_handler);
|
||||
if (setjmp(sigend_jmp_buf))
|
||||
iperf_got_sigend(test, signed_sig);
|
||||
|
||||
/* Ignore SIGPIPE to simplify error handling */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
switch (test->role) {
|
||||
case 's':
|
||||
if (test->daemon) {
|
||||
int rc;
|
||||
rc = daemon(1, 0);
|
||||
if (rc < 0) {
|
||||
i_errno = IEDAEMON;
|
||||
iperf_errexit(test, "error - %s", iperf_strerror(i_errno));
|
||||
}
|
||||
}
|
||||
if (iperf_create_pidfile(test) < 0) {
|
||||
i_errno = IEPIDFILE;
|
||||
iperf_errexit(test, "error - %s", iperf_strerror(i_errno));
|
||||
}
|
||||
for (;;) {
|
||||
int rc;
|
||||
rc = iperf_run_server(test);
|
||||
test->server_last_run_rc = rc;
|
||||
if (rc < 0) {
|
||||
iperf_err(test, "error - %s", iperf_strerror(i_errno));
|
||||
if (test->json_output) {
|
||||
if (iperf_json_finish(test) < 0)
|
||||
return -1;
|
||||
}
|
||||
iflush(test);
|
||||
|
||||
if (rc < -1) {
|
||||
iperf_errexit(test, "exiting");
|
||||
}
|
||||
}
|
||||
iperf_reset_test(test);
|
||||
if (iperf_get_test_one_off(test) && rc != 2) {
|
||||
/* Authentication failure doesn't count for 1-off test */
|
||||
if (rc < 0 && i_errno == IEAUTHTEST) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
iperf_delete_pidfile(test);
|
||||
break;
|
||||
case 'c':
|
||||
if (iperf_create_pidfile(test) < 0) {
|
||||
i_errno = IEPIDFILE;
|
||||
iperf_errexit(test, "error - %s", iperf_strerror(i_errno));
|
||||
}
|
||||
if (iperf_run_client(test) < 0)
|
||||
iperf_errexit(test, "error - %s", iperf_strerror(i_errno));
|
||||
iperf_delete_pidfile(test);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
|
||||
iperf_catch_sigend(SIG_DFL);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,664 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014-2023, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#include "iperf_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <assert.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef HAVE_SENDFILE
|
||||
#ifdef linux
|
||||
#include <sys/sendfile.h>
|
||||
#else
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/uio.h>
|
||||
#else
|
||||
#if defined(__APPLE__) && defined(__MACH__) /* OS X */
|
||||
#include <AvailabilityMacros.h>
|
||||
#if defined(MAC_OS_X_VERSION_10_6)
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif /* HAVE_SENDFILE */
|
||||
|
||||
#ifdef HAVE_POLL_H
|
||||
#include <poll.h>
|
||||
#endif /* HAVE_POLL_H */
|
||||
|
||||
#include "iperf.h"
|
||||
#include "iperf_util.h"
|
||||
#include "net.h"
|
||||
#include "timer.h"
|
||||
|
||||
static int nread_read_timeout = 10;
|
||||
static int nread_overall_timeout = 30;
|
||||
|
||||
/*
|
||||
* Declaration of gerror in iperf_error.c. Most other files in iperf3 can get this
|
||||
* by including "iperf.h", but net.c lives "below" this layer. Clearly the
|
||||
* presence of this declaration is a sign we need to revisit this layering.
|
||||
*/
|
||||
extern int gerror;
|
||||
|
||||
/*
|
||||
* timeout_connect adapted from netcat, via OpenBSD and FreeBSD
|
||||
* Copyright (c) 2001 Eric Jackson <ericj@monkey.org>
|
||||
*/
|
||||
int
|
||||
timeout_connect(int s, const struct sockaddr *name, socklen_t namelen,
|
||||
int timeout)
|
||||
{
|
||||
struct pollfd pfd;
|
||||
socklen_t optlen;
|
||||
int flags, optval;
|
||||
int ret;
|
||||
|
||||
flags = 0;
|
||||
if (timeout != -1) {
|
||||
flags = fcntl(s, F_GETFL, 0);
|
||||
if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((ret = connect(s, name, namelen)) != 0 && errno == EINPROGRESS) {
|
||||
pfd.fd = s;
|
||||
pfd.events = POLLOUT;
|
||||
if ((ret = poll(&pfd, 1, timeout)) == 1) {
|
||||
optlen = sizeof(optval);
|
||||
if ((ret = getsockopt(s, SOL_SOCKET, SO_ERROR,
|
||||
&optval, &optlen)) == 0) {
|
||||
errno = optval;
|
||||
ret = optval == 0 ? 0 : -1;
|
||||
}
|
||||
} else if (ret == 0) {
|
||||
errno = ETIMEDOUT;
|
||||
ret = -1;
|
||||
} else
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (timeout != -1 && fcntl(s, F_SETFL, flags) == -1)
|
||||
ret = -1;
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/* netdial and netannouce code comes from libtask: http://swtch.com/libtask/
|
||||
* Copyright: http://swtch.com/libtask/COPYRIGHT
|
||||
*/
|
||||
|
||||
/* create a socket */
|
||||
int
|
||||
create_socket(int domain, int type, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, struct addrinfo **server_res_out)
|
||||
{
|
||||
struct addrinfo hints, *local_res = NULL, *server_res = NULL;
|
||||
int s, saved_errno;
|
||||
char portstr[6];
|
||||
|
||||
if (local) {
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = domain;
|
||||
hints.ai_socktype = type;
|
||||
if ((gerror = getaddrinfo(local, NULL, &hints, &local_res)) != 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = domain;
|
||||
hints.ai_socktype = type;
|
||||
snprintf(portstr, sizeof(portstr), "%d", port);
|
||||
if ((gerror = getaddrinfo(server, portstr, &hints, &server_res)) != 0) {
|
||||
if (local)
|
||||
freeaddrinfo(local_res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = socket(server_res->ai_family, type, proto);
|
||||
if (s < 0) {
|
||||
if (local)
|
||||
freeaddrinfo(local_res);
|
||||
freeaddrinfo(server_res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bind_dev) {
|
||||
#if defined(HAVE_SO_BINDTODEVICE)
|
||||
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
|
||||
bind_dev, IFNAMSIZ) < 0)
|
||||
#endif // HAVE_SO_BINDTODEVICE
|
||||
{
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(local_res);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Bind the local address if given a name (with or without --cport) */
|
||||
if (local) {
|
||||
if (local_port) {
|
||||
struct sockaddr_in *lcladdr;
|
||||
lcladdr = (struct sockaddr_in *)local_res->ai_addr;
|
||||
lcladdr->sin_port = htons(local_port);
|
||||
}
|
||||
|
||||
if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(local_res);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
freeaddrinfo(local_res);
|
||||
}
|
||||
/* No local name, but --cport given */
|
||||
else if (local_port) {
|
||||
size_t addrlen;
|
||||
struct sockaddr_storage lcl;
|
||||
|
||||
/* IPv4 */
|
||||
if (server_res->ai_family == AF_INET) {
|
||||
struct sockaddr_in *lcladdr = (struct sockaddr_in *) &lcl;
|
||||
lcladdr->sin_family = AF_INET;
|
||||
lcladdr->sin_port = htons(local_port);
|
||||
lcladdr->sin_addr.s_addr = INADDR_ANY;
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
}
|
||||
/* IPv6 */
|
||||
else if (server_res->ai_family == AF_INET6) {
|
||||
struct sockaddr_in6 *lcladdr = (struct sockaddr_in6 *) &lcl;
|
||||
lcladdr->sin6_family = AF_INET6;
|
||||
lcladdr->sin6_port = htons(local_port);
|
||||
lcladdr->sin6_addr = in6addr_any;
|
||||
addrlen = sizeof(struct sockaddr_in6);
|
||||
}
|
||||
/* Unknown protocol */
|
||||
else {
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = EAFNOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
*server_res_out = server_res;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* make connection to server */
|
||||
int
|
||||
netdial(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, int timeout)
|
||||
{
|
||||
struct addrinfo *server_res = NULL;
|
||||
int s, saved_errno;
|
||||
|
||||
s = create_socket(domain, proto, 0, local, bind_dev, local_port, server, port, &server_res);
|
||||
if (s < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (timeout_connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen, timeout) < 0 && errno != EINPROGRESS) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(server_res);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(server_res);
|
||||
return s;
|
||||
}
|
||||
|
||||
/***************************************************************/
|
||||
|
||||
int
|
||||
netannounce(int domain, int proto, const char *local, const char *bind_dev, int port)
|
||||
{
|
||||
struct addrinfo hints, *res;
|
||||
char portstr[6];
|
||||
int s, opt, saved_errno;
|
||||
|
||||
snprintf(portstr, 6, "%d", port);
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
/*
|
||||
* If binding to the wildcard address with no explicit address
|
||||
* family specified, then force us to get an AF_INET6 socket. On
|
||||
* CentOS 6 and MacOS, getaddrinfo(3) with AF_UNSPEC in ai_family,
|
||||
* and ai_flags containing AI_PASSIVE returns a result structure
|
||||
* with ai_family set to AF_INET, with the result that we create
|
||||
* and bind an IPv4 address wildcard address and by default, we
|
||||
* can't accept IPv6 connections.
|
||||
*
|
||||
* On FreeBSD, under the above circumstances, ai_family in the
|
||||
* result structure is set to AF_INET6.
|
||||
*/
|
||||
if (domain == AF_UNSPEC && !local) {
|
||||
hints.ai_family = AF_INET6;
|
||||
}
|
||||
else {
|
||||
hints.ai_family = domain;
|
||||
}
|
||||
hints.ai_socktype = proto;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
if ((gerror = getaddrinfo(local, portstr, &hints, &res)) != 0)
|
||||
return -1;
|
||||
|
||||
s = socket(res->ai_family, proto, 0);
|
||||
if (s < 0) {
|
||||
freeaddrinfo(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bind_dev) {
|
||||
#if defined(HAVE_SO_BINDTODEVICE)
|
||||
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
|
||||
bind_dev, IFNAMSIZ) < 0)
|
||||
#endif // HAVE_SO_BINDTODEVICE
|
||||
{
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
opt = 1;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *) &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* If we got an IPv6 socket, figure out if it should accept IPv4
|
||||
* connections as well. We do that if and only if no address
|
||||
* family was specified explicitly. Note that we can only
|
||||
* do this if the IPV6_V6ONLY socket option is supported. Also,
|
||||
* OpenBSD explicitly omits support for IPv4-mapped addresses,
|
||||
* even though it implements IPV6_V6ONLY.
|
||||
*/
|
||||
#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
|
||||
if (res->ai_family == AF_INET6 && (domain == AF_UNSPEC || domain == AF_INET6)) {
|
||||
if (domain == AF_UNSPEC)
|
||||
opt = 0;
|
||||
else
|
||||
opt = 1;
|
||||
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
(char *) &opt, sizeof(opt)) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif /* IPV6_V6ONLY */
|
||||
|
||||
if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
freeaddrinfo(res);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
if (proto == SOCK_STREAM) {
|
||||
if (listen(s, INT_MAX) < 0) {
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
/* Nread - reads 'count' bytes from a socket */
|
||||
/********************************************************************/
|
||||
|
||||
int
|
||||
Nread(int fd, char *buf, size_t count, int prot)
|
||||
{
|
||||
return Nrecv(fd, buf, count, prot, 0);
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
/* Nrecv - reads 'count' bytes from a socket */
|
||||
/********************************************************************/
|
||||
|
||||
int
|
||||
Nrecv(int fd, char *buf, size_t count, int prot, int sock_opt)
|
||||
{
|
||||
register ssize_t r;
|
||||
// `nleft` must be signed as it may get negative value for SKIP-RX-COPY UDP (MSG_TRUNC in sock_opt).
|
||||
register ssize_t nleft = count;
|
||||
register size_t total = 0;
|
||||
struct iperf_time ftimeout = { 0, 0 };
|
||||
|
||||
fd_set rfdset;
|
||||
struct timeval timeout = { nread_read_timeout, 0 };
|
||||
|
||||
/*
|
||||
* fd might not be ready for reading on entry. Check for this
|
||||
* (with timeout) first.
|
||||
*
|
||||
* This check could go inside the while() loop below, except we're
|
||||
* currently considering whether it might make sense to support a
|
||||
* codepath that bypasses this check, for situations where we
|
||||
* already know that fd has data on it (for example if we'd gotten
|
||||
* to here as the result of a select() call.
|
||||
*/
|
||||
{
|
||||
FD_ZERO(&rfdset);
|
||||
FD_SET(fd, &rfdset);
|
||||
r = select(fd + 1, &rfdset, NULL, NULL, &timeout);
|
||||
if (r < 0) {
|
||||
return NET_HARDERROR;
|
||||
}
|
||||
if (r == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
while (nleft > 0) {
|
||||
if (sock_opt)
|
||||
r = recv(fd, buf, nleft, sock_opt);
|
||||
else
|
||||
r = read(fd, buf, nleft);
|
||||
|
||||
if (r < 0) {
|
||||
/* XXX EWOULDBLOCK can't happen without non-blocking sockets */
|
||||
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
break;
|
||||
else
|
||||
return NET_HARDERROR;
|
||||
} else if (r == 0)
|
||||
break;
|
||||
|
||||
total += r;
|
||||
nleft -= r;
|
||||
buf += r;
|
||||
|
||||
/*
|
||||
* We need some more bytes but don't want to wait around
|
||||
* forever for them. In the case of partial results, we need
|
||||
* to be able to read some bytes every nread_timeout seconds.
|
||||
*/
|
||||
if (nleft > 0) {
|
||||
struct iperf_time now;
|
||||
|
||||
/*
|
||||
* Also, we have an approximate upper limit for the total time
|
||||
* that a Nread call is supposed to take. We trade off accuracy
|
||||
* of this timeout for a hopefully lower performance impact.
|
||||
*/
|
||||
iperf_time_now(&now);
|
||||
if (ftimeout.secs == 0) {
|
||||
ftimeout = now;
|
||||
iperf_time_add_usecs(&ftimeout, nread_overall_timeout * 1000000L);
|
||||
}
|
||||
if (iperf_time_compare(&ftimeout, &now) < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
FD_ZERO(&rfdset);
|
||||
FD_SET(fd, &rfdset);
|
||||
r = select(fd + 1, &rfdset, NULL, NULL, &timeout);
|
||||
if (r < 0) {
|
||||
return NET_HARDERROR;
|
||||
}
|
||||
if (r == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/********************************************************************/
|
||||
/* Nreads 'count' bytes from a socket - but without using select() */
|
||||
/********************************************************************/
|
||||
int
|
||||
Nread_no_select(int fd, char *buf, size_t count, int prot)
|
||||
{
|
||||
return Nrecv_no_select(fd, buf, count, prot, 0);
|
||||
}
|
||||
|
||||
/********************************************************************/
|
||||
/* Nrecv reads 'count' bytes from a socket - but without using select() */
|
||||
/********************************************************************/
|
||||
int
|
||||
Nrecv_no_select(int fd, char *buf, size_t count, int prot, int sock_opt)
|
||||
{
|
||||
register ssize_t r;
|
||||
register size_t nleft = count;
|
||||
|
||||
while (nleft > 0) {
|
||||
if (sock_opt)
|
||||
r = recv(fd, buf, nleft, sock_opt);
|
||||
else
|
||||
r = read(fd, buf, nleft);
|
||||
if (r < 0) {
|
||||
/* XXX EWOULDBLOCK can't happen without non-blocking sockets */
|
||||
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
break;
|
||||
else
|
||||
return NET_HARDERROR;
|
||||
} else if (r == 0)
|
||||
break;
|
||||
|
||||
nleft -= r;
|
||||
buf += r;
|
||||
|
||||
}
|
||||
return count - nleft;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* N W R I T E
|
||||
*/
|
||||
|
||||
int
|
||||
Nwrite(int fd, const char *buf, size_t count, int prot)
|
||||
{
|
||||
register ssize_t r;
|
||||
register size_t nleft = count;
|
||||
|
||||
while (nleft > 0) {
|
||||
r = write(fd, buf, nleft);
|
||||
if (r < 0) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
#if (EAGAIN != EWOULDBLOCK)
|
||||
/* XXX EWOULDBLOCK can't happen without non-blocking sockets */
|
||||
case EWOULDBLOCK:
|
||||
#endif
|
||||
if (count == nleft)
|
||||
return NET_SOFTERROR;
|
||||
return count - nleft;
|
||||
|
||||
case ENOBUFS :
|
||||
return NET_SOFTERROR;
|
||||
|
||||
default:
|
||||
return NET_HARDERROR;
|
||||
}
|
||||
} else if (r == 0)
|
||||
return NET_SOFTERROR;
|
||||
nleft -= r;
|
||||
buf += r;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
has_sendfile(void)
|
||||
{
|
||||
#if defined(HAVE_SENDFILE)
|
||||
return 1;
|
||||
#else /* HAVE_SENDFILE */
|
||||
return 0;
|
||||
#endif /* HAVE_SENDFILE */
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* N S E N D F I L E
|
||||
*/
|
||||
|
||||
int
|
||||
Nsendfile(int fromfd, int tofd, const char *buf, size_t count)
|
||||
{
|
||||
#if defined(HAVE_SENDFILE)
|
||||
off_t offset;
|
||||
#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_6))
|
||||
off_t sent;
|
||||
#endif
|
||||
register size_t nleft;
|
||||
register ssize_t r;
|
||||
|
||||
nleft = count;
|
||||
while (nleft > 0) {
|
||||
offset = count - nleft;
|
||||
#ifdef linux
|
||||
r = sendfile(tofd, fromfd, &offset, nleft);
|
||||
if (r > 0)
|
||||
nleft -= r;
|
||||
#elif defined(__FreeBSD__)
|
||||
r = sendfile(fromfd, tofd, offset, nleft, NULL, &sent, 0);
|
||||
nleft -= sent;
|
||||
#elif defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_6) /* OS X */
|
||||
sent = nleft;
|
||||
r = sendfile(fromfd, tofd, offset, &sent, NULL, 0);
|
||||
nleft -= sent;
|
||||
#else
|
||||
/* Shouldn't happen. */
|
||||
r = -1;
|
||||
errno = ENOSYS;
|
||||
#endif
|
||||
if (r < 0) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
#if (EAGAIN != EWOULDBLOCK)
|
||||
/* XXX EWOULDBLOCK can't happen without non-blocking sockets */
|
||||
case EWOULDBLOCK:
|
||||
#endif
|
||||
if (count == nleft)
|
||||
return NET_SOFTERROR;
|
||||
return count - nleft;
|
||||
|
||||
case ENOBUFS:
|
||||
case ENOMEM:
|
||||
return NET_SOFTERROR;
|
||||
|
||||
default:
|
||||
return NET_HARDERROR;
|
||||
}
|
||||
}
|
||||
#ifdef linux
|
||||
else if (r == 0)
|
||||
return NET_SOFTERROR;
|
||||
#endif
|
||||
}
|
||||
return count;
|
||||
#else /* HAVE_SENDFILE */
|
||||
errno = ENOSYS; /* error if somehow get called without HAVE_SENDFILE */
|
||||
return NET_HARDERROR;
|
||||
#endif /* HAVE_SENDFILE */
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
int
|
||||
setnonblocking(int fd, int nonblocking)
|
||||
{
|
||||
int flags, newflags;
|
||||
|
||||
flags = fcntl(fd, F_GETFL, 0);
|
||||
if (flags < 0) {
|
||||
perror("fcntl(F_GETFL)");
|
||||
return -1;
|
||||
}
|
||||
if (nonblocking)
|
||||
newflags = flags | (int) O_NONBLOCK;
|
||||
else
|
||||
newflags = flags & ~((int) O_NONBLOCK);
|
||||
if (newflags != flags)
|
||||
if (fcntl(fd, F_SETFL, newflags) < 0) {
|
||||
perror("fcntl(F_SETFL)");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
int
|
||||
getsockdomain(int sock)
|
||||
{
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t len = sizeof(sa);
|
||||
|
||||
if (getsockname(sock, (struct sockaddr *)&sa, &len) < 0) {
|
||||
return -1;
|
||||
}
|
||||
return ((struct sockaddr *) &sa)->sa_family;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, 2017, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#ifndef __NET_H
|
||||
#define __NET_H
|
||||
|
||||
int timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, int timeout);
|
||||
int create_socket(int domain, int type, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, struct addrinfo **server_res_out);
|
||||
int netdial(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, int timeout);
|
||||
int netannounce(int domain, int proto, const char *local, const char *bind_dev, int port);
|
||||
int Nread(int fd, char *buf, size_t count, int prot);
|
||||
int Nrecv(int fd, char *buf, size_t count, int prot, int sock_opt);
|
||||
int Nread_no_select(int fd, char *buf, size_t count, int prot);
|
||||
int Nrecv_no_select(int fd, char *buf, size_t count, int prot, int sock_opt);
|
||||
int Nwrite(int fd, const char *buf, size_t count, int prot) /* __attribute__((hot)) */;
|
||||
int has_sendfile(void);
|
||||
int Nsendfile(int fromfd, int tofd, const char *buf, size_t count) /* __attribute__((hot)) */;
|
||||
int setnonblocking(int fd, int nonblocking);
|
||||
int getsockdomain(int sock);
|
||||
int parse_qos(const char *tos);
|
||||
|
||||
#define NET_SOFTERROR -1
|
||||
#define NET_HARDERROR -2
|
||||
|
||||
#endif /* __NET_H */
|
||||
@@ -0,0 +1,166 @@
|
||||
// "License": Public Domain
|
||||
// I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like.
|
||||
|
||||
#ifndef PORTABLE_ENDIAN_H__
|
||||
#define PORTABLE_ENDIAN_H__
|
||||
|
||||
#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__)
|
||||
|
||||
# define __WINDOWS__
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__CYGWIN__)
|
||||
|
||||
# include <endian.h>
|
||||
|
||||
#elif defined(HAVE_ENDIAN_H)
|
||||
# include <endian.h>
|
||||
|
||||
#elif defined(HAVE_SYS_ENDIAN_H)
|
||||
# include <sys/endian.h>
|
||||
|
||||
# if defined(__sgi)
|
||||
|
||||
# include <netinet/in.h>
|
||||
# include <inttypes.h>
|
||||
|
||||
# define be64toh(x) (x)
|
||||
# define htobe64(x) (x)
|
||||
|
||||
# endif
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
# include <libkern/OSByteOrder.h>
|
||||
|
||||
# define htobe16(x) OSSwapHostToBigInt16(x)
|
||||
# define htole16(x) OSSwapHostToLittleInt16(x)
|
||||
# define be16toh(x) OSSwapBigToHostInt16(x)
|
||||
# define le16toh(x) OSSwapLittleToHostInt16(x)
|
||||
|
||||
# define htobe32(x) OSSwapHostToBigInt32(x)
|
||||
# define htole32(x) OSSwapHostToLittleInt32(x)
|
||||
# define be32toh(x) OSSwapBigToHostInt32(x)
|
||||
# define le32toh(x) OSSwapLittleToHostInt32(x)
|
||||
|
||||
# define htobe64(x) OSSwapHostToBigInt64(x)
|
||||
# define htole64(x) OSSwapHostToLittleInt64(x)
|
||||
# define be64toh(x) OSSwapBigToHostInt64(x)
|
||||
# define le64toh(x) OSSwapLittleToHostInt64(x)
|
||||
|
||||
# define __BYTE_ORDER BYTE_ORDER
|
||||
# define __BIG_ENDIAN BIG_ENDIAN
|
||||
# define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
# define __PDP_ENDIAN PDP_ENDIAN
|
||||
|
||||
#elif defined(__sun) && defined(__SVR4)
|
||||
|
||||
# include <sys/types.h>
|
||||
# include <netinet/in.h>
|
||||
# include <inttypes.h>
|
||||
|
||||
# if !defined (ntohll) || !defined(htonll)
|
||||
# ifdef _BIG_ENDIAN
|
||||
# define htonll(x) (x)
|
||||
# define ntohll(x) (x)
|
||||
# else
|
||||
# define htonll(x) ((((uint64_t)htonl(x)) << 32) + htonl((uint64_t)(x) >> 32))
|
||||
# define ntohll(x) ((((uint64_t)ntohl(x)) << 32) + ntohl((uint64_t)(x) >> 32))
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# define be64toh(x) ntohll(x)
|
||||
# define htobe64(x) htonll(x)
|
||||
|
||||
#elif defined(__WINDOWS__)
|
||||
|
||||
# include <winsock2.h>
|
||||
# include <sys/param.h>
|
||||
|
||||
# if BYTE_ORDER == LITTLE_ENDIAN
|
||||
|
||||
# define htobe16(x) htons(x)
|
||||
# define htole16(x) (x)
|
||||
# define be16toh(x) ntohs(x)
|
||||
# define le16toh(x) (x)
|
||||
|
||||
# define htobe32(x) htonl(x)
|
||||
# define htole32(x) (x)
|
||||
# define be32toh(x) ntohl(x)
|
||||
# define le32toh(x) (x)
|
||||
|
||||
# define htobe64(x) htonll(x)
|
||||
# define htole64(x) (x)
|
||||
# define be64toh(x) ntohll(x)
|
||||
# define le64toh(x) (x)
|
||||
|
||||
# elif BYTE_ORDER == BIG_ENDIAN
|
||||
|
||||
/* that would be xbox 360 */
|
||||
# define htobe16(x) (x)
|
||||
# define htole16(x) __builtin_bswap16(x)
|
||||
# define be16toh(x) (x)
|
||||
# define le16toh(x) __builtin_bswap16(x)
|
||||
|
||||
# define htobe32(x) (x)
|
||||
# define htole32(x) __builtin_bswap32(x)
|
||||
# define be32toh(x) (x)
|
||||
# define le32toh(x) __builtin_bswap32(x)
|
||||
|
||||
# define htobe64(x) (x)
|
||||
# define htole64(x) __builtin_bswap64(x)
|
||||
# define be64toh(x) (x)
|
||||
# define le64toh(x) __builtin_bswap64(x)
|
||||
|
||||
# else
|
||||
|
||||
# error byte order not supported
|
||||
|
||||
# endif
|
||||
|
||||
# define __BYTE_ORDER BYTE_ORDER
|
||||
# define __BIG_ENDIAN BIG_ENDIAN
|
||||
# define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
# define __PDP_ENDIAN PDP_ENDIAN
|
||||
|
||||
#else
|
||||
|
||||
// Unsupported platforms.
|
||||
// Intended to support CentOS 5 but hopefully not too far from
|
||||
// the truth because we use the homebrew htonll, et al. implementations
|
||||
// that were originally the sole implementation of this functionality
|
||||
// in iperf 3.0.
|
||||
#if (!defined(__vxworks)) && (!defined(__VXWORKS__))
|
||||
# warning platform not supported
|
||||
#endif
|
||||
# include <endian.h>
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
#define HTONLL(n) (n)
|
||||
#define NTOHLL(n) (n)
|
||||
#else
|
||||
#define HTONLL(n) ((((unsigned long long)(n) & 0xFF) << 56) | \
|
||||
(((unsigned long long)(n) & 0xFF00) << 40) | \
|
||||
(((unsigned long long)(n) & 0xFF0000) << 24) | \
|
||||
(((unsigned long long)(n) & 0xFF000000) << 8) | \
|
||||
(((unsigned long long)(n) & 0xFF00000000) >> 8) | \
|
||||
(((unsigned long long)(n) & 0xFF0000000000) >> 24) | \
|
||||
(((unsigned long long)(n) & 0xFF000000000000) >> 40) | \
|
||||
(((unsigned long long)(n) & 0xFF00000000000000) >> 56))
|
||||
|
||||
#define NTOHLL(n) ((((unsigned long long)(n) & 0xFF) << 56) | \
|
||||
(((unsigned long long)(n) & 0xFF00) << 40) | \
|
||||
(((unsigned long long)(n) & 0xFF0000) << 24) | \
|
||||
(((unsigned long long)(n) & 0xFF000000) << 8) | \
|
||||
(((unsigned long long)(n) & 0xFF00000000) >> 8) | \
|
||||
(((unsigned long long)(n) & 0xFF0000000000) >> 24) | \
|
||||
(((unsigned long long)(n) & 0xFF000000000000) >> 40) | \
|
||||
(((unsigned long long)(n) & 0xFF00000000000000) >> 56))
|
||||
#endif
|
||||
|
||||
#define htobe64(n) HTONLL(n)
|
||||
#define be64toh(n) NTOHLL(n)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAwVbvPf/eDIZKmEVth9+VPgSx1RkXOAPCJ5tl51bcYoy9P10N
|
||||
noutsTK/64VclIuyUDUdAw81Vu5tYRcBeB8Jllp02xL8kPo0IROsT3wmMYbPziUG
|
||||
/I2988sAP9mL8QbtKydnADHMikfadfyPkfxW2naFtquWT/vKKVkhC2LHJyTpmAVj
|
||||
pp6R9LDDu/YY0/kb5DvYPpe62xgNWjNVIhABu3R+StAAL25SaLXuaGSVpDe6Sphn
|
||||
TqVf+LmKCkZmSMSfQoozXomNyFpLU8ODoV3QyHYi6QSWWFtU6gu0uso/9pfRFjbN
|
||||
GV9lxokKNz/cZGyu0SFddMiTGrt24NROi0RHwwIDAQABAoIBACVG+cHef5WyntdV
|
||||
K5UzCrd2eEM6HzvxnZG9aJx+JufpcuOwsVuMWuT7f/2NLRiHBs5oLzvTxtkIB5bc
|
||||
tK/QbCzNLBLBSmk5lKt7+5EnwsVx1MdOZFZ1jdZfoaCt9Ul3qGrVogprj6Bp0jlF
|
||||
hPkEyko85/McilLJnWTzhmeHmBZ3tOJ9LWHgVdXGXZx/pBuxZ+UB/xgZdvdZK86F
|
||||
xndWqD8lvfpoSsVwzCORdXvQWs78CtT2KXYvt8S6mrLMoIoHLO/1rwwKKoZfFuja
|
||||
NouN36PGaSo4/9O1d11/s9zwdJH3ShozY0Fao8I3XhdH0uvTirmAEPh7U1Lo71pP
|
||||
ksUvbUECgYEA/7p1Bg/XsSK72KgJ695B2hi8g9+wv0eKCpGwkKJQZKE338it8zIn
|
||||
+JMaeDjObLhb4B2QP/3iL0mdU2mJzm0X8hYCKqBdyICb9wixznLLAej+uw2j60Ef
|
||||
tlGenCqAkVL3pRS5txztNhFsXx3JwxCSj4zwmVm5oyxkpAeuwvZ1cPkCgYEAwYuC
|
||||
6nPlRf6GEf/MuHCziaCCPrDm1u472uspxHn41crw6BaMl5xcmr0Z8O7heOtlVmMl
|
||||
Mn3gfIbd/NGyjs8ejmWcO5mZHtESGM/zAaVyu7bn3ij37OEndbEpZsCpRtxvtaji
|
||||
MTMwhdV2xteJ0+KCtg66ziSyJv3krQTEW8DZKZsCgYB8BcnLbtOErPu9T4HASsJV
|
||||
K7oBmvL1UZS5G38uJgonQ6j9dy4lzCVmgLFNrP8v6xljz/Ktlkuj82fBlGWpH2+F
|
||||
kPbsBWp2WylI3YaeQT4DZyRjQ3JEHglrOppZ0qMX180S2sJW9Eh2+Gw+lQvM9rSd
|
||||
uhTVypYldNo6Ux+GnlDGwQKBgQC/6W4uvCyjcvXN8y8z08yysw1yzEaY6DFBqd0I
|
||||
jUlH9Ktb9sABtXG9nbSTSssX85HQTw8bOeXWlISZo/TB1m4eFHMORgemnviq0cfL
|
||||
4hoaOAtCJq1vnPJbqQe8c11mfj3mi0d+MZvzmO7ly+NGzlt92q0wqwJb13VgelGa
|
||||
CWdL8QKBgQD1ABK8WJuMowihrQsLaUxNO/1hsN8CP/rjoi9D5NdSFt5zzcC3D/RA
|
||||
m42ScOaAFIh44Js5aAROvbDqIlelhwRlmutP/lYyQRZDZm7X6u/bKuqK4H/sM6ZB
|
||||
SICe2eWFvbKexI7QnreRltOWIFrNoZy9FdELM8DS+kePAlVt7c83Uw==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -0,0 +1,9 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwVbvPf/eDIZKmEVth9+V
|
||||
PgSx1RkXOAPCJ5tl51bcYoy9P10NnoutsTK/64VclIuyUDUdAw81Vu5tYRcBeB8J
|
||||
llp02xL8kPo0IROsT3wmMYbPziUG/I2988sAP9mL8QbtKydnADHMikfadfyPkfxW
|
||||
2naFtquWT/vKKVkhC2LHJyTpmAVjpp6R9LDDu/YY0/kb5DvYPpe62xgNWjNVIhAB
|
||||
u3R+StAAL25SaLXuaGSVpDe6SphnTqVf+LmKCkZmSMSfQoozXomNyFpLU8ODoV3Q
|
||||
yHYi6QSWWFtU6gu0uso/9pfRFjbNGV9lxokKNz/cZGyu0SFddMiTGrt24NROi0RH
|
||||
wwIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
@@ -0,0 +1,527 @@
|
||||
/* $OpenBSD: queue.h,v 1.32 2007/04/30 18:42:34 pedro Exp $ */
|
||||
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
|
||||
/*
|
||||
* This file defines five types of data structures: singly-linked lists,
|
||||
* lists, simple queues, tail queues, and circular queues.
|
||||
*
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The elements
|
||||
* are singly linked for minimum space and pointer manipulation overhead at
|
||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
* added to the list after an existing element or at the head of the list.
|
||||
* Elements being removed from the head of the list should use the explicit
|
||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||
* for applications with large datasets and few or no removals or for
|
||||
* implementing a LIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may only be traversed in the forward direction.
|
||||
*
|
||||
* A simple queue is headed by a pair of pointers, one the head of the
|
||||
* list and the other to the tail of the list. The elements are singly
|
||||
* linked to save space, so elements can only be removed from the
|
||||
* head of the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the
|
||||
* list. A simple queue may only be traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* A circle queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the list.
|
||||
* A circle queue may be traversed in either direction, but has a more
|
||||
* complex end of list detection.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*/
|
||||
|
||||
#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
|
||||
#define _Q_INVALIDATE(a) (a) = ((void *)-1)
|
||||
#else
|
||||
#define _Q_INVALIDATE(a)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Singly-linked List definitions.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List access methods.
|
||||
*/
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
#define SLIST_END(head) NULL
|
||||
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for((var) = SLIST_FIRST(head); \
|
||||
(var) != SLIST_END(head); \
|
||||
(var) = SLIST_NEXT(var, field))
|
||||
|
||||
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
|
||||
for ((varp) = &SLIST_FIRST((head)); \
|
||||
((var) = *(varp)) != SLIST_END(head); \
|
||||
(varp) = &SLIST_NEXT((var), field))
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_INIT(head) { \
|
||||
SLIST_FIRST(head) = SLIST_END(head); \
|
||||
}
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
||||
(slistelm)->field.sle_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.sle_next = (head)->slh_first; \
|
||||
(head)->slh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_NEXT(head, elm, field) do { \
|
||||
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
if ((head)->slh_first == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} else { \
|
||||
struct type *curelm = (head)->slh_first; \
|
||||
\
|
||||
while (curelm->field.sle_next != (elm)) \
|
||||
curelm = curelm->field.sle_next; \
|
||||
curelm->field.sle_next = \
|
||||
curelm->field.sle_next->field.sle_next; \
|
||||
_Q_INVALIDATE((elm)->field.sle_next); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* List definitions.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List access methods
|
||||
*/
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
#define LIST_END(head) NULL
|
||||
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for((var) = LIST_FIRST(head); \
|
||||
(var)!= LIST_END(head); \
|
||||
(var) = LIST_NEXT(var, field))
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
#define LIST_INIT(head) do { \
|
||||
LIST_FIRST(head) = LIST_END(head); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||
(listelm)->field.le_next->field.le_prev = \
|
||||
&(elm)->field.le_next; \
|
||||
(listelm)->field.le_next = (elm); \
|
||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
(elm)->field.le_next = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
|
||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
||||
(head)->lh_first = (elm); \
|
||||
(elm)->field.le_prev = &(head)->lh_first; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REPLACE(elm, elm2, field) do { \
|
||||
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
|
||||
(elm2)->field.le_next->field.le_prev = \
|
||||
&(elm2)->field.le_next; \
|
||||
(elm2)->field.le_prev = (elm)->field.le_prev; \
|
||||
*(elm2)->field.le_prev = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Simple queue definitions.
|
||||
*/
|
||||
#define SIMPLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sqh_first; /* first element */ \
|
||||
struct type **sqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define SIMPLEQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).sqh_first }
|
||||
|
||||
#define SIMPLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple queue access methods.
|
||||
*/
|
||||
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
|
||||
#define SIMPLEQ_END(head) NULL
|
||||
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
|
||||
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
||||
|
||||
#define SIMPLEQ_FOREACH(var, head, field) \
|
||||
for((var) = SIMPLEQ_FIRST(head); \
|
||||
(var) != SIMPLEQ_END(head); \
|
||||
(var) = SIMPLEQ_NEXT(var, field))
|
||||
|
||||
/*
|
||||
* Simple queue functions.
|
||||
*/
|
||||
#define SIMPLEQ_INIT(head) do { \
|
||||
(head)->sqh_first = NULL; \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(head)->sqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.sqe_next = NULL; \
|
||||
*(head)->sqh_last = (elm); \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(listelm)->field.sqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Tail queue definitions.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* tail queue access methods
|
||||
*/
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
#define TAILQ_END(head) NULL
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
/* XXX */
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
#define TAILQ_EMPTY(head) \
|
||||
(TAILQ_FIRST(head) == TAILQ_END(head))
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for((var) = TAILQ_FIRST(head); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_NEXT(var, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for((var) = TAILQ_LAST(head, headname); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_PREV(var, headname, field))
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_INIT(head) do { \
|
||||
(head)->tqh_first = NULL; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||
(head)->tqh_first->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.tqe_next = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(listelm)->field.tqe_next = (elm); \
|
||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
(elm)->field.tqe_next = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
|
||||
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
|
||||
(elm2)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm2)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm2)->field.tqe_next; \
|
||||
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
|
||||
*(elm2)->field.tqe_prev = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Circular queue definitions.
|
||||
*/
|
||||
#define CIRCLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *cqh_first; /* first element */ \
|
||||
struct type *cqh_last; /* last element */ \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_HEAD_INITIALIZER(head) \
|
||||
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
|
||||
|
||||
#define CIRCLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *cqe_next; /* next element */ \
|
||||
struct type *cqe_prev; /* previous element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Circular queue access methods
|
||||
*/
|
||||
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
|
||||
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
|
||||
#define CIRCLEQ_END(head) ((void *)(head))
|
||||
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
|
||||
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
|
||||
#define CIRCLEQ_EMPTY(head) \
|
||||
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
|
||||
|
||||
#define CIRCLEQ_FOREACH(var, head, field) \
|
||||
for((var) = CIRCLEQ_FIRST(head); \
|
||||
(var) != CIRCLEQ_END(head); \
|
||||
(var) = CIRCLEQ_NEXT(var, field))
|
||||
|
||||
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
|
||||
for((var) = CIRCLEQ_LAST(head); \
|
||||
(var) != CIRCLEQ_END(head); \
|
||||
(var) = CIRCLEQ_PREV(var, field))
|
||||
|
||||
/*
|
||||
* Circular queue functions.
|
||||
*/
|
||||
#define CIRCLEQ_INIT(head) do { \
|
||||
(head)->cqh_first = CIRCLEQ_END(head); \
|
||||
(head)->cqh_last = CIRCLEQ_END(head); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
||||
(elm)->field.cqe_prev = (listelm); \
|
||||
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
||||
(listelm)->field.cqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm); \
|
||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
||||
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
||||
(listelm)->field.cqe_prev = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = (head)->cqh_first; \
|
||||
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
|
||||
if ((head)->cqh_last == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(head)->cqh_first->field.cqe_prev = (elm); \
|
||||
(head)->cqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = CIRCLEQ_END(head); \
|
||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
||||
if ((head)->cqh_first == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(head)->cqh_last->field.cqe_next = (elm); \
|
||||
(head)->cqh_last = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_REMOVE(head, elm, field) do { \
|
||||
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
||||
else \
|
||||
(elm)->field.cqe_next->field.cqe_prev = \
|
||||
(elm)->field.cqe_prev; \
|
||||
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
||||
else \
|
||||
(elm)->field.cqe_prev->field.cqe_next = \
|
||||
(elm)->field.cqe_next; \
|
||||
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.cqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
|
||||
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
|
||||
CIRCLEQ_END(head)) \
|
||||
(head).cqh_last = (elm2); \
|
||||
else \
|
||||
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
|
||||
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
|
||||
CIRCLEQ_END(head)) \
|
||||
(head).cqh_first = (elm2); \
|
||||
else \
|
||||
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.cqe_next); \
|
||||
} while (0)
|
||||
|
||||
#endif /* !_SYS_QUEUE_H_ */
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2017-2020, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "iperf.h"
|
||||
#include "iperf_api.h"
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#include "units.h"
|
||||
|
||||
int test_iperf_set_test_bind_port(struct iperf_test *test)
|
||||
{
|
||||
int port;
|
||||
port = iperf_get_test_bind_port(test);
|
||||
iperf_set_test_bind_port(test, 5202);
|
||||
port = iperf_get_test_bind_port(test);
|
||||
assert(port == 5202);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_iperf_set_mss(struct iperf_test *test)
|
||||
{
|
||||
int mss = iperf_get_test_mss(test);
|
||||
iperf_set_test_mss(test, 535);
|
||||
mss = iperf_get_test_mss(test);
|
||||
assert(mss == 535);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
const char *ver;
|
||||
struct iperf_test *test;
|
||||
int sint, gint;
|
||||
|
||||
ver = iperf_get_iperf_version();
|
||||
assert(strcmp(ver, IPERF_VERSION) == 0);
|
||||
|
||||
test = iperf_new_test();
|
||||
assert(test != NULL);
|
||||
|
||||
iperf_defaults(test);
|
||||
|
||||
sint = 10;
|
||||
iperf_set_test_connect_timeout(test, sint);
|
||||
gint = iperf_get_test_connect_timeout(test);
|
||||
assert(sint == gint);
|
||||
|
||||
int ret;
|
||||
ret = test_iperf_set_test_bind_port(test);
|
||||
|
||||
ret += test_iperf_set_mss(test);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2020, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#include "iperf_config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "iperf.h"
|
||||
#include "iperf_api.h"
|
||||
#if defined(HAVE_SSL)
|
||||
#include "iperf_auth.h"
|
||||
#endif /* HAVE_SSL */
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#include "units.h"
|
||||
|
||||
#if defined(HAVE_SSL)
|
||||
int test_authtoken(const char *authUser, const char *authPassword, EVP_PKEY *pubkey, EVP_PKEY *privkey);
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
/* sha256 */
|
||||
void sha256(const char *string, char outputBuffer[65]);
|
||||
const char sha256String[] = "This is a SHA256 test.";
|
||||
const char sha256Digest[] = "4816482f8b4149f687a1a33d61a0de6b611364ec0fb7adffa59ff2af672f7232"; /* echo -n "This is a SHA256 test." | shasum -a256 */
|
||||
char sha256Output[65];
|
||||
|
||||
sha256(sha256String, sha256Output);
|
||||
assert(strcmp(sha256Output, sha256Digest) == 0);
|
||||
|
||||
/* Base64{Encode,Decode} */
|
||||
int Base64Encode(const unsigned char* buffer, const size_t length, char** b64text);
|
||||
int Base64Decode(const char* b64message, unsigned char** buffer, size_t* length);
|
||||
const char base64String[] = "This is a Base64 test.";
|
||||
char *base64Text;
|
||||
char *base64Decode;
|
||||
size_t base64DecodeLength;
|
||||
const char base64EncodeCheck[] = "VGhpcyBpcyBhIEJhc2U2NCB0ZXN0Lg=="; /* echo -n "This is a Base64 test." | b64encode -r - */
|
||||
|
||||
assert(Base64Encode((unsigned char *) base64String, strlen(base64String), &base64Text) == 0);
|
||||
assert(strcmp(base64Text, base64EncodeCheck) == 0);
|
||||
assert(Base64Decode(base64Text, (unsigned char **) &base64Decode, &base64DecodeLength) == 0);
|
||||
assert(strcmp(base64String, base64Decode) == 0);
|
||||
|
||||
/* public/private key tests */
|
||||
const char *pubkeyfile = "public.pem";
|
||||
const char *privkeyfile = "private.pem";
|
||||
|
||||
/* built-in tests */
|
||||
assert(test_load_pubkey_from_file(pubkeyfile) == 0);
|
||||
assert(test_load_private_key_from_file(privkeyfile) == 0);
|
||||
|
||||
/* load public key pair for use in further tests */
|
||||
EVP_PKEY *pubkey, *privkey;
|
||||
pubkey = load_pubkey_from_file(pubkeyfile);
|
||||
assert(pubkey);
|
||||
privkey = load_privkey_from_file(privkeyfile);
|
||||
assert(privkey);
|
||||
|
||||
/* authentication token tests */
|
||||
assert(test_authtoken("kilroy", "fubar", pubkey, privkey) == 0);
|
||||
|
||||
/* This should fail because the data is way too long for the RSA key */
|
||||
/* assert(test_authtoken("kilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroy", "fubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubar", pubkey, privkey) < 0); */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
test_authtoken(const char *authUser, const char *authPassword, EVP_PKEY *pubkey, EVP_PKEY *privkey) {
|
||||
char *authToken;
|
||||
char *decodeUser;
|
||||
char *decodePassword;
|
||||
time_t decodeTime;
|
||||
|
||||
int use_pkcs1_padding = 1;
|
||||
assert(encode_auth_setting(authUser, authPassword, pubkey, &authToken, use_pkcs1_padding) == 0);
|
||||
assert(decode_auth_setting(0, authToken, privkey, &decodeUser, &decodePassword, &decodeTime, use_pkcs1_padding) == 0);
|
||||
|
||||
assert(strcmp(decodeUser, authUser) == 0);
|
||||
assert(strcmp(decodePassword, authPassword) == 0);
|
||||
|
||||
time_t now = time(NULL);
|
||||
|
||||
assert(now - decodeTime >= 0); /* time has to go forwards */
|
||||
assert(now - decodeTime <= 1); /* shouldn't take more than a second to run */
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_SSL */
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#include "iperf_config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "timer.h"
|
||||
#include "iperf_time.h"
|
||||
|
||||
|
||||
static int flag;
|
||||
|
||||
|
||||
static void
|
||||
timer_proc( TimerClientData client_data, struct iperf_time* nowP )
|
||||
{
|
||||
flag = 1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
Timer *tp;
|
||||
|
||||
flag = 0;
|
||||
tp = tmr_create(NULL, timer_proc, JunkClientData, 3000000, 0);
|
||||
if (!tp)
|
||||
{
|
||||
printf("failed to create timer\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
sleep(2);
|
||||
|
||||
tmr_run(NULL);
|
||||
if (flag)
|
||||
{
|
||||
printf("timer should not have expired\n");
|
||||
exit(-1);
|
||||
}
|
||||
sleep(1);
|
||||
|
||||
tmr_run(NULL);
|
||||
if (!flag)
|
||||
{
|
||||
printf("timer should have expired\n");
|
||||
exit(-2);
|
||||
}
|
||||
|
||||
tmr_destroy();
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, 2017, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "iperf.h"
|
||||
#include "units.h"
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
iperf_size_t llu;
|
||||
double d;
|
||||
char s[11];
|
||||
|
||||
assert(1024.0 * 0.5 == unit_atof("0.5K"));
|
||||
assert(1024.0 == unit_atof("1K"));
|
||||
assert(1024.0 * 1024.0 == unit_atof("1M"));
|
||||
assert(4.0 * 1024.0 * 1024.0 * 1024.0 == unit_atof("4G"));
|
||||
assert(3.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 == unit_atof("3T"));
|
||||
|
||||
assert(1024.0 * 0.5 == unit_atof("0.5k"));
|
||||
assert(1024.0 == unit_atof("1k"));
|
||||
assert(1024.0 * 1024.0 == unit_atof("1m"));
|
||||
assert(4.0 * 1024.0 * 1024.0 * 1024.0 == unit_atof("4g"));
|
||||
assert(3.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 == unit_atof("3t"));
|
||||
|
||||
assert(1024 * 0.5 == unit_atoi("0.5K"));
|
||||
assert(1024 == unit_atoi("1K"));
|
||||
assert(1024 * 1024 == unit_atoi("1M"));
|
||||
d = 4.0 * 1024 * 1024 * 1024;
|
||||
llu = (iperf_size_t) d;
|
||||
assert(llu == unit_atoi("4G"));
|
||||
d = 3.0 * 1024 * 1024 * 1024 * 1024;
|
||||
llu = (iperf_size_t) d;
|
||||
assert(llu == unit_atoi("3T"));
|
||||
|
||||
assert(1024 * 0.5 == unit_atoi("0.5k"));
|
||||
assert(1024 == unit_atoi("1k"));
|
||||
assert(1024 * 1024 == unit_atoi("1m"));
|
||||
d = 4.0 * 1024 * 1024 * 1024;
|
||||
llu = (iperf_size_t) d;
|
||||
assert(llu == unit_atoi("4g"));
|
||||
d = 3.0 * 1024 * 1024 * 1024 * 1024;
|
||||
llu = (iperf_size_t) d;
|
||||
assert(llu == unit_atoi("3t"));
|
||||
|
||||
unit_snprintf(s, 11, 1024.0, 'A');
|
||||
assert(strncmp(s, "1.00 KByte", 11) == 0);
|
||||
|
||||
unit_snprintf(s, 11, 1024.0 * 1024.0, 'A');
|
||||
assert(strncmp(s, "1.00 MByte", 11) == 0);
|
||||
|
||||
unit_snprintf(s, 11, 1000.0, 'k');
|
||||
assert(strncmp(s, "8.00 Kbit", 11) == 0);
|
||||
|
||||
unit_snprintf(s, 11, 1000.0 * 1000.0, 'a');
|
||||
assert(strncmp(s, "8.00 Mbit", 11) == 0);
|
||||
|
||||
d = 4.0 * 1024 * 1024 * 1024;
|
||||
unit_snprintf(s, 11, d, 'A');
|
||||
assert(strncmp(s, "4.00 GByte", 11) == 0);
|
||||
|
||||
unit_snprintf(s, 11, d, 'a');
|
||||
assert(strncmp(s, "34.4 Gbit", 11) == 0);
|
||||
|
||||
d = 4.0 * 1024 * 1024 * 1024 * 1024;
|
||||
unit_snprintf(s, 11, d, 'A');
|
||||
assert(strncmp(s, "4.00 TByte", 11) == 0);
|
||||
|
||||
unit_snprintf(s, 11, d, 'a');
|
||||
assert(strncmp(s, "35.2 Tbit", 11) == 0);
|
||||
|
||||
d = 4.0 * 1024 * 1024 * 1024 * 1024 * 1024;
|
||||
unit_snprintf(s, 11, d, 'A');
|
||||
assert(strncmp(s, "4096 TByte", 11) == 0);
|
||||
|
||||
unit_snprintf(s, 11, d, 'a');
|
||||
assert(strncmp(s, "36029 Tbit", 11) == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "iperf_util.h"
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char cookie[37];
|
||||
make_cookie(cookie);
|
||||
printf("cookie: '%s'\n", cookie);
|
||||
if (strlen(cookie) != 36)
|
||||
{
|
||||
printf("Not 36 characters long!\n");
|
||||
exit(-1);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, 2017, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
|
||||
/*
|
||||
* routines related to collection TCP_INFO using getsockopt()
|
||||
*
|
||||
* Brian Tierney, ESnet (bltierney@es.net)
|
||||
*
|
||||
* Note that this is only really useful on Linux.
|
||||
* XXX: only standard on linux versions 2.4 and later
|
||||
#
|
||||
* FreeBSD has a limited implementation that only includes the following:
|
||||
* tcpi_snd_ssthresh, tcpi_snd_cwnd, tcpi_rcv_space, tcpi_rtt
|
||||
* Based on information on http://wiki.freebsd.org/8.0TODO, I dont think this will be
|
||||
* fixed before v8.1 at the earliest.
|
||||
*
|
||||
* OSX has no support.
|
||||
*
|
||||
* I think MS Windows does support TCP_INFO, but iperf3 does not currently support Windows.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "iperf.h"
|
||||
#include "iperf_api.h"
|
||||
#include "iperf_locale.h"
|
||||
|
||||
/*************************************************************/
|
||||
int
|
||||
has_tcpinfo(void)
|
||||
{
|
||||
#if (defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)) \
|
||||
&& defined(TCP_INFO)
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
int
|
||||
has_tcpinfo_retransmits(void)
|
||||
{
|
||||
#if defined(linux) && defined(TCP_MD5SIG)
|
||||
/* TCP_MD5SIG doesn't actually have anything to do with TCP
|
||||
** retransmits, it just showed up in the same rev of the header
|
||||
** file. If it's present then struct tcp_info has the
|
||||
** tcpi_total_retrans field that we need; if not, not.
|
||||
*/
|
||||
return 1;
|
||||
#else
|
||||
#if defined(__FreeBSD__) && __FreeBSD_version >= 600000
|
||||
return 1; /* Should work now */
|
||||
#elif (defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO)
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
void
|
||||
save_tcpinfo(struct iperf_stream *sp, struct iperf_interval_results *irp)
|
||||
{
|
||||
#if (defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)) && \
|
||||
defined(TCP_INFO)
|
||||
socklen_t tcp_info_length = sizeof(struct tcp_info);
|
||||
|
||||
if (getsockopt(sp->socket, IPPROTO_TCP, TCP_INFO, (void *)&irp->tcpInfo, &tcp_info_length) < 0)
|
||||
iperf_err(sp->test, "getsockopt - %s", strerror(errno));
|
||||
|
||||
if (sp->test->debug) {
|
||||
printf("tcpi_snd_cwnd %u tcpi_snd_mss %u tcpi_rtt %u\n",
|
||||
irp->tcpInfo.tcpi_snd_cwnd, irp->tcpInfo.tcpi_snd_mss,
|
||||
irp->tcpInfo.tcpi_rtt);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
long
|
||||
get_total_retransmits(struct iperf_interval_results *irp)
|
||||
{
|
||||
#if defined(linux) && defined(TCP_MD5SIG)
|
||||
return irp->tcpInfo.tcpi_total_retrans;
|
||||
#elif defined(__FreeBSD__) && __FreeBSD_version >= 600000
|
||||
return irp->tcpInfo.tcpi_snd_rexmitpack;
|
||||
#elif (defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO)
|
||||
return irp->tcpInfo.tcpi_snd_rexmitpack;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/*
|
||||
* Return snd_cwnd in octets.
|
||||
*/
|
||||
long
|
||||
get_snd_cwnd(struct iperf_interval_results *irp)
|
||||
{
|
||||
#if defined(linux) && defined(TCP_MD5SIG)
|
||||
return (long)irp->tcpInfo.tcpi_snd_cwnd * irp->tcpInfo.tcpi_snd_mss;
|
||||
#elif defined(__FreeBSD__) && __FreeBSD_version >= 600000
|
||||
return irp->tcpInfo.tcpi_snd_cwnd;
|
||||
#elif defined(__NetBSD__) && defined(TCP_INFO)
|
||||
return (long)irp->tcpInfo.tcpi_snd_cwnd * irp->tcpInfo.tcpi_snd_mss;
|
||||
#elif defined(__OpenBSD__) && defined(TCP_INFO)
|
||||
return irp->tcpInfo.tcpi_snd_cwnd;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/*
|
||||
* Return snd_wnd in octets.
|
||||
*/
|
||||
long
|
||||
get_snd_wnd(struct iperf_interval_results *irp)
|
||||
{
|
||||
#if !defined(HAVE_TCP_INFO_SND_WND)
|
||||
return -1;
|
||||
#elif defined(linux) && defined(TCP_MD5SIG)
|
||||
return irp->tcpInfo.tcpi_snd_wnd;
|
||||
#elif defined(__FreeBSD__) && __FreeBSD_version >= 600000
|
||||
return irp->tcpInfo.tcpi_snd_wnd;
|
||||
#elif defined(__NetBSD__) && defined(TCP_INFO)
|
||||
return (long)irp->tcpInfo.tcpi_snd_wnd * irp->tcpInfo.tcpi_snd_mss;
|
||||
#elif defined(__OpenBSD__) && defined(TCP_INFO)
|
||||
return irp->tcpInfo.tcpi_snd_wnd;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/*
|
||||
* Return rtt in usec.
|
||||
*/
|
||||
long
|
||||
get_rtt(struct iperf_interval_results *irp)
|
||||
{
|
||||
#if defined(linux) && defined(TCP_MD5SIG)
|
||||
return irp->tcpInfo.tcpi_rtt;
|
||||
#elif defined(__FreeBSD__) && __FreeBSD_version >= 600000
|
||||
return irp->tcpInfo.tcpi_rtt;
|
||||
#elif (defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO)
|
||||
return irp->tcpInfo.tcpi_rtt;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/*
|
||||
* Return rttvar in usec.
|
||||
*/
|
||||
long
|
||||
get_rttvar(struct iperf_interval_results *irp)
|
||||
{
|
||||
#if defined(linux) && defined(TCP_MD5SIG)
|
||||
return irp->tcpInfo.tcpi_rttvar;
|
||||
#elif defined(__FreeBSD__) && __FreeBSD_version >= 600000
|
||||
return irp->tcpInfo.tcpi_rttvar;
|
||||
#elif (defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO)
|
||||
return irp->tcpInfo.tcpi_rttvar;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/*
|
||||
* Return PMTU in bytes.
|
||||
*/
|
||||
long
|
||||
get_pmtu(struct iperf_interval_results *irp)
|
||||
{
|
||||
#if defined(linux) && defined(TCP_MD5SIG)
|
||||
return irp->tcpInfo.tcpi_pmtu;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
void
|
||||
build_tcpinfo_message(struct iperf_interval_results *r, char *message)
|
||||
{
|
||||
#if defined(linux) && defined(TCP_INFO)
|
||||
sprintf(message, report_tcpInfo, r->tcpInfo.tcpi_snd_cwnd, r->tcpInfo.tcpi_snd_ssthresh,
|
||||
r->tcpInfo.tcpi_rcv_ssthresh, r->tcpInfo.tcpi_unacked, r->tcpInfo.tcpi_sacked,
|
||||
r->tcpInfo.tcpi_lost, r->tcpInfo.tcpi_retrans, r->tcpInfo.tcpi_fackets,
|
||||
r->tcpInfo.tcpi_rtt, r->tcpInfo.tcpi_reordering);
|
||||
#endif
|
||||
#if (defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO)
|
||||
sprintf(message, report_tcpInfo, r->tcpInfo.tcpi_snd_cwnd,
|
||||
r->tcpInfo.tcpi_rcv_space, r->tcpInfo.tcpi_snd_ssthresh, r->tcpInfo.tcpi_rtt);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*
|
||||
* Based on timers.c by Jef Poskanzer. Used with permission.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "timer.h"
|
||||
#include "iperf_time.h"
|
||||
|
||||
static Timer* timers = NULL;
|
||||
static Timer* free_timers = NULL;
|
||||
|
||||
TimerClientData JunkClientData;
|
||||
|
||||
|
||||
|
||||
/* This is an efficiency tweak. All the routines that need to know the
|
||||
** current time get passed a pointer to a struct iperf_time. If it's non-NULL
|
||||
** it gets used, otherwise we do our own iperf_time_now() to fill it in.
|
||||
** This lets the caller avoid extraneous iperf_time_now()s when efficiency
|
||||
** is needed, and not bother with the extra code when efficiency doesn't
|
||||
** matter too much.
|
||||
*/
|
||||
static void
|
||||
getnow( struct iperf_time* nowP, struct iperf_time* nowP2 )
|
||||
{
|
||||
if ( nowP != NULL )
|
||||
*nowP2 = *nowP;
|
||||
else
|
||||
iperf_time_now(nowP2);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
list_add( Timer* t )
|
||||
{
|
||||
Timer* t2;
|
||||
Timer* t2prev;
|
||||
|
||||
if ( timers == NULL ) {
|
||||
/* The list is empty. */
|
||||
timers = t;
|
||||
t->prev = t->next = NULL;
|
||||
} else {
|
||||
if (iperf_time_compare(&t->time, &timers->time) < 0) {
|
||||
/* The new timer goes at the head of the list. */
|
||||
t->prev = NULL;
|
||||
t->next = timers;
|
||||
timers->prev = t;
|
||||
timers = t;
|
||||
} else {
|
||||
/* Walk the list to find the insertion point. */
|
||||
for ( t2prev = timers, t2 = timers->next; t2 != NULL;
|
||||
t2prev = t2, t2 = t2->next ) {
|
||||
if (iperf_time_compare(&t->time, &t2->time) < 0) {
|
||||
/* Found it. */
|
||||
t2prev->next = t;
|
||||
t->prev = t2prev;
|
||||
t->next = t2;
|
||||
t2->prev = t;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Oops, got to the end of the list. Add to tail. */
|
||||
t2prev->next = t;
|
||||
t->prev = t2prev;
|
||||
t->next = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
list_remove( Timer* t )
|
||||
{
|
||||
if ( t->prev == NULL )
|
||||
timers = t->next;
|
||||
else
|
||||
t->prev->next = t->next;
|
||||
if ( t->next != NULL )
|
||||
t->next->prev = t->prev;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
list_resort( Timer* t )
|
||||
{
|
||||
/* Remove the timer from the list. */
|
||||
list_remove( t );
|
||||
/* And add it back in, sorted correctly. */
|
||||
list_add( t );
|
||||
}
|
||||
|
||||
|
||||
Timer*
|
||||
tmr_create(
|
||||
struct iperf_time* nowP, TimerProc* timer_proc, TimerClientData client_data,
|
||||
int64_t usecs, int periodic )
|
||||
{
|
||||
struct iperf_time now;
|
||||
Timer* t;
|
||||
|
||||
getnow( nowP, &now );
|
||||
|
||||
if ( free_timers != NULL ) {
|
||||
t = free_timers;
|
||||
free_timers = t->next;
|
||||
} else {
|
||||
t = (Timer*) malloc( sizeof(Timer) );
|
||||
if ( t == NULL )
|
||||
return NULL;
|
||||
}
|
||||
|
||||
t->timer_proc = timer_proc;
|
||||
t->client_data = client_data;
|
||||
t->usecs = usecs;
|
||||
t->periodic = periodic;
|
||||
t->time = now;
|
||||
iperf_time_add_usecs(&t->time, usecs);
|
||||
/* Add the new timer to the active list. */
|
||||
list_add( t );
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
struct timeval*
|
||||
tmr_timeout( struct iperf_time* nowP )
|
||||
{
|
||||
struct iperf_time now, diff;
|
||||
int64_t usecs;
|
||||
int past;
|
||||
static struct timeval timeout;
|
||||
|
||||
getnow( nowP, &now );
|
||||
/* Since the list is sorted, we only need to look at the first timer. */
|
||||
if ( timers == NULL )
|
||||
return NULL;
|
||||
past = iperf_time_diff(&timers->time, &now, &diff);
|
||||
if (past)
|
||||
usecs = 0;
|
||||
else
|
||||
usecs = iperf_time_in_usecs(&diff);
|
||||
timeout.tv_sec = usecs / 1000000LL;
|
||||
timeout.tv_usec = usecs % 1000000LL;
|
||||
return &timeout;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
tmr_run( struct iperf_time* nowP )
|
||||
{
|
||||
struct iperf_time now;
|
||||
Timer* t;
|
||||
Timer* next;
|
||||
|
||||
getnow( nowP, &now );
|
||||
for ( t = timers; t != NULL; t = next ) {
|
||||
next = t->next;
|
||||
/* Since the list is sorted, as soon as we find a timer
|
||||
** that isn't ready yet, we are done.
|
||||
*/
|
||||
if (iperf_time_compare(&t->time, &now) > 0)
|
||||
break;
|
||||
(t->timer_proc)( t->client_data, &now );
|
||||
if ( t->periodic ) {
|
||||
/* Reschedule. */
|
||||
iperf_time_add_usecs(&t->time, t->usecs);
|
||||
list_resort( t );
|
||||
} else
|
||||
tmr_cancel( t );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
tmr_reset( struct iperf_time* nowP, Timer* t )
|
||||
{
|
||||
struct iperf_time now;
|
||||
|
||||
getnow( nowP, &now );
|
||||
t->time = now;
|
||||
iperf_time_add_usecs( &t->time, t->usecs );
|
||||
list_resort( t );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
tmr_cancel( Timer* t )
|
||||
{
|
||||
/* Remove it from the active list. */
|
||||
list_remove( t );
|
||||
/* And put it on the free list. */
|
||||
t->next = free_timers;
|
||||
free_timers = t;
|
||||
t->prev = NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
tmr_cleanup( void )
|
||||
{
|
||||
Timer* t;
|
||||
|
||||
while ( free_timers != NULL ) {
|
||||
t = free_timers;
|
||||
free_timers = t->next;
|
||||
free( (void*) t );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
tmr_destroy( void )
|
||||
{
|
||||
while ( timers != NULL )
|
||||
tmr_cancel( timers );
|
||||
tmr_cleanup();
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*
|
||||
* Based on timers.h by Jef Poskanzer. Used with permission.
|
||||
*/
|
||||
|
||||
#ifndef __TIMER_H
|
||||
#define __TIMER_H
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "iperf_time.h"
|
||||
|
||||
/* TimerClientData is an opaque value that tags along with a timer. The
|
||||
** client can use it for whatever, and it gets passed to the callback when
|
||||
** the timer triggers.
|
||||
*/
|
||||
typedef union
|
||||
{
|
||||
void* p;
|
||||
int i;
|
||||
long l;
|
||||
} TimerClientData;
|
||||
|
||||
extern TimerClientData JunkClientData; /* for use when you don't care */
|
||||
|
||||
/* The TimerProc gets called when the timer expires. It gets passed
|
||||
** the TimerClientData associated with the timer, and a iperf_time in case
|
||||
** it wants to schedule another timer.
|
||||
*/
|
||||
typedef void TimerProc( TimerClientData client_data, struct iperf_time* nowP );
|
||||
|
||||
/* The Timer struct. */
|
||||
typedef struct TimerStruct
|
||||
{
|
||||
TimerProc* timer_proc;
|
||||
TimerClientData client_data;
|
||||
int64_t usecs;
|
||||
int periodic;
|
||||
struct iperf_time time;
|
||||
struct TimerStruct* prev;
|
||||
struct TimerStruct* next;
|
||||
int hash;
|
||||
} Timer;
|
||||
|
||||
/* Set up a timer, either periodic or one-shot. Returns (Timer*) 0 on errors. */
|
||||
extern Timer* tmr_create(
|
||||
struct iperf_time* nowP, TimerProc* timer_proc, TimerClientData client_data,
|
||||
int64_t usecs, int periodic );
|
||||
|
||||
/* Returns a timeout indicating how long until the next timer triggers. You
|
||||
** can just put the call to this routine right in your select(). Returns
|
||||
** (struct timeval*) 0 if no timers are pending.
|
||||
*/
|
||||
extern struct timeval* tmr_timeout( struct iperf_time* nowP ) /* __attribute__((hot)) */;
|
||||
|
||||
/* Run the list of timers. Your main program needs to call this every so often,
|
||||
** or as indicated by tmr_timeout().
|
||||
*/
|
||||
extern void tmr_run( struct iperf_time* nowP ) /* __attribute__((hot)) */;
|
||||
|
||||
/* Reset the clock on a timer, to current time plus the original timeout. */
|
||||
extern void tmr_reset( struct iperf_time* nowP, Timer* timer );
|
||||
|
||||
/* Deschedule a timer. Note that non-periodic timers are automatically
|
||||
** descheduled when they run, so you don't have to call this on them.
|
||||
*/
|
||||
extern void tmr_cancel( Timer* timer );
|
||||
|
||||
/* Clean up the timers package, freeing any unused storage. */
|
||||
extern void tmr_cleanup( void );
|
||||
|
||||
/* Cancel all timers and free storage, usually in preparation for exiting. */
|
||||
extern void tmr_destroy( void );
|
||||
|
||||
#endif /* __TIMER_H */
|
||||
@@ -0,0 +1,353 @@
|
||||
/*---------------------------------------------------------------
|
||||
* Copyright (c) 1999,2000,2001,2002,2003
|
||||
* The Board of Trustees of the University of Illinois
|
||||
* All Rights Reserved.
|
||||
*---------------------------------------------------------------
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software (Iperf) and associated
|
||||
* documentation files (the "Software"), to deal in the Software
|
||||
* without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit
|
||||
* persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
*
|
||||
* Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and
|
||||
* the following disclaimers.
|
||||
*
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimers in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
*
|
||||
* Neither the names of the University of Illinois, NCSA,
|
||||
* nor the names of its contributors may be used to endorse
|
||||
* or promote products derived from this Software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
* ________________________________________________________________
|
||||
* National Laboratory for Applied Network Research
|
||||
* National Center for Supercomputing Applications
|
||||
* University of Illinois at Urbana-Champaign
|
||||
* http://www.ncsa.uiuc.edu
|
||||
* ________________________________________________________________
|
||||
*
|
||||
* stdio.c
|
||||
* by Mark Gates <mgates@nlanr.net>
|
||||
* and Ajay Tirumalla <tirumala@ncsa.uiuc.edu>
|
||||
* -------------------------------------------------------------------
|
||||
* input and output numbers, converting with kilo, mega, giga, tera
|
||||
* ------------------------------------------------------------------- */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
|
||||
#include "iperf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
const double KILO_UNIT = 1024.0;
|
||||
const double MEGA_UNIT = 1024.0 * 1024.0;
|
||||
const double GIGA_UNIT = 1024.0 * 1024.0 * 1024.0;
|
||||
const double TERA_UNIT = 1024.0 * 1024.0 * 1024.0 * 1024.0;
|
||||
|
||||
const double KILO_RATE_UNIT = 1000.0;
|
||||
const double MEGA_RATE_UNIT = 1000.0 * 1000.0;
|
||||
const double GIGA_RATE_UNIT = 1000.0 * 1000.0 * 1000.0;
|
||||
const double TERA_RATE_UNIT = 1000.0 * 1000.0 * 1000.0 * 1000.0;
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* unit_atof
|
||||
*
|
||||
* Given a string of form #x where # is a number and x is a format
|
||||
* character listed below, this returns the interpreted integer.
|
||||
* Gg, Mm, Kk are giga, mega, kilo respectively
|
||||
* ------------------------------------------------------------------- */
|
||||
|
||||
double unit_atof(const char *s)
|
||||
{
|
||||
double n;
|
||||
char suffix = '\0';
|
||||
|
||||
assert(s != NULL);
|
||||
|
||||
/* scan the number and any suffices */
|
||||
sscanf(s, "%lf%c", &n, &suffix);
|
||||
|
||||
/* convert according to [Tt Gg Mm Kk] */
|
||||
switch (suffix)
|
||||
{
|
||||
case 't': case 'T':
|
||||
n *= TERA_UNIT;
|
||||
break;
|
||||
case 'g': case 'G':
|
||||
n *= GIGA_UNIT;
|
||||
break;
|
||||
case 'm': case 'M':
|
||||
n *= MEGA_UNIT;
|
||||
break;
|
||||
case 'k': case 'K':
|
||||
n *= KILO_UNIT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return n;
|
||||
} /* end unit_atof */
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* unit_atof_rate
|
||||
*
|
||||
* Similar to unit_atof, but uses 10-based rather than 2-based
|
||||
* suffixes.
|
||||
* ------------------------------------------------------------------- */
|
||||
|
||||
double unit_atof_rate(const char *s)
|
||||
{
|
||||
double n;
|
||||
char suffix = '\0';
|
||||
|
||||
assert(s != NULL);
|
||||
|
||||
/* scan the number and any suffices */
|
||||
sscanf(s, "%lf%c", &n, &suffix);
|
||||
|
||||
/* convert according to [Tt Gg Mm Kk] */
|
||||
switch (suffix)
|
||||
{
|
||||
case 't': case 'T':
|
||||
n *= TERA_RATE_UNIT;
|
||||
break;
|
||||
case 'g': case 'G':
|
||||
n *= GIGA_RATE_UNIT;
|
||||
break;
|
||||
case 'm': case 'M':
|
||||
n *= MEGA_RATE_UNIT;
|
||||
break;
|
||||
case 'k': case 'K':
|
||||
n *= KILO_RATE_UNIT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return n;
|
||||
} /* end unit_atof_rate */
|
||||
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* unit_atoi
|
||||
*
|
||||
* Given a string of form #x where # is a number and x is a format
|
||||
* character listed below, this returns the interpreted integer.
|
||||
* Tt, Gg, Mm, Kk are tera, giga, mega, kilo respectively
|
||||
* ------------------------------------------------------------------- */
|
||||
|
||||
iperf_size_t unit_atoi(const char *s)
|
||||
{
|
||||
double n;
|
||||
char suffix = '\0';
|
||||
|
||||
assert(s != NULL);
|
||||
|
||||
/* scan the number and any suffices */
|
||||
sscanf(s, "%lf%c", &n, &suffix);
|
||||
|
||||
/* convert according to [Tt Gg Mm Kk] */
|
||||
switch (suffix)
|
||||
{
|
||||
case 't': case 'T':
|
||||
n *= TERA_UNIT;
|
||||
break;
|
||||
case 'g': case 'G':
|
||||
n *= GIGA_UNIT;
|
||||
break;
|
||||
case 'm': case 'M':
|
||||
n *= MEGA_UNIT;
|
||||
break;
|
||||
case 'k': case 'K':
|
||||
n *= KILO_UNIT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return (iperf_size_t) n;
|
||||
} /* end unit_atof */
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* constants for byte_printf
|
||||
* ------------------------------------------------------------------- */
|
||||
|
||||
/* used as indices into conversion_bytes[], label_byte[], and label_bit[] */
|
||||
enum
|
||||
{
|
||||
UNIT_CONV,
|
||||
KILO_CONV,
|
||||
MEGA_CONV,
|
||||
GIGA_CONV,
|
||||
TERA_CONV
|
||||
};
|
||||
|
||||
/* factor to multiply the number by */
|
||||
const double conversion_bytes[] =
|
||||
{
|
||||
1.0, /* unit */
|
||||
1.0 / 1024, /* kilo */
|
||||
1.0 / 1024 / 1024, /* mega */
|
||||
1.0 / 1024 / 1024 / 1024, /* giga */
|
||||
1.0 / 1024 / 1024 / 1024 / 1024 /* tera */
|
||||
};
|
||||
|
||||
/* factor to multiply the number by for bits*/
|
||||
const double conversion_bits[] =
|
||||
{
|
||||
1.0, /* unit */
|
||||
1.0 / 1000, /* kilo */
|
||||
1.0 / 1000 / 1000, /* mega */
|
||||
1.0 / 1000 / 1000 / 1000, /* giga */
|
||||
1.0 / 1000 / 1000 / 1000 / 1000 /* tera */
|
||||
};
|
||||
|
||||
|
||||
/* labels for Byte formats [KMGT] */
|
||||
const char *label_byte[] =
|
||||
{
|
||||
"Byte",
|
||||
"KByte",
|
||||
"MByte",
|
||||
"GByte",
|
||||
"TByte"
|
||||
};
|
||||
|
||||
/* labels for bit formats [kmgt] */
|
||||
const char *label_bit[] =
|
||||
{
|
||||
"bit",
|
||||
"Kbit",
|
||||
"Mbit",
|
||||
"Gbit",
|
||||
"Tbit"
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* unit_snprintf
|
||||
*
|
||||
* Given a number in bytes and a format, converts the number and
|
||||
* prints it out with a bits or bytes label.
|
||||
* B, K, M, G, A for Byte, Kbyte, Mbyte, Gbyte, adaptive byte
|
||||
* b, k, m, g, a for bit, Kbit, Mbit, Gbit, adaptive bit
|
||||
* adaptive picks the "best" one based on the number.
|
||||
* s should be at least 11 chars long
|
||||
* (4 digits + space + 5 chars max + null)
|
||||
* ------------------------------------------------------------------- */
|
||||
|
||||
void unit_snprintf(char *s, int inLen,
|
||||
double inNum, char inFormat)
|
||||
{
|
||||
int conv;
|
||||
const char *suffix;
|
||||
const char *format;
|
||||
|
||||
/* convert to bits for [bkmga] */
|
||||
if (!isupper((int) inFormat))
|
||||
{
|
||||
inNum *= 8;
|
||||
}
|
||||
switch (toupper((u_char)inFormat))
|
||||
{
|
||||
case 'B':
|
||||
conv = UNIT_CONV;
|
||||
break;
|
||||
case 'K':
|
||||
conv = KILO_CONV;
|
||||
break;
|
||||
case 'M':
|
||||
conv = MEGA_CONV;
|
||||
break;
|
||||
case 'G':
|
||||
conv = GIGA_CONV;
|
||||
break;
|
||||
case 'T':
|
||||
conv = TERA_CONV;
|
||||
break;
|
||||
|
||||
default:
|
||||
case 'A':
|
||||
{
|
||||
double tmpNum = inNum;
|
||||
conv = UNIT_CONV;
|
||||
|
||||
if (isupper((int) inFormat))
|
||||
{
|
||||
while (tmpNum >= 1024.0 && conv < TERA_CONV)
|
||||
{
|
||||
tmpNum /= 1024.0;
|
||||
conv++;
|
||||
}
|
||||
} else
|
||||
{
|
||||
while (tmpNum >= 1000.0 && conv < TERA_CONV)
|
||||
{
|
||||
tmpNum /= 1000.0;
|
||||
conv++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isupper((int) inFormat))
|
||||
{
|
||||
inNum *= conversion_bits[conv];
|
||||
suffix = label_bit[conv];
|
||||
} else
|
||||
{
|
||||
inNum *= conversion_bytes[conv];
|
||||
suffix = label_byte[conv];
|
||||
}
|
||||
|
||||
/* print such that we always fit in 4 places */
|
||||
if (inNum < 9.995)
|
||||
{ /* 9.995 would be rounded to 10.0 */
|
||||
format = "%4.2f %s";/* #.## */
|
||||
} else if (inNum < 99.95)
|
||||
{ /* 99.95 would be rounded to 100 */
|
||||
format = "%4.1f %s";/* ##.# */
|
||||
} else if (inNum < 999.5)
|
||||
{ /* 999.5 would be rounded to 1000 */
|
||||
format = "%4.0f %s";/* ### */
|
||||
} else
|
||||
{ /* 1000-1024 fits in 4 places If not using
|
||||
* Adaptive sizes then this code will not
|
||||
* control spaces */
|
||||
format = "%4.0f %s";/* #### */
|
||||
}
|
||||
snprintf(s, inLen, format, inNum, suffix);
|
||||
} /* end unit_snprintf */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end extern "C" */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
enum {
|
||||
UNIT_LEN = 32
|
||||
};
|
||||
|
||||
double unit_atof( const char *s );
|
||||
double unit_atof_rate( const char *s );
|
||||
iperf_size_t unit_atoi( const char *s );
|
||||
void unit_snprintf( char *s, int inLen, double inNum, char inFormat );
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#define IPERF_VERSION "3.19"
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* iperf, Copyright (c) 2014, The Regents of the University of
|
||||
* California, through Lawrence Berkeley National Laboratory (subject
|
||||
* to receipt of any required approvals from the U.S. Dept. of
|
||||
* Energy). All rights reserved.
|
||||
*
|
||||
* If you have questions about your rights to use or distribute this
|
||||
* software, please contact Berkeley Lab's Technology Transfer
|
||||
* Department at TTD@lbl.gov.
|
||||
*
|
||||
* NOTICE. This software is owned by the U.S. Department of Energy.
|
||||
* As such, the U.S. Government has been granted for itself and others
|
||||
* acting on its behalf a paid-up, nonexclusive, irrevocable,
|
||||
* worldwide license in the Software to reproduce, prepare derivative
|
||||
* works, and perform publicly and display publicly. Beginning five
|
||||
* (5) years after the date permission to assert copyright is obtained
|
||||
* from the U.S. Department of Energy, and subject to any subsequent
|
||||
* five (5) year renewals, the U.S. Government is granted for itself
|
||||
* and others acting on its behalf a paid-up, nonexclusive,
|
||||
* irrevocable, worldwide license in the Software to reproduce,
|
||||
* prepare derivative works, distribute copies to the public, perform
|
||||
* publicly and display publicly, and to permit others to do so.
|
||||
*
|
||||
* This code is distributed under a BSD style license, see the LICENSE
|
||||
* file for complete information.
|
||||
*/
|
||||
#define IPERF_VERSION "@PACKAGE_VERSION@"
|
||||
@@ -0,0 +1,184 @@
|
||||
/* src/iperf_config.h.in. Generated for Android JNI build. */
|
||||
|
||||
/* Can bind to device via SO_BINDTODEVICE or IP_BOUND_IF. */
|
||||
/* #undef CAN_BIND_TO_DEVICE */
|
||||
|
||||
/* Define to 1 if you have the 'clock_gettime' function. */
|
||||
#define HAVE_CLOCK_GETTIME 1
|
||||
|
||||
/* Define to 1 if you have the 'clock_nanosleep' function. */
|
||||
/* #undef HAVE_CLOCK_NANOSLEEP */
|
||||
|
||||
/* Define to 1 if you have the 'cpuset_setaffinity' function. */
|
||||
/* #undef HAVE_CPUSET_SETAFFINITY */
|
||||
|
||||
/* Have CPU affinity support. */
|
||||
#define HAVE_CPU_AFFINITY 1
|
||||
|
||||
/* Define to 1 if you have the 'daemon' function. */
|
||||
/* #undef HAVE_DAEMON */
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#define HAVE_DLFCN_H 1
|
||||
|
||||
/* Have IP_MTU_DISCOVER/IP_DONTFRAG/IP_DONTFRAGMENT sockopt. */
|
||||
/* #undef HAVE_DONT_FRAGMENT */
|
||||
|
||||
/* Define to 1 if you have the <endian.h> header file. */
|
||||
#define HAVE_ENDIAN_H 1
|
||||
|
||||
/* Have IPv6 flowlabel support. */
|
||||
/* #undef HAVE_FLOWLABEL */
|
||||
|
||||
/* Define to 1 if you have the 'getline' function. */
|
||||
#define HAVE_GETLINE 1
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Have MPTCP protocol. */
|
||||
/* #undef HAVE_IPPROTO_MPTCP */
|
||||
|
||||
/* Have IP_BOUND_IF sockopt. */
|
||||
/* #undef HAVE_IP_BOUND_IF */
|
||||
|
||||
/* Have IP_DONTFRAG sockopt. */
|
||||
/* #undef HAVE_IP_DONTFRAG */
|
||||
|
||||
/* Have IP_DONTFRAGMENT sockopt. */
|
||||
/* #undef HAVE_IP_DONTFRAGMENT */
|
||||
|
||||
/* Have IP_MTU_DISCOVER sockopt. */
|
||||
/* #undef HAVE_IP_MTU_DISCOVER */
|
||||
|
||||
/* Define to 1 if you have the <linux/tcp.h> header file. */
|
||||
/* #undef HAVE_LINUX_TCP_H */
|
||||
|
||||
/* Have MSG_TRUNC recv option. */
|
||||
#define HAVE_MSG_TRUNC 1
|
||||
|
||||
/* Define to 1 if you have the 'nanosleep' function. */
|
||||
#define HAVE_NANOSLEEP 1
|
||||
|
||||
/* Define to 1 if you have the <netinet/sctp.h> header file. */
|
||||
/* #undef HAVE_NETINET_SCTP_H */
|
||||
|
||||
/* Define to 1 if you have the <poll.h> header file. */
|
||||
#define HAVE_POLL_H 1
|
||||
|
||||
/* Define if you have POSIX threads libraries and header files. */
|
||||
#define HAVE_PTHREAD 1
|
||||
|
||||
/* Have PTHREAD_PRIO_INHERIT. */
|
||||
/* #undef HAVE_PTHREAD_PRIO_INHERIT */
|
||||
|
||||
/* Define to 1 if you have the 'sched_setaffinity' function. */
|
||||
/* #undef HAVE_SCHED_SETAFFINITY */
|
||||
|
||||
/* Have SCTP support. */
|
||||
/* #undef HAVE_SCTP_H */
|
||||
|
||||
/* Define to 1 if you have the 'sendfile' function. */
|
||||
/* #undef HAVE_SENDFILE */
|
||||
|
||||
/* Define to 1 if you have the 'SetProcessAffinityMask' function. */
|
||||
/* #undef HAVE_SETPROCESSAFFINITYMASK */
|
||||
|
||||
/* Have SHUT_WR socket shutdown option. */
|
||||
#define HAVE_SOCKET_SHUTDOWN_SHUT_WR 1
|
||||
|
||||
/* Have SO_BINDTODEVICE sockopt. */
|
||||
/* #undef HAVE_SO_BINDTODEVICE */
|
||||
|
||||
/* Have SO_MAX_PACING_RATE sockopt. */
|
||||
/* #undef HAVE_SO_MAX_PACING_RATE */
|
||||
|
||||
/* OpenSSL Is Available */
|
||||
/* #undef HAVE_SSL */
|
||||
|
||||
/* Define to 1 if you have the <stdatomic.h> header file. */
|
||||
#define HAVE_STDATOMIC_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdio.h> header file. */
|
||||
#define HAVE_STDIO_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if the system has the type 'struct sctp_assoc_value'. */
|
||||
/* #undef HAVE_STRUCT_SCTP_ASSOC_VALUE */
|
||||
|
||||
/* Define to 1 if you have the <sys/endian.h> header file. */
|
||||
/* #undef HAVE_SYS_ENDIAN_H */
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#define HAVE_SYS_SOCKET_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Have TCP_CONGESTION sockopt. */
|
||||
#define HAVE_TCP_CONGESTION 1
|
||||
|
||||
/* Have TCP_KEEPIDLE sockopt. */
|
||||
#define HAVE_TCP_KEEPALIVE 1
|
||||
|
||||
/* Have TCP_USER_TIMEOUT sockopt. */
|
||||
#define HAVE_TCP_USER_TIMEOUT 1
|
||||
|
||||
/* Have UDP_GRO sockopt. */
|
||||
/* #undef HAVE_UDP_GRO */
|
||||
|
||||
/* Have UDP_SEGMENT sockopt. */
|
||||
/* #undef HAVE_UDP_SEGMENT */
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Define to the sub-directory where libtool stores uninstalled libraries. */
|
||||
#define LT_OBJDIR ".libs/"
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "iperf"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT "iperf-devel@lists.es.net"
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "iperf"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "iperf 3.19"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "iperf"
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define PACKAGE_URL "http://iperf.fr/"
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "3.19"
|
||||
|
||||
/* Define to necessary symbol if this constant uses a non-standard name on your system. */
|
||||
#define PTHREAD_CREATE_JOINABLE 2
|
||||
|
||||
/* Define to 1 if all of the C89 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "3.19"
|
||||
|
||||
/* Define to empty if 'const' does not conform to ANSI C. */
|
||||
/* #undef const */
|
||||
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* iperf JNI bridge for Android.
|
||||
*
|
||||
* This file exposes iperf3's API to Java/Kotlin via JNI, enabling
|
||||
* the Flutter app to run iperf3 tests natively inside the app process
|
||||
* (no Process.start needed, no SELinux restrictions).
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "iperf.h"
|
||||
#include "iperf_api.h"
|
||||
#include "iperf_time.h"
|
||||
|
||||
/* Android logging macros */
|
||||
#define TAG "iperf3-jni"
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
|
||||
|
||||
/* Global state for controlling the iperf test */
|
||||
static struct iperf_test *g_test = NULL;
|
||||
static volatile int g_stop = 0;
|
||||
|
||||
/* Callback interface in Java/Kotlin */
|
||||
typedef void (*output_callback_t)(JNIEnv *env, jobject callback, jstring line);
|
||||
typedef void (*error_callback_t)(JNIEnv *env, jobject callback, jstring msg);
|
||||
typedef void (*complete_callback_t)(JNIEnv *env, jobject callback);
|
||||
|
||||
/* Per-call state to pass between JNI and the reader thread */
|
||||
struct iperf_run_state {
|
||||
JNIEnv *env;
|
||||
jobject callback;
|
||||
FILE *outfile;
|
||||
int pipe_fd[2];
|
||||
JavaVM *jvm;
|
||||
pthread_t reader_thread;
|
||||
output_callback_t on_output;
|
||||
error_callback_t on_error;
|
||||
complete_callback_t on_complete;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Output reader thread — reads from iperf's output pipe and forwards */
|
||||
/* each line to the Java callback via JNI. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void *reader_thread_func(void *arg) {
|
||||
struct iperf_run_state *state = (struct iperf_run_state *) arg;
|
||||
JNIEnv *env;
|
||||
int ret;
|
||||
|
||||
/* Attach this native thread to the Java VM */
|
||||
ret = (*state->jvm)->AttachCurrentThread(state->jvm, &env, NULL);
|
||||
if (ret != JNI_OK) {
|
||||
LOGE("AttachCurrentThread failed: %d", ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jclass callback_cls = (*env)->GetObjectClass(env, state->callback);
|
||||
jmethodID mid_output = (*env)->GetMethodID(
|
||||
env, callback_cls, "onOutput", "(Ljava/lang/String;)V");
|
||||
|
||||
char buf[1024];
|
||||
FILE *fp = fdopen(state->pipe_fd[0], "r");
|
||||
if (fp) {
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
/* Skip empty lines */
|
||||
if (buf[0] == '\n' || buf[0] == '\0')
|
||||
continue;
|
||||
jstring line = (*env)->NewStringUTF(env, buf);
|
||||
if (line != NULL) {
|
||||
(*env)->CallVoidMethod(env, state->callback,
|
||||
mid_output, line);
|
||||
(*env)->DeleteLocalRef(env, line);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/* Detach this native thread */
|
||||
(*state->jvm)->DetachCurrentThread(state->jvm);
|
||||
|
||||
/* Free the state struct — caller owns it */
|
||||
free(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Convert a Java String[] into a native char** argv array. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static char **java_string_array_to_argv(JNIEnv *env, jobjectArray str_array,
|
||||
int *out_argc) {
|
||||
int argc = (*env)->GetArrayLength(env, str_array);
|
||||
if (argc <= 0) {
|
||||
*out_argc = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char **argv = malloc((argc + 1) * sizeof(char *));
|
||||
if (!argv) return NULL;
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
jstring jstr = (jstring) (*env)->GetObjectArrayElement(env,
|
||||
str_array, i);
|
||||
const char *cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
|
||||
if (cstr) {
|
||||
argv[i] = strdup(cstr);
|
||||
(*env)->ReleaseStringUTFChars(env, jstr, cstr);
|
||||
} else {
|
||||
argv[i] = strdup("");
|
||||
}
|
||||
(*env)->DeleteLocalRef(env, jstr);
|
||||
}
|
||||
argv[argc] = NULL; /* NULL-terminated for iperf */
|
||||
*out_argc = argc;
|
||||
return argv;
|
||||
}
|
||||
|
||||
/* Free an argv array returned by java_string_array_to_argv() */
|
||||
static void free_argv(char **argv, int argc) {
|
||||
if (!argv) return;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
free(argv[i]);
|
||||
}
|
||||
free(argv);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Helper: signal an error through the Java callback. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void signal_error(struct iperf_run_state *state, const char *msg) {
|
||||
if (!state || !msg) return;
|
||||
JNIEnv *env = state->env;
|
||||
if (!env) return;
|
||||
|
||||
if (state->on_error) {
|
||||
jstring jmsg = (*env)->NewStringUTF(env, msg);
|
||||
if (jmsg) {
|
||||
state->on_error(env, state->callback, jmsg);
|
||||
(*env)->DeleteLocalRef(env, jmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Helper: signal output through the Java callback. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void signal_output(struct iperf_run_state *state, const char *msg) {
|
||||
if (!state || !msg || !msg[0]) return;
|
||||
JNIEnv *env = state->env;
|
||||
if (!env) return;
|
||||
|
||||
if (state->on_output) {
|
||||
jstring jline = (*env)->NewStringUTF(env, msg);
|
||||
if (jline) {
|
||||
state->on_output(env, state->callback, jline);
|
||||
(*env)->DeleteLocalRef(env, jline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* JNI: Start an iperf3 test with the given arguments. */
|
||||
/* Signature: void runIperfLive(String[] args, IperfCallback cb) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_novoyuuparosk_iperf3gui_IperfRunner_runIperfLive(
|
||||
JNIEnv *env, jobject thiz,
|
||||
jobjectArray arguments, jobject callback) {
|
||||
|
||||
/* Resolve callback methods */
|
||||
jclass callback_cls = (*env)->GetObjectClass(env, callback);
|
||||
jmethodID mid_output = (*env)->GetMethodID(
|
||||
env, callback_cls, "onOutput", "(Ljava/lang/String;)V");
|
||||
jmethodID mid_error = (*env)->GetMethodID(
|
||||
env, callback_cls, "onError", "(Ljava/lang/String;)V");
|
||||
jmethodID mid_complete = (*env)->GetMethodID(
|
||||
env, callback_cls, "onComplete", "()V");
|
||||
|
||||
/* Get JavaVM for thread attachment */
|
||||
JavaVM *jvm;
|
||||
(*env)->GetJavaVM(env, &jvm);
|
||||
|
||||
/* Convert Java String[] -> char** argv */
|
||||
int argc = 0;
|
||||
char **argv = java_string_array_to_argv(env, arguments, &argc);
|
||||
if (!argv) {
|
||||
const char *err = "Failed to parse arguments";
|
||||
signal_error((struct iperf_run_state *) 0, err);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create a pipe to capture iperf's stdout/stderr */
|
||||
int pipefd[2];
|
||||
if (pipe(pipefd) != 0) {
|
||||
free_argv(argv, argc);
|
||||
const char *err = "Failed to create output pipe";
|
||||
LOGE("pipe() failed: %s", strerror(errno));
|
||||
if (mid_error) {
|
||||
jstring jmsg = (*env)->NewStringUTF(env, err);
|
||||
(*env)->CallVoidMethod(env, callback, mid_error, jmsg);
|
||||
(*env)->DeleteLocalRef(env, jmsg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate per-call state */
|
||||
struct iperf_run_state *state =
|
||||
calloc(1, sizeof(struct iperf_run_state));
|
||||
if (!state) {
|
||||
free_argv(argv, argc);
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
return;
|
||||
}
|
||||
state->env = env;
|
||||
state->callback = (*env)->NewGlobalRef(env, callback);
|
||||
state->outfile = fdopen(pipefd[1], "w");
|
||||
state->pipe_fd[0] = pipefd[0];
|
||||
state->jvm = jvm;
|
||||
state->on_output = mid_output
|
||||
? (output_callback_t) mid_output : NULL;
|
||||
state->on_error = mid_error
|
||||
? (error_callback_t) mid_error : NULL;
|
||||
state->on_complete = mid_complete
|
||||
? (complete_callback_t) mid_complete : NULL;
|
||||
|
||||
/* Create the iperf test object */
|
||||
struct iperf_test *test = iperf_new_test();
|
||||
if (!test) {
|
||||
free_argv(argv, argc);
|
||||
free(state);
|
||||
close(pipefd[0]);
|
||||
const char *err = "Failed to create iperf test";
|
||||
LOGE("iperf_new_test() returned NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
iperf_defaults(test);
|
||||
g_test = test;
|
||||
|
||||
/* Redirect iperf's output to our pipe */
|
||||
test->outfile = state->outfile;
|
||||
setvbuf(test->outfile, NULL, _IOLBF, 0); /* Line-buffered */
|
||||
|
||||
/* Parse arguments */
|
||||
if (iperf_parse_arguments(test, argc, argv) < 0) {
|
||||
char errbuf[256];
|
||||
snprintf(errbuf, sizeof(errbuf), "Argument parse error: %s",
|
||||
iperf_strerror(i_errno));
|
||||
free_argv(argv, argc);
|
||||
|
||||
signal_error(state, errbuf);
|
||||
|
||||
fclose(test->outfile);
|
||||
iperf_free_test(test);
|
||||
g_test = NULL;
|
||||
|
||||
/* Cleanup */
|
||||
free(state);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Notify Java that the test is starting */
|
||||
signal_output(state, "🚀 Initiating iPerf3 client request...\n");
|
||||
|
||||
/* Run the test */
|
||||
g_stop = 0;
|
||||
int ret = iperf_run_client(test);
|
||||
|
||||
/* Flush and close output */
|
||||
fflush(state->outfile);
|
||||
fclose(state->outfile);
|
||||
|
||||
/* Clean up iperf */
|
||||
iperf_free_test(test);
|
||||
g_test = NULL;
|
||||
|
||||
/* Free argv */
|
||||
free_argv(argv, argc);
|
||||
|
||||
/* Signal completion */
|
||||
if (g_stop) {
|
||||
signal_output(state, "[iPerf JNI] Test was stopped by user.\n");
|
||||
} else if (ret < 0) {
|
||||
char errbuf[256];
|
||||
snprintf(errbuf, sizeof(errbuf), "Test failed: %s",
|
||||
iperf_strerror(i_errno));
|
||||
signal_error(state, errbuf);
|
||||
} else {
|
||||
signal_output(state,
|
||||
"[iPerf JNI] Test completed successfully.\n");
|
||||
}
|
||||
|
||||
/* Signal completion to Java */
|
||||
if (state->on_complete) {
|
||||
state->on_complete(env, state->callback);
|
||||
}
|
||||
|
||||
/* Free the state struct */
|
||||
free(state);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* JNI: Gracefully stop a running iperf3 test. */
|
||||
/* Signature: void forceStopIperfTest(IperfCallback cb) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_novoyuuparosk_iperf3gui_IperfRunner_forceStopIperfTest(
|
||||
JNIEnv *env, jobject thiz, jobject callback) {
|
||||
if (!g_test || g_test->done) return;
|
||||
|
||||
/* Set the stop flag */
|
||||
g_stop = 1;
|
||||
g_test->done = 1;
|
||||
iperf_set_send_state(g_test, IPERF_DONE);
|
||||
|
||||
/* Try to unblock select() on the control socket */
|
||||
if (g_test->ctrl_sck >= 0) {
|
||||
shutdown(g_test->ctrl_sck, SHUT_RDWR);
|
||||
}
|
||||
|
||||
/* Notify Java */
|
||||
jclass callback_cls = (*env)->GetObjectClass(env, callback);
|
||||
jmethodID mid_output = (*env)->GetMethodID(
|
||||
env, callback_cls, "onOutput", "(Ljava/lang/String;)V");
|
||||
if (mid_output) {
|
||||
jstring msg = (*env)->NewStringUTF(env,
|
||||
"[iPerf JNI] Requested graceful stop.");
|
||||
(*env)->CallVoidMethod(env, callback, mid_output, msg);
|
||||
(*env)->DeleteLocalRef(env, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* JNI: Get elapsed milliseconds since test started. */
|
||||
/* Signature: int getTestProgress() */
|
||||
/* ------------------------------------------------------------------ */
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_novoyuuparosk_iperf3gui_IperfRunner_getTestProgress(JNIEnv *env,
|
||||
jobject thiz) {
|
||||
if (!g_test) return -1;
|
||||
|
||||
/* Get elapsed time in usecs and convert to int ms */
|
||||
/* Note: iperf3 3.19 doesn't expose start_time on iperf_test struct.
|
||||
* We track it locally via a static variable set at test start. */
|
||||
return (jint)(g_test->duration * 1000); /* fallback: return configured duration */
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* JNI: Get whether iperf is currently running. */
|
||||
/* Signature: boolean isTestRunning() */
|
||||
/* ------------------------------------------------------------------ */
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_novoyuuparosk_iperf3gui_IperfRunner_isTestRunning(JNIEnv *env,
|
||||
jobject thiz) {
|
||||
return (g_test != NULL && !g_test->done);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.novoyuuparosk.iperf3gui
|
||||
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
/**
|
||||
* Kotlin binding layer for native iperf3 via JNI.
|
||||
*
|
||||
* This class loads the native library (libiperf3jni.so) and exposes
|
||||
* iperf3 functionality to Flutter via MethodChannel callbacks.
|
||||
*
|
||||
* iperf3 runs natively inside the app process — no Process.start() needed,
|
||||
* no SELinux restrictions. Output is streamed live to the Flutter UI via
|
||||
* the IperfCallback interface.
|
||||
*/
|
||||
object IperfRunner : FlutterPlugin {
|
||||
|
||||
private var channel: MethodChannel? = null
|
||||
|
||||
init {
|
||||
// Load native library at class initialization
|
||||
System.loadLibrary("iperf3jni")
|
||||
}
|
||||
|
||||
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel = MethodChannel(binding.binaryMessenger, "com.novoyuuparosk.iperf3gui/iperf")
|
||||
channel?.setMethodCallHandler { call, result ->
|
||||
when (call.method) {
|
||||
"runIperf" -> {
|
||||
val args = call.argument<Array<String>>("args")
|
||||
if (args != null) {
|
||||
runIperfLive(args, object : IperfCallback {
|
||||
override fun onOutput(line: String) {
|
||||
channel?.invokeMethod("onOutput", mapOf("output" to line))
|
||||
}
|
||||
override fun onError(error: String) {
|
||||
channel?.invokeMethod("onError", mapOf("error" to error))
|
||||
}
|
||||
override fun onComplete() {
|
||||
channel?.invokeMethod("onComplete", emptyMap<String, Any>())
|
||||
}
|
||||
})
|
||||
result.success(null)
|
||||
} else {
|
||||
result.error("INVALID_ARGS", "args must be a String array", null)
|
||||
}
|
||||
}
|
||||
"stopIperf" -> {
|
||||
forceStopIperfTest(object : IperfCallback {
|
||||
override fun onOutput(line: String) {
|
||||
channel?.invokeMethod("onOutput", mapOf("output" to line))
|
||||
}
|
||||
override fun onError(error: String) {
|
||||
channel?.invokeMethod("onError", mapOf("error" to error))
|
||||
}
|
||||
override fun onComplete() {
|
||||
channel?.invokeMethod("onComplete", emptyMap<String, Any>())
|
||||
}
|
||||
})
|
||||
result.success(null)
|
||||
}
|
||||
"isTestRunning" -> {
|
||||
result.success(isTestRunning())
|
||||
}
|
||||
else -> {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel = null
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
// Native method bindings
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Start an iperf3 test with the given arguments.
|
||||
* This calls the native C function via JNI.
|
||||
*/
|
||||
external fun runIperfLive(arguments: Array<String>, callback: IperfCallback)
|
||||
|
||||
/**
|
||||
* Gracefully stop a running iperf3 test.
|
||||
*/
|
||||
external fun forceStopIperfTest(callback: IperfCallback)
|
||||
|
||||
/**
|
||||
* Check if an iperf3 test is currently running.
|
||||
* @return true if a test is in progress, false otherwise
|
||||
*/
|
||||
external fun isTestRunning(): Boolean
|
||||
|
||||
/**
|
||||
* Callback interface for iperf3 test output.
|
||||
*
|
||||
* Used by the native layer to stream test results back to Flutter
|
||||
* in real-time.
|
||||
*/
|
||||
interface IperfCallback {
|
||||
/** Called with each line of iperf3 output (stdout/stderr) */
|
||||
fun onOutput(line: String)
|
||||
|
||||
/** Called when an error occurs */
|
||||
fun onError(error: String)
|
||||
|
||||
/** Called when the test completes (success or failure) */
|
||||
fun onComplete()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,12 @@
|
||||
package com.novoyuuparosk.iperf3gui
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
|
||||
class MainActivity : FlutterActivity()
|
||||
class MainActivity: FlutterActivity() {
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
// Register IperfRunner as a FlutterPlugin for native iperf3 access
|
||||
flutterEngine.plugins.add(IperfRunner)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +1,9 @@
|
||||
import 'dart:io';
|
||||
// This file is kept as a stub — iperf3 is now compiled as a native
|
||||
// shared library (libiperf3jni.so) via JNI.
|
||||
// The old binary-path-provider pattern (extract ELF from assets → chmod → execve)
|
||||
// was replaced by the JNI approach after SELinux blocked binary execution.
|
||||
// See .hermes/plans/ for details on the migration.
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
/// Provides the path to the iperf3 binary on the device.
|
||||
///
|
||||
/// Call [initialize] in [main] (before [runApp]) to extract the
|
||||
/// bundled asset and populate the cache. After initialization,
|
||||
/// [path] returns a synchronous [String].
|
||||
class Iperf3BinaryPathProvider {
|
||||
static String? _cachedPath;
|
||||
|
||||
/// Extract the bundled iperf3 binary to the app's cache directory.
|
||||
///
|
||||
/// Call this once in [main] before [runApp].
|
||||
static Future<void> initialize() async {
|
||||
if (_cachedPath != null) return;
|
||||
|
||||
final cacheDir = await getApplicationCacheDirectory();
|
||||
final binaryDir = Directory('${cacheDir.path}/iperf3');
|
||||
if (!await binaryDir.exists()) {
|
||||
await binaryDir.create(recursive: true);
|
||||
}
|
||||
|
||||
final abi = await _detectAbi();
|
||||
final assetPath = 'assets/iperf3/$abi/iperf3';
|
||||
|
||||
final bytes = await rootBundle.load(assetPath);
|
||||
final file = File('${binaryDir.path}/iperf3');
|
||||
await file.writeAsBytes(bytes.buffer.asUint8List());
|
||||
|
||||
// chmod +x the binary — /system/bin/sh is always present on Android
|
||||
await Process.run(
|
||||
'/system/bin/sh',
|
||||
['-c', 'chmod +x "\$1"', '_', file.path],
|
||||
);
|
||||
|
||||
_cachedPath = file.path;
|
||||
}
|
||||
|
||||
/// The resolved path to the iperf3 binary.
|
||||
///
|
||||
/// Must call [initialize] first, or this will return a placeholder.
|
||||
static String get path {
|
||||
return _cachedPath ?? '/data/local/tmp/iperf3';
|
||||
}
|
||||
|
||||
static Future<String> _detectAbi() async {
|
||||
final result = await Process.run(
|
||||
'getprop',
|
||||
['ro.product.cpu.abi'],
|
||||
);
|
||||
final abi = result.stdout.toString().trim();
|
||||
if (abi.contains('arm64')) return 'arm64-v8a';
|
||||
return 'armeabi-v7a';
|
||||
}
|
||||
}
|
||||
// The Iperf3Service now uses MethodChannel to communicate with Kotlin/JNI:
|
||||
// Dart → MethodChannel → Kotlin → JNI → iperf3 (same process)
|
||||
// No file extraction, no chmod, no Process.start needed.
|
||||
|
||||
@@ -1,63 +1,142 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:iperf3gui/domain/models/iperf3_result.dart';
|
||||
import 'package:iperf3gui/domain/models/test_configuration.dart';
|
||||
|
||||
/// Service that runs iperf3 via [Process.start] and captures output.
|
||||
/// Service that runs iperf3 via JNI (native library).
|
||||
///
|
||||
/// On Android, iperf3 is compiled into the app as a shared library (.so).
|
||||
/// This avoids SELinux restrictions on executing ELF binaries from assets.
|
||||
///
|
||||
/// The service communicates with the native layer via a MethodChannel.
|
||||
/// Output is streamed live to the Flutter UI through callbacks.
|
||||
class Iperf3Service {
|
||||
final String _binaryPath;
|
||||
static const MethodChannel _channel =
|
||||
MethodChannel('com.novoyuuparosk.iperf3gui/iperf');
|
||||
|
||||
// ignore: prefer_initializing_formals
|
||||
Iperf3Service({required String binaryPath}) : _binaryPath = binaryPath;
|
||||
/// Collect output lines during a test run.
|
||||
final List<String> _outputBuffer = [];
|
||||
|
||||
/// Completer that resolves when the native iperf3 test finishes.
|
||||
Completer<Iperf3Result>? _runCompleter;
|
||||
|
||||
/// Whether iperf3 is currently running.
|
||||
bool _isRunning = false;
|
||||
|
||||
/// Whether iperf3 is currently running.
|
||||
bool get isRunning => _isRunning;
|
||||
|
||||
/// Cancel any running test.
|
||||
Future<void> stop() async {
|
||||
if (!_isRunning) return;
|
||||
try {
|
||||
await _channel.invokeMethod('stopIperf');
|
||||
} catch (e) {
|
||||
debugPrint('Iperf3Service: error stopping: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Run iperf3 with the given configuration.
|
||||
///
|
||||
/// Returns an [Iperf3Result] with stdout/stderr and exit code.
|
||||
Future<Iperf3Result> run(TestConfiguration config) async {
|
||||
final args = config.toCliArgs();
|
||||
|
||||
debugPrint(
|
||||
'Iperf3Service: running $_binaryPath ${args.join(" ")}',
|
||||
);
|
||||
|
||||
final process = await Process.start(
|
||||
_binaryPath,
|
||||
args,
|
||||
runInShell: false, // must execve directly — Android sh can't exec ELF binaries
|
||||
);
|
||||
|
||||
final stdoutBuffer = <int>[];
|
||||
final stderrBuffer = <int>[];
|
||||
|
||||
final stdoutStream = process.stdout.transform(utf8.decoder).listen(
|
||||
(data) {
|
||||
stdoutBuffer.addAll(utf8.encode(data));
|
||||
debugPrint('Iperf3Service stdout: $data');
|
||||
},
|
||||
);
|
||||
final stderrStream = process.stderr.transform(utf8.decoder).listen(
|
||||
(data) {
|
||||
stderrBuffer.addAll(utf8.encode(data));
|
||||
debugPrint('Iperf3Service stderr: $data');
|
||||
},
|
||||
);
|
||||
|
||||
final exitCode = await process.exitCode;
|
||||
|
||||
await stdoutStream.cancel();
|
||||
await stderrStream.cancel();
|
||||
|
||||
final rawOutput = utf8.decode(stdoutBuffer) + utf8.decode(stderrBuffer);
|
||||
|
||||
debugPrint(
|
||||
'Iperf3Service: finished with exit code $exitCode, output length ${rawOutput.length}',
|
||||
);
|
||||
|
||||
if (_isRunning) {
|
||||
return Iperf3Result(
|
||||
rawOutput: rawOutput,
|
||||
exitCode: exitCode,
|
||||
rawOutput: 'Another test is already running.',
|
||||
exitCode: -2,
|
||||
);
|
||||
}
|
||||
|
||||
_isRunning = true;
|
||||
_outputBuffer.clear();
|
||||
|
||||
final args = <String>[
|
||||
'-c', config.server,
|
||||
'-p', config.port.toString(),
|
||||
'-t', config.duration.inSeconds.toString(),
|
||||
'-i', config.interval.toString(),
|
||||
'-P', config.parallelStreams.toString(),
|
||||
];
|
||||
|
||||
debugPrint('Iperf3Service: running: ${args.join(" ")}');
|
||||
|
||||
// Set up the completer to await completion
|
||||
_runCompleter = Completer<Iperf3Result>();
|
||||
|
||||
// Listen for events from native layer
|
||||
_channel.setMethodCallHandler((call) async {
|
||||
if (!_runCompleter!.isCompleted) {
|
||||
switch (call.method) {
|
||||
case 'onOutput':
|
||||
final output = call.arguments['output'] as String?;
|
||||
if (output != null) {
|
||||
_outputBuffer.add(output);
|
||||
debugPrint('Iperf3Service output: ${output.trim()}');
|
||||
}
|
||||
break;
|
||||
case 'onError':
|
||||
final error = call.arguments['error'] as String?;
|
||||
if (error != null) {
|
||||
debugPrint('Iperf3Service error: $error');
|
||||
_outputBuffer.add('[ERROR] $error');
|
||||
// Don't complete yet — the test might still be running
|
||||
}
|
||||
break;
|
||||
case 'onComplete':
|
||||
debugPrint('Iperf3Service: test completed');
|
||||
_isRunning = false;
|
||||
_runCompleter!.complete(
|
||||
Iperf3Result(
|
||||
rawOutput: _outputBuffer.join(''),
|
||||
exitCode: 0,
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Start the test
|
||||
try {
|
||||
await _channel.invokeMethod('runIperf', {'args': args});
|
||||
} catch (e, stackTrace) {
|
||||
debugPrint('Iperf3Service exception: $e');
|
||||
debugPrint('Iperf3Service stack: $stackTrace');
|
||||
_isRunning = false;
|
||||
if (!_runCompleter!.isCompleted) {
|
||||
_runCompleter!.completeError(e);
|
||||
}
|
||||
return Iperf3Result(
|
||||
rawOutput: 'Error starting iperf3: $e',
|
||||
exitCode: -1,
|
||||
);
|
||||
}
|
||||
|
||||
// Wait for the native layer to signal completion (with a timeout)
|
||||
try {
|
||||
final result = await _runCompleter!.future.timeout(
|
||||
config.duration + const Duration(seconds: 30),
|
||||
onTimeout: () {
|
||||
debugPrint('Iperf3Service: timeout, stopping test');
|
||||
stop();
|
||||
return Iperf3Result(
|
||||
rawOutput: 'Test timed out after ${config.duration.inSeconds}s',
|
||||
exitCode: -3,
|
||||
);
|
||||
},
|
||||
);
|
||||
return result;
|
||||
} catch (e) {
|
||||
debugPrint('Iperf3Service: error waiting for completion: $e');
|
||||
return Iperf3Result(
|
||||
rawOutput: 'Test failed: $e',
|
||||
exitCode: -1,
|
||||
);
|
||||
} finally {
|
||||
_runCompleter = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -1,10 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:iperf3gui/data/services/iperf3_binary_path_provider.dart';
|
||||
import 'package:iperf3gui/data/services/iperf3_service.dart';
|
||||
import 'package:iperf3gui/ui/features/test_run/test_run_screen.dart';
|
||||
|
||||
/// Application entry point.
|
||||
///
|
||||
/// iperf3 is now compiled as a native shared library (libiperf3jni.so)
|
||||
/// and accessed via JNI — no binary extraction or chmod needed.
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await Iperf3BinaryPathProvider.initialize();
|
||||
runApp(const Iperf3GuiApp());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:iperf3gui/data/services/iperf3_service.dart';
|
||||
import 'package:iperf3gui/data/services/iperf3_binary_path_provider.dart';
|
||||
import 'package:iperf3gui/domain/models/test_configuration.dart';
|
||||
import 'package:iperf3gui/ui/features/test_run/test_run_view_model.dart';
|
||||
|
||||
/// Main test run screen.
|
||||
///
|
||||
/// iperf3 is now compiled as a native shared library (libiperf3jni.so)
|
||||
/// and accessed via JNI. No binary extraction or chmod needed.
|
||||
class TestRunScreen extends StatelessWidget {
|
||||
const TestRunScreen({super.key});
|
||||
|
||||
@@ -12,9 +15,7 @@ class TestRunScreen extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return ChangeNotifierProvider(
|
||||
create: (_) => TestRunViewModel(
|
||||
iperf3Service: Iperf3Service(
|
||||
binaryPath: Iperf3BinaryPathProvider.path,
|
||||
),
|
||||
iperf3Service: Iperf3Service(),
|
||||
),
|
||||
child: const _TestRunContent(),
|
||||
);
|
||||
|
||||
+2
-61
@@ -1,32 +1,12 @@
|
||||
name: iperf3gui
|
||||
description: "A new Flutter project."
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
description: "A Flutter-based Android GUI wrapper for iperf3."
|
||||
publish_to: 'none'
|
||||
|
||||
# The following defines the version and build number for your application.
|
||||
# A version number is three numbers separated by dots, like 1.2.43
|
||||
# followed by an optional build number separated by a +.
|
||||
# Both the version and the builder number may be overridden in flutter
|
||||
# build by specifying --build-name and --build-number, respectively.
|
||||
# In Android, build-name is used as versionName while build-number used as versionCode.
|
||||
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
|
||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: ^3.13.1
|
||||
|
||||
# Dependencies specify other packages that your package needs in order to work.
|
||||
# To automatically upgrade your package dependencies to the latest versions
|
||||
# consider running `flutter pub upgrade --major-versions`. Alternatively,
|
||||
# dependencies can be manually updated by changing the version numbers below to
|
||||
# the latest version available on pub.dev. To see which dependencies have newer
|
||||
# versions available, run `flutter pub outdated`.
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
@@ -39,44 +19,5 @@ dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
assets:
|
||||
- assets/iperf3/arm64-v8a/iperf3
|
||||
- assets/iperf3/armeabi-v7a/iperf3
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/to/resolution-aware-images
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/to/asset-from-package
|
||||
|
||||
# To add custom fonts to your application, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/to/font-from-package
|
||||
|
||||
Reference in New Issue
Block a user