Compare commits

..

4 Commits

Author SHA1 Message Date
irene 7263808918 docs: add current structure/implementation/design documentation
CI / Analyze & Test (push) Failing after 27s
2026-08-25 19:25:40 +09:00
irene 0dd202291c fix: resolve SIGBUS crash in JNI bridge — use CallVoidMethod instead of function pointer casts
The root cause of the SIGBUS (alignment fault) crash was casting jmethodID
values to function pointers and invoking them directly. jmethodID is an
opaque JNI type that must only be used via CallVoidMethod/CallObjectMethod
etc. Calling it as a function pointer dereferences garbage memory, causing
SIGBUS at address 0xb1.

Changes:
- Replace function pointer typedefs (output_callback_t etc.) with a struct
  storing JNIEnv + callback global ref + jmethodIDs
- All callbacks now go through CallVoidMethod(env, ref, mid, ...)
- Remove getTestProgress() (never used, unused start_time workaround)
- Fix Kotlin arg cast: List<String> -> toTypedArray() (ClassCastException)
- Update UI default server to speedtest.uztelecom.uz
2026-08-25 18:59:42 +09:00
irene d551f41b41 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)
2026-08-25 13:46:33 +09:00
irene 27e8e50469 feat: add INTERNET permission to AndroidManifest
Required for iperf3 client mode to resolve hostnames and connect to servers. Without this, the app silently fails to make network connections.
2026-08-24 13:11:37 +09:00
65 changed files with 24332 additions and 168 deletions
+11 -1
View File
@@ -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 {
+1
View File
@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:label="iperf3_gui"
android:name="${applicationName}"
+74
View File
@@ -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
)
+104
View File
@@ -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
+302
View File
@@ -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
+162
View File
@@ -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
+485
View File
@@ -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 */
+529
View File
@@ -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
+518
View File
@@ -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 */
+455
View File
@@ -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)
+778
View File
@@ -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;
}
+613
View File
@@ -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
+155
View File
@@ -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
+634
View File
@@ -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
+660
View File
@@ -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
+115
View File
@@ -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/
+214
View File
@@ -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;
}
+664
View File
@@ -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;
}
+48
View File
@@ -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-----
+527
View File
@@ -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_ */
+90
View File
@@ -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;
}
+124
View File
@@ -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 */
+81
View File
@@ -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);
}
+108
View File
@@ -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;
}
+46
View File
@@ -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);
}
+234
View File
@@ -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
}
+245
View File
@@ -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();
}
+100
View File
@@ -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 */
+353
View File
@@ -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
+34
View File
@@ -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 );
+27
View File
@@ -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@"
+184
View File
@@ -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 */
+351
View File
@@ -0,0 +1,351 @@
/*
* 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 "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;
/* Per-call state to pass between JNI and the reader thread.
* We store the callback object (global ref) and method IDs.
* Callbacks are invoked via CallVoidMethod, NOT as function pointers. */
struct iperf_run_state {
JNIEnv *env;
jobject callback_ref; /* Global ref to the callback object */
jmethodID mid_output; /* Method ID for onOutput(String) */
jmethodID mid_error; /* Method ID for onError(String) */
jmethodID mid_complete; /* Method ID for onComplete() */
FILE *outfile;
int pipe_fd[2];
JavaVM *jvm;
pthread_t reader_thread;
};
/* Helper: invoke the output callback via CallVoidMethod */
static void signal_output(struct iperf_run_state *state, const char *msg) {
if (!state || !msg || !msg[0]) return;
JNIEnv *env = state->env;
if (!env || !state->mid_output) return;
jstring jline = (*env)->NewStringUTF(env, msg);
if (jline) {
(*env)->CallVoidMethod(env, state->callback_ref, state->mid_output, jline);
(*env)->DeleteLocalRef(env, jline);
}
}
/* Helper: invoke the error callback via CallVoidMethod */
static void signal_error(struct iperf_run_state *state, const char *msg) {
if (!state || !msg) return;
JNIEnv *env = state->env;
if (!env || !state->mid_error) return;
jstring jmsg = (*env)->NewStringUTF(env, msg);
if (jmsg) {
(*env)->CallVoidMethod(env, state->callback_ref, state->mid_error, jmsg);
(*env)->DeleteLocalRef(env, jmsg);
}
}
/* Helper: invoke the complete callback via CallVoidMethod */
static void signal_complete(struct iperf_run_state *state) {
if (!state || !state->mid_complete) return;
JNIEnv *env = state->env;
if (!env) return;
(*env)->CallVoidMethod(env, state->callback_ref, state->mid_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;
}
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_ref,
state->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);
}
/* ------------------------------------------------------------------ */
/* 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) {
/* Create the iperf test object */
struct iperf_test *test = iperf_new_test();
if (!test) {
const char *err = "Failed to create iperf test";
LOGE("%s", err);
return;
}
iperf_defaults(test);
g_test = test;
/* 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);
iperf_free_test(test);
g_test = NULL;
return;
}
/* 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((struct iperf_run_state *) 0, errbuf);
iperf_free_test(test);
g_test = NULL;
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));
signal_error((struct iperf_run_state *) 0, err);
iperf_free_test(test);
g_test = NULL;
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]);
iperf_free_test(test);
g_test = NULL;
return;
}
/* Resolve callback method IDs (do this in the calling thread) */
jclass callback_cls = (*env)->GetObjectClass(env, callback);
state->mid_output = (*env)->GetMethodID(
env, callback_cls, "onOutput", "(Ljava/lang/String;)V");
state->mid_error = (*env)->GetMethodID(
env, callback_cls, "onError", "(Ljava/lang/String;)V");
state->mid_complete = (*env)->GetMethodID(
env, callback_cls, "onComplete", "()V");
/* Get JavaVM for thread attachment */
JavaVM *jvm;
(*env)->GetJavaVM(env, &jvm);
state->env = env;
state->callback_ref = (*env)->NewGlobalRef(env, callback);
state->outfile = fdopen(pipefd[1], "w");
state->pipe_fd[0] = pipefd[0];
state->jvm = jvm;
/* Redirect iperf's output to our pipe */
test->outfile = state->outfile;
setvbuf(test->outfile, NULL, _IOLBF, 0); /* Line-buffered */
/* Notify Java that the test is starting */
signal_output(state, "[iPerf JNI] Starting iPerf3 client...\n");
/* Spawn reader thread to forward iperf output to Java */
if (pthread_create(&state->reader_thread, NULL,
reader_thread_func, state) != 0) {
free_argv(argv, argc);
close(pipefd[0]);
close(pipefd[1]);
const char *err = "Failed to start output reader thread";
LOGE("%s", err);
signal_error(state, err);
iperf_free_test(test);
g_test = NULL;
free(state);
return;
}
/* Run the test */
g_stop = 0;
int ret = iperf_run_client(test);
/* Wait for reader thread to finish */
pthread_join(state->reader_thread, NULL);
/* 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 result to Java */
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), "[iPerf JNI] Test failed: %s\n",
iperf_strerror(i_errno));
signal_error(state, errbuf);
} else {
signal_output(state, "[iPerf JNI] Test completed successfully.\n");
}
/* Signal completion to Java */
signal_complete(state);
/* Free global ref and state struct */
(*env)->DeleteGlobalRef(env, state->callback_ref);
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 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<List<String>>("args")?.toTypedArray()
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)
}
}
+102
View File
@@ -0,0 +1,102 @@
# iperf3-gui
A Flutter-based Android GUI wrapper for iperf3, the popular network performance measurement tool.
## Architecture
```
lib/ # Flutter/Dart application layer
├── main.dart # App entry point (MaterialApp)
├── domain/ # Business logic (platform-independent)
│ ├── models/
│ │ ├── test_configuration.dart # Server, port, duration, interval, parallel streams
│ │ └── iperf3_result.dart # Raw output + exit code
│ └── services/
│ ├── iperf3_service.dart # Dart → MethodChannel bridge (async with Completer)
│ └── iperf3_binary_path_provider.dart # Deprecated (kept for reference)
└── ui/ # Presentation layer
└── features/
└── test_run/
├── test_run_screen.dart # Server/port inputs + GO button
└── test_run_view_model.dart # State machine: idle → running → completed/error
android/app/src/main/
├── kotlin/ # Kotlin plugin layer
│ └── com/novoyuuparosk/iperf3gui/
│ ├── MainActivity.kt # Registers IperfRunner as FlutterPlugin
│ └── IperfRunner.kt # MethodChannel handler → native JNI calls
└── cpp/ # Native iperf3 library
├── CMakeLists.txt # Compiles iperf3 + cJSON + JNI into libiperf3jni.so
├── iperf_jni.c # JNI bridge: Java String[] → iperf3 API
├── iperf/ # iperf3 3.19 C source tree (see below)
├── iperf_config.h # Generated Android-compatible config header
└── iperf_config_android.h # Android-specific compile options
```
## Data Flow
```
User taps GO
→ Dart TestRunViewModel.runTest(config)
→ Iperf3Service.run(config) via MethodChannel
→ Kotlin IperfRunner.runIperfLive(args, callback)
→ JNI C: java_string_array_to_argv() → iperf_parse_arguments()
→ iperf_run_client(test)
→ Reader thread pipes stdout → CallVoidMethod → Dart onOutput callbacks
→ On completion: Dart Completer resolves with Iperf3Result
```
## Key Design Decisions
### JNI Over Process Execution
Android SELinux Enforcing mode blocks `execve()` from the `untrusted_app` domain, making Process.start() of external binaries fail with Permission denied. Compiling iperf3 as a shared library linked into the app process eliminates this restriction entirely — iperf3 code runs in-process with no domain transitions needed.
### Callback Mechanism
The JNI bridge uses `CallVoidMethod` on stored `jmethodID`s to invoke the Kotlin `IperfCallback` interface, NOT function pointer casts. Direct function pointer casts caused SIGBUS crashes on ARM due to JNINativeInterface method ID layout differences.
### Output Streaming
iperf3 output is redirected via pipe to a background reader thread that attaches to the Java VM via `AttachCurrentThread` and invokes callbacks via `CallVoidMethod`. The reader thread frees the state struct on exit; the JNI caller owns the callback object's lifetime.
### Async Pattern
Dart uses `Completer<Iperf3Result>` to await native completion. The MethodChannel callback handler resolves the completer on `onComplete`, with a timeout fallback that calls `stopIperf` on the native layer.
## iperf3 Source
- Version: 3.19
- Location: `android/app/src/main/cpp/iperf/`
- Files: Full C source tree including cJSON, headers, man pages, PEM keys
- Build: CMake compiles into `libiperf3jni.so` for `armeabi-v7a` and `arm64-v8a`
- Config: `iperf_config.h` generated from `iperf_config.h.in` with Android-compatible POSIX/threading macros
## Native JNI API
| Kotlin Method | JNI Signature | Purpose |
|---|---|---|
| `runIperfLive(String[], IperfCallback)` | `runIperfLive` | Start test with args and callback |
| `forceStopIperfTest(IperfCallback)` | `forceStopIperfTest` | Graceful stop via shutdown() |
| `isTestRunning()` | `isTestRunning` | Check if test is in progress |
## Building
Prerequisites:
- Flutter 3.47+
- Android SDK with platform android-36
- JDK 17
- NDK r28+
- CMake 3.22.1
```bash
flutter pub get
flutter build apk --debug
adb install build/app/outputs/flutter-apk/app-debug.apk
```
## Dependencies
- Dart: `provider`, `path_provider`
- Android: CMake external native build
- Native: iperf3 3.19, cJSON (single file)
## License
MIT — bundled iperf3 3.19 is BSD-3-Clause licensed.
@@ -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.
+120 -41
View File
@@ -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();
if (_isRunning) {
return Iperf3Result(
rawOutput: 'Another test is already running.',
exitCode: -2,
);
}
debugPrint(
'Iperf3Service: running $_binaryPath ${args.join(" ")}',
);
_isRunning = true;
_outputBuffer.clear();
final process = await Process.start(
_binaryPath,
args,
runInShell: false, // must execve directly — Android sh can't exec ELF binaries
);
final args = <String>[
'-c', config.server,
'-p', config.port.toString(),
'-t', config.duration.inSeconds.toString(),
'-i', config.interval.toString(),
'-P', config.parallelStreams.toString(),
];
final stdoutBuffer = <int>[];
final stderrBuffer = <int>[];
debugPrint('Iperf3Service: running: ${args.join(" ")}');
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');
},
);
// Set up the completer to await completion
_runCompleter = Completer<Iperf3Result>();
final exitCode = await process.exitCode;
// 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;
}
}
});
await stdoutStream.cancel();
await stderrStream.cancel();
// 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,
);
}
final rawOutput = utf8.decode(stdoutBuffer) + utf8.decode(stderrBuffer);
debugPrint(
'Iperf3Service: finished with exit code $exitCode, output length ${rawOutput.length}',
);
return Iperf3Result(
rawOutput: rawOutput,
exitCode: exitCode,
);
// 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
View File
@@ -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(),
);
@@ -29,7 +30,7 @@ class _TestRunContent extends StatefulWidget {
}
class _TestRunContentState extends State<_TestRunContent> {
final _serverController = TextEditingController(text: 'iperf.es.net');
final _serverController = TextEditingController(text: 'speedtest.uztelecom.uz');
final _portController = TextEditingController(text: '5201');
@override
+2 -61
View File
@@ -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