diff options
Diffstat (limited to '')
-rw-r--r-- | src/iperf_api.c | 153 |
1 files changed, 125 insertions, 28 deletions
diff --git a/src/iperf_api.c b/src/iperf_api.c index eb23403..7fb741e 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2022, The Regents of the University of + * iperf, Copyright (c) 2014-2024, 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. @@ -46,9 +46,7 @@ #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> -#ifdef HAVE_STDINT_H #include <stdint.h> -#endif #include <sys/time.h> #include <sys/resource.h> #include <sys/mman.h> @@ -102,6 +100,7 @@ static int diskfile_recv(struct iperf_stream *sp); static int JSON_write(int fd, cJSON *json); static void print_interval_results(struct iperf_test *test, struct iperf_stream *sp, cJSON *json_interval_streams); static cJSON *JSON_read(int fd); +static int JSONStream_Output(struct iperf_test *test, const char* event_name, cJSON* obj); /*************************** Print usage functions ****************************/ @@ -327,6 +326,12 @@ iperf_get_test_json_output_string(struct iperf_test *ipt) } int +iperf_get_test_json_stream(struct iperf_test *ipt) +{ + return ipt->json_stream; +} + +int iperf_get_test_zerocopy(struct iperf_test *ipt) { return ipt->zerocopy; @@ -682,6 +687,12 @@ iperf_set_test_json_output(struct iperf_test *ipt, int json_output) ipt->json_output = json_output; } +void +iperf_set_test_json_stream(struct iperf_test *ipt, int json_stream) +{ + ipt->json_stream = json_stream; +} + int iperf_has_zerocopy( void ) { @@ -881,7 +892,7 @@ void iperf_on_test_start(struct iperf_test *test) { if (test->json_output) { - cJSON_AddItemToObject(test->json_start, "test_start", iperf_json_printf("protocol: %s num_streams: %d blksize: %d omit: %d duration: %d bytes: %d blocks: %d reverse: %d tos: %d target_bitrate: %d bidir: %d fqrate: %d", test->protocol->name, (int64_t) test->num_streams, (int64_t) test->settings->blksize, (int64_t) test->omit, (int64_t) test->duration, (int64_t) test->settings->bytes, (int64_t) test->settings->blocks, test->reverse?(int64_t)1:(int64_t)0, (int64_t) test->settings->tos, (int64_t) test->settings->rate, (int64_t) test->bidirectional, (uint64_t) test->settings->fqrate)); + cJSON_AddItemToObject(test->json_start, "test_start", iperf_json_printf("protocol: %s num_streams: %d blksize: %d omit: %d duration: %d bytes: %d blocks: %d reverse: %d tos: %d target_bitrate: %d bidir: %d fqrate: %d interval: %f", test->protocol->name, (int64_t) test->num_streams, (int64_t) test->settings->blksize, (int64_t) test->omit, (int64_t) test->duration, (int64_t) test->settings->bytes, (int64_t) test->settings->blocks, test->reverse?(int64_t)1:(int64_t)0, (int64_t) test->settings->tos, (int64_t) test->settings->rate, (int64_t) test->bidirectional, (uint64_t) test->settings->fqrate, test->stats_interval)); } else { if (test->verbose) { if (test->settings->bytes) @@ -892,8 +903,12 @@ iperf_on_test_start(struct iperf_test *test) iperf_printf(test, test_start_time, test->protocol->name, test->num_streams, test->settings->blksize, test->omit, test->duration, test->settings->tos); } } + if (test->json_stream) { + JSONStream_Output(test, "start", test->json_start); + } } + /* This converts an IPv6 string address from IPv4-mapped format into regular ** old IPv4 format, which is easier on the eyes of network veterans. ** @@ -1058,6 +1073,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {"one-off", no_argument, NULL, '1'}, {"verbose", no_argument, NULL, 'V'}, {"json", no_argument, NULL, 'J'}, + {"json-stream", no_argument, NULL, OPT_JSON_STREAM}, {"version", no_argument, NULL, 'v'}, {"server", no_argument, NULL, 's'}, {"client", required_argument, NULL, 'c'}, @@ -1121,6 +1137,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {"rsa-private-key-path", required_argument, NULL, OPT_SERVER_RSA_PRIVATE_KEY}, {"authorized-users-path", required_argument, NULL, OPT_SERVER_AUTHORIZED_USERS}, {"time-skew-threshold", required_argument, NULL, OPT_SERVER_SKEW_THRESHOLD}, + {"use-pkcs1-padding", no_argument, NULL, OPT_USE_PKCS1_PADDING}, #endif /* HAVE_SSL */ {"fq-rate", required_argument, NULL, OPT_FQ_RATE}, {"pacing-timer", required_argument, NULL, OPT_PACING_TIMER}, @@ -1207,6 +1224,10 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) case 'J': test->json_output = 1; break; + case OPT_JSON_STREAM: + test->json_output = 1; + test->json_stream = 1; + break; case 'v': printf("%s (cJSON %s)\n%s\n%s\n", version, cJSON_Version(), get_system_info(), get_optional_features()); @@ -1293,7 +1314,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) break; case 't': test->duration = atoi(optarg); - if (test->duration > MAX_TIME) { + if (test->duration > MAX_TIME || test->duration < 0) { i_errno = IEDURATION; return -1; } @@ -1610,6 +1631,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) return -1; } break; + case OPT_USE_PKCS1_PADDING: + test->use_pkcs1_padding = 1; + break; #endif /* HAVE_SSL */ case OPT_PACING_TIMER: test->settings->pacing_timer = unit_atoi(optarg); @@ -2050,7 +2074,7 @@ int test_is_authorized(struct iperf_test *test){ if (test->settings->authtoken){ char *username = NULL, *password = NULL; time_t ts; - int rc = decode_auth_setting(test->debug, test->settings->authtoken, test->server_rsa_private_key, &username, &password, &ts); + int rc = decode_auth_setting(test->debug, test->settings->authtoken, test->server_rsa_private_key, &username, &password, &ts, test->use_pkcs1_padding); if (rc) { return -1; } @@ -2235,7 +2259,7 @@ send_parameters(struct iperf_test *test) #if defined(HAVE_SSL) /* Send authentication parameters */ if (test->settings->client_username && test->settings->client_password && test->settings->client_rsa_pubkey){ - int rc = encode_auth_setting(test->settings->client_username, test->settings->client_password, test->settings->client_rsa_pubkey, &test->settings->authtoken); + int rc = encode_auth_setting(test->settings->client_username, test->settings->client_password, test->settings->client_rsa_pubkey, &test->settings->authtoken, test->use_pkcs1_padding); if (rc) { cJSON_Delete(j); @@ -2738,6 +2762,35 @@ JSON_read(int fd) /*************************************************************/ /** + * JSONStream_Output - outputs an obj as event without distrubing it + */ + +static int +JSONStream_Output(struct iperf_test * test, const char * event_name, cJSON * obj) +{ + cJSON *event = cJSON_CreateObject(); + if (!event) + return -1; + cJSON_AddStringToObject(event, "event", event_name); + cJSON_AddItemReferenceToObject(event, "data", obj); + char *str = cJSON_PrintUnformatted(event); + if (str == NULL) + return -1; + if (pthread_mutex_lock(&(test->print_mutex)) != 0) { + perror("iperf_json_finish: pthread_mutex_lock"); + } + fprintf(test->outfile, "%s\n", str); + if (pthread_mutex_unlock(&(test->print_mutex)) != 0) { + perror("iperf_json_finish: pthread_mutex_unlock"); + } + iflush(test); + cJSON_free(str); + cJSON_Delete(event); + return 0; +} + +/*************************************************************/ +/** * add_to_interval_list -- adds new interval to the interval_list */ @@ -3209,6 +3262,7 @@ iperf_reset_test(struct iperf_test *test) test->settings->socket_bufsize = 0; test->settings->blksize = DEFAULT_TCP_BLKSIZE; test->settings->rate = 0; + test->settings->fqrate = 0; test->settings->burst = 0; test->settings->mss = 0; test->settings->tos = 0; @@ -3400,6 +3454,7 @@ iperf_print_intermediate(struct iperf_test *test) int lower_mode, upper_mode; int current_mode; + int discard_json; /* * Due to timing oddities, there can be cases, especially on the @@ -3445,11 +3500,20 @@ iperf_print_intermediate(struct iperf_test *test) return; } + /* + * When we use streamed json, we don't actually need to keep the interval + * results around unless we're the server and the client requested the server output. + * + * This avoids unneeded memory build up for long sessions. + */ + discard_json = test->json_stream == 1 && !(test->role == 's' && test->get_server_output); + if (test->json_output) { json_interval = cJSON_CreateObject(); if (json_interval == NULL) return; - cJSON_AddItemToArray(test->json_intervals, json_interval); + if (!discard_json) + cJSON_AddItemToArray(test->json_intervals, json_interval); json_interval_streams = cJSON_CreateArray(); if (json_interval_streams == NULL) return; @@ -3600,6 +3664,11 @@ iperf_print_intermediate(struct iperf_test *test) } } } + + if (test->json_stream) + JSONStream_Output(test, "interval", json_interval); + if (discard_json) + cJSON_Delete(json_interval); } /** @@ -4710,7 +4779,15 @@ iperf_create_pidfile(struct iperf_test *test) if (pid > 0) { /* See if the process exists. */ +#if (defined(__vxworks)) || (defined(__VXWORKS__)) +#if (defined(_WRS_KERNEL)) && (defined(_WRS_CONFIG_LP64)) + if (kill((_Vx_TASK_ID)pid, 0) == 0) { +#else + if (kill(pid, 0) == 0) { +#endif // _WRS_KERNEL and _WRS_CONFIG_LP64 +#else if (kill(pid, 0) == 0) { +#endif // __vxworks or __VXWORKS__ /* * Make sure not to try to delete existing PID file by * scribbling over the pathname we'd use to refer to it. @@ -4798,31 +4875,51 @@ iperf_json_finish(struct iperf_test *test) if (test->server_output_text) { cJSON_AddStringToObject(test->json_top, "server_output_text", test->server_output_text); } - // Get ASCII rendering of JSON structure. Then make our - // own copy of it and return the storage that cJSON allocated - // on our behalf. We keep our own copy around. - char *str = cJSON_Print(test->json_top); - if (str == NULL) { - return -1; - } - test->json_output_string = strdup(str); - cJSON_free(str); - if (test->json_output_string == NULL) { - return -1; - } - if (pthread_mutex_lock(&(test->print_mutex)) != 0) { - perror("iperf_json_finish: pthread_mutex_lock"); + /* --json-stream, so we print various individual objects */ + if (test->json_stream) { + cJSON *error = cJSON_GetObjectItem(test->json_top, "error"); + if (error) { + JSONStream_Output(test, "error", error); + } + if (test->json_server_output) { + JSONStream_Output(test, "server_output_json", test->json_server_output); + } + if (test->server_output_text) { + JSONStream_Output(test, "server_output_text", cJSON_CreateString(test->server_output_text)); + } + JSONStream_Output(test, "end", test->json_end); } - fprintf(test->outfile, "%s\n", test->json_output_string); - if (pthread_mutex_unlock(&(test->print_mutex)) != 0) { - perror("iperf_json_finish: pthread_mutex_unlock"); + /* Original --json output, single monolithic object */ + else { + /* + * Get ASCII rendering of JSON structure. Then make our + * own copy of it and return the storage that cJSON + * allocated on our behalf. We keep our own copy + * around. + */ + char *str = cJSON_Print(test->json_top); + if (str == NULL) { + return -1; + } + test->json_output_string = strdup(str); + cJSON_free(str); + if (test->json_output_string == NULL) { + return -1; + } + if (pthread_mutex_lock(&(test->print_mutex)) != 0) { + perror("iperf_json_finish: pthread_mutex_lock"); + } + fprintf(test->outfile, "%s\n", test->json_output_string); + if (pthread_mutex_unlock(&(test->print_mutex)) != 0) { + perror("iperf_json_finish: pthread_mutex_unlock"); + } + iflush(test); } - iflush(test); cJSON_Delete(test->json_top); - test->json_top = NULL; } - test->json_start = test->json_connected = test->json_intervals = test->json_server_output = test->json_end = NULL; + + test->json_top = test->json_start = test->json_connected = test->json_intervals = test->json_server_output = test->json_end = NULL; return 0; } |