1a951c980SBrian Tierney /* 27d375156SJon Dugan * Copyright (c) 2009-2011, The Regents of the University of California, 37d375156SJon Dugan * through Lawrence Berkeley National Laboratory (subject to receipt of any 47d375156SJon Dugan * required approvals from the U.S. Dept. of Energy). All rights reserved. 57d375156SJon Dugan * 67d375156SJon Dugan * This code is distributed under a BSD style license, see the LICENSE file 77d375156SJon Dugan * for complete information. 8a951c980SBrian Tierney */ 9a951c980SBrian Tierney 10a951c980SBrian Tierney /* iperf_server_api.c: Functions to be used by an iperf server 11a951c980SBrian Tierney */ 12a951c980SBrian Tierney 13a951c980SBrian Tierney #include <stdio.h> 14a951c980SBrian Tierney #include <stdlib.h> 15a951c980SBrian Tierney #include <string.h> 16a951c980SBrian Tierney #include <getopt.h> 17a951c980SBrian Tierney #include <errno.h> 18a951c980SBrian Tierney #include <unistd.h> 19a951c980SBrian Tierney #include <assert.h> 20a951c980SBrian Tierney #include <fcntl.h> 21a951c980SBrian Tierney #include <sys/socket.h> 22a951c980SBrian Tierney #include <sys/types.h> 23a951c980SBrian Tierney #include <netinet/in.h> 24a951c980SBrian Tierney #include <arpa/inet.h> 25a951c980SBrian Tierney #include <netdb.h> 26a951c980SBrian Tierney #include <pthread.h> 27a951c980SBrian Tierney #include <stdint.h> 28a951c980SBrian Tierney #include <netinet/tcp.h> 29a951c980SBrian Tierney #include <sys/time.h> 30a951c980SBrian Tierney #include <sys/resource.h> 31a951c980SBrian Tierney #include <sched.h> 32a951c980SBrian Tierney #include <setjmp.h> 33a951c980SBrian Tierney 34a951c980SBrian Tierney #include "iperf.h" 35a951c980SBrian Tierney #include "iperf_api.h" 36a951c980SBrian Tierney #include "iperf_udp.h" 37a951c980SBrian Tierney #include "iperf_tcp.h" 388a0b5a5dSsethdelliott #include "iperf_util.h" 39a951c980SBrian Tierney #include "timer.h" 40a951c980SBrian Tierney #include "net.h" 41a951c980SBrian Tierney #include "units.h" 42a951c980SBrian Tierney #include "tcp_window_size.h" 43b0b16b86SJon Dugan #include "iperf_util.h" 44a951c980SBrian Tierney #include "locale.h" 45a951c980SBrian Tierney 46a3281a3dSBrian Tierney 47982c704aSsethdelliott int 48982c704aSsethdelliott iperf_server_listen(struct iperf_test *test) 49982c704aSsethdelliott { 50f99bd3b2Ssethdelliott if((test->listener = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) { 51b60a49ddSsethdelliott i_errno = IELISTEN; 52ec2d0670SJef Poskanzer return -1; 53982c704aSsethdelliott } 54982c704aSsethdelliott 559bfb54c0SJef Poskanzer if (!test->json_output) { 56982c704aSsethdelliott printf("-----------------------------------------------------------\n"); 57982c704aSsethdelliott printf("Server listening on %d\n", test->server_port); 589bfb54c0SJef Poskanzer } 59982c704aSsethdelliott 60982c704aSsethdelliott // This needs to be changed to reflect if client has different window size 61982c704aSsethdelliott // make sure we got what we asked for 62ba2672a2Ssethdelliott /* XXX: This needs to be moved to the stream listener 6366ce7ad4Ssethdelliott if ((x = get_tcp_windowsize(test->listener, SO_RCVBUF)) < 0) { 64982c704aSsethdelliott // Needs to set some sort of error number/message 65982c704aSsethdelliott perror("SO_RCVBUF"); 66982c704aSsethdelliott return -1; 67982c704aSsethdelliott } 68ba2672a2Ssethdelliott */ 69982c704aSsethdelliott 70ba2672a2Ssethdelliott // XXX: This code needs to be moved to after parameter exhange 71c81883bfSsethdelliott /* 7298ce496bSjef char ubuf[UNIT_LEN]; 7398ce496bSjef int x; 7498ce496bSjef 7566ce7ad4Ssethdelliott if (test->protocol->id == Ptcp) { 76112a907cSsethdelliott if (test->settings->socket_bufsize > 0) { 77982c704aSsethdelliott unit_snprintf(ubuf, UNIT_LEN, (double) x, 'A'); 789bfb54c0SJef Poskanzer if (test->json_output) 79982c704aSsethdelliott printf("TCP window size: %s\n", ubuf); 80982c704aSsethdelliott } else { 819bfb54c0SJef Poskanzer if (test->json_output) 82982c704aSsethdelliott printf("Using TCP Autotuning\n"); 83982c704aSsethdelliott } 84982c704aSsethdelliott } 85c81883bfSsethdelliott */ 869bfb54c0SJef Poskanzer if (!test->json_output) 87982c704aSsethdelliott printf("-----------------------------------------------------------\n"); 88982c704aSsethdelliott 89982c704aSsethdelliott FD_ZERO(&test->read_set); 90efdc02f7Ssethdelliott FD_ZERO(&test->write_set); 9166ce7ad4Ssethdelliott FD_SET(test->listener, &test->read_set); 9266ce7ad4Ssethdelliott test->max_fd = (test->listener > test->max_fd) ? test->listener : test->max_fd; 93982c704aSsethdelliott 94982c704aSsethdelliott return 0; 95982c704aSsethdelliott } 96982c704aSsethdelliott 97982c704aSsethdelliott int 98982c704aSsethdelliott iperf_accept(struct iperf_test *test) 99982c704aSsethdelliott { 100982c704aSsethdelliott int s; 101465b565cSsethdelliott int rbuf = ACCESS_DENIED; 102af70b448Ssethdelliott char cookie[COOKIE_SIZE]; 103982c704aSsethdelliott socklen_t len; 104982c704aSsethdelliott struct sockaddr_in addr; 105982c704aSsethdelliott 106b68ac00eSsethdelliott len = sizeof(addr); 10766ce7ad4Ssethdelliott if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) { 108b60a49ddSsethdelliott i_errno = IEACCEPT; 109ec2d0670SJef Poskanzer return -1; 110982c704aSsethdelliott } 111af70b448Ssethdelliott 112af70b448Ssethdelliott if (test->ctrl_sck == -1) { 113af70b448Ssethdelliott /* Server free, accept new client */ 114112a907cSsethdelliott if (Nread(s, test->cookie, COOKIE_SIZE, Ptcp) < 0) { 115b60a49ddSsethdelliott i_errno = IERECVCOOKIE; 116ec2d0670SJef Poskanzer return -1; 117af70b448Ssethdelliott } 118af70b448Ssethdelliott 119465b565cSsethdelliott FD_SET(s, &test->read_set); 120465b565cSsethdelliott FD_SET(s, &test->write_set); 121465b565cSsethdelliott test->max_fd = (s > test->max_fd) ? s : test->max_fd; 122465b565cSsethdelliott test->ctrl_sck = s; 123982c704aSsethdelliott 124af70b448Ssethdelliott test->state = PARAM_EXCHANGE; 125af70b448Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 126b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 127ec2d0670SJef Poskanzer return -1; 128af70b448Ssethdelliott } 129b60a49ddSsethdelliott if (iperf_exchange_parameters(test) < 0) { 130ec2d0670SJef Poskanzer return -1; 131b60a49ddSsethdelliott } 1324df383f6Ssethdelliott if (test->on_connect) { 1334df383f6Ssethdelliott test->on_connect(test); 1344df383f6Ssethdelliott } 135982c704aSsethdelliott } else { 136*111645edSJef Poskanzer /* XXX: Do we even need to receive cookie if we're just going to deny anyways? */ 137af70b448Ssethdelliott if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) { 138b60a49ddSsethdelliott i_errno = IERECVCOOKIE; 139ec2d0670SJef Poskanzer return -1; 140465b565cSsethdelliott } 141987b4323SJef Poskanzer if (Nwrite(s, (char*) &rbuf, sizeof(int), Ptcp) < 0) { 142b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 143ec2d0670SJef Poskanzer return -1; 144465b565cSsethdelliott } 145465b565cSsethdelliott close(s); 146982c704aSsethdelliott } 147982c704aSsethdelliott 148ec2d0670SJef Poskanzer return 0; 149af70b448Ssethdelliott } 150af70b448Ssethdelliott 151b68ac00eSsethdelliott 152a951c980SBrian Tierney /**************************************************************************/ 153982c704aSsethdelliott int 154465b565cSsethdelliott iperf_handle_message_server(struct iperf_test *test) 155982c704aSsethdelliott { 156b68ac00eSsethdelliott int rval; 157efdc02f7Ssethdelliott struct iperf_stream *sp; 158efdc02f7Ssethdelliott 159b60a49ddSsethdelliott // XXX: Need to rethink how this behaves to fit API 160b60a49ddSsethdelliott if ((rval = Nread(test->ctrl_sck, &test->state, sizeof(char), Ptcp)) <= 0) { 161b68ac00eSsethdelliott if (rval == 0) { 162b5e0751fSJef Poskanzer iperf_err(test, "the client has unexpectedly closed the connection"); 163b60a49ddSsethdelliott i_errno = IECTRLCLOSE; 164b68ac00eSsethdelliott test->state = IPERF_DONE; 165ec2d0670SJef Poskanzer return 0; 166b68ac00eSsethdelliott } else { 167b60a49ddSsethdelliott i_errno = IERECVMESSAGE; 168ec2d0670SJef Poskanzer return -1; 169982c704aSsethdelliott } 170b68ac00eSsethdelliott } 171982c704aSsethdelliott 172982c704aSsethdelliott switch(test->state) { 173ba2672a2Ssethdelliott case TEST_START: 174ba2672a2Ssethdelliott break; 175465b565cSsethdelliott case TEST_END: 1763331e801Ssethdelliott cpu_util(&test->cpu_util); 177f1b3bd81Ssethdelliott test->stats_callback(test); 178dd4bc008Ssethdelliott SLIST_FOREACH(sp, &test->streams, streams) { 179efdc02f7Ssethdelliott FD_CLR(sp->socket, &test->read_set); 180efdc02f7Ssethdelliott FD_CLR(sp->socket, &test->write_set); 181efdc02f7Ssethdelliott close(sp->socket); 182efdc02f7Ssethdelliott } 183181d61afSsethdelliott test->state = EXCHANGE_RESULTS; 184efb7bbe4Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 185b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 186ec2d0670SJef Poskanzer return -1; 187181d61afSsethdelliott } 188b60a49ddSsethdelliott if (iperf_exchange_results(test) < 0) 189ec2d0670SJef Poskanzer return -1; 190873952e3Ssethdelliott test->state = DISPLAY_RESULTS; 191efb7bbe4Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 192b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 193ec2d0670SJef Poskanzer return -1; 194465b565cSsethdelliott } 1954df383f6Ssethdelliott if (test->on_test_finish) 1964df383f6Ssethdelliott test->on_test_finish(test); 197181d61afSsethdelliott test->reporter_callback(test); 198465b565cSsethdelliott break; 199873952e3Ssethdelliott case IPERF_DONE: 200873952e3Ssethdelliott break; 201465b565cSsethdelliott case CLIENT_TERMINATE: 202ffdcc7d4Ssethdelliott i_errno = IECLIENTTERM; 203ffdcc7d4Ssethdelliott 204ffdcc7d4Ssethdelliott // XXX: Remove this line below! 205b5e0751fSJef Poskanzer iperf_err(test, "the client has terminated"); 206dd4bc008Ssethdelliott SLIST_FOREACH(sp, &test->streams, streams) { 2071ac522ecSsethdelliott FD_CLR(sp->socket, &test->read_set); 2081ac522ecSsethdelliott FD_CLR(sp->socket, &test->write_set); 2091ac522ecSsethdelliott close(sp->socket); 2101ac522ecSsethdelliott } 2111ac522ecSsethdelliott test->state = IPERF_DONE; 2121ac522ecSsethdelliott break; 213ba2672a2Ssethdelliott default: 214b60a49ddSsethdelliott i_errno = IEMESSAGE; 215ec2d0670SJef Poskanzer return -1; 216982c704aSsethdelliott } 217982c704aSsethdelliott 218ec2d0670SJef Poskanzer return 0; 219982c704aSsethdelliott } 220982c704aSsethdelliott 2210bd8d9daSsethdelliott /* XXX: This function is not used anymore */ 222efdc02f7Ssethdelliott void 223efdc02f7Ssethdelliott iperf_test_reset(struct iperf_test *test) 224efdc02f7Ssethdelliott { 2250bd8d9daSsethdelliott struct iperf_stream *sp; 226efdc02f7Ssethdelliott 227efdc02f7Ssethdelliott close(test->ctrl_sck); 228efdc02f7Ssethdelliott 229efdc02f7Ssethdelliott /* Free streams */ 230dd4bc008Ssethdelliott while (!SLIST_EMPTY(&test->streams)) { 231dd4bc008Ssethdelliott sp = SLIST_FIRST(&test->streams); 232dd4bc008Ssethdelliott SLIST_REMOVE_HEAD(&test->streams, streams); 233dd4bc008Ssethdelliott iperf_free_stream(sp); 234dd4bc008Ssethdelliott } 235ec2d0670SJef Poskanzer if (test->timer != NULL) { 236ec2d0670SJef Poskanzer tmr_cancel(test->timer); 237f1b3bd81Ssethdelliott test->timer = NULL; 238ec2d0670SJef Poskanzer } 239ec2d0670SJef Poskanzer if (test->stats_timer != NULL) { 240ec2d0670SJef Poskanzer tmr_cancel(test->stats_timer); 241f1b3bd81Ssethdelliott test->stats_timer = NULL; 242ec2d0670SJef Poskanzer } 243ec2d0670SJef Poskanzer if (test->reporter_timer != NULL) { 244ec2d0670SJef Poskanzer tmr_cancel(test->reporter_timer); 245f1b3bd81Ssethdelliott test->reporter_timer = NULL; 246ec2d0670SJef Poskanzer } 247efdc02f7Ssethdelliott 248dd4bc008Ssethdelliott SLIST_INIT(&test->streams); 24932885561Ssethdelliott 250efdc02f7Ssethdelliott test->role = 's'; 25166ce7ad4Ssethdelliott set_protocol(test, Ptcp); 25232885561Ssethdelliott test->duration = DURATION; 25332885561Ssethdelliott test->state = 0; 25432885561Ssethdelliott test->server_hostname = NULL; 25532885561Ssethdelliott 25632885561Ssethdelliott test->ctrl_sck = -1; 25766ce7ad4Ssethdelliott test->prot_listener = -1; 25832885561Ssethdelliott 259f1b3bd81Ssethdelliott test->bytes_sent = 0; 260f1b3bd81Ssethdelliott 26132885561Ssethdelliott test->reverse = 0; 262b68ac00eSsethdelliott test->no_delay = 0; 26332885561Ssethdelliott 264efdc02f7Ssethdelliott FD_ZERO(&test->read_set); 265efdc02f7Ssethdelliott FD_ZERO(&test->write_set); 26666ce7ad4Ssethdelliott FD_SET(test->listener, &test->read_set); 26766ce7ad4Ssethdelliott test->max_fd = test->listener; 26832885561Ssethdelliott 26932885561Ssethdelliott test->num_streams = 1; 270112a907cSsethdelliott test->settings->socket_bufsize = 0; 271112a907cSsethdelliott test->settings->blksize = DEFAULT_TCP_BLKSIZE; 272112a907cSsethdelliott test->settings->rate = RATE; /* UDP only */ 273112a907cSsethdelliott test->settings->mss = 0; 274112a907cSsethdelliott memset(test->cookie, 0, COOKIE_SIZE); 275efdc02f7Ssethdelliott } 276efdc02f7Ssethdelliott 2777fa9f68fSsethdelliott int 278a951c980SBrian Tierney iperf_run_server(struct iperf_test *test) 279a951c980SBrian Tierney { 280112a907cSsethdelliott int result, s, streams_accepted; 2816177a7f1SJef Poskanzer fd_set read_set, write_set; 2828a0b5a5dSsethdelliott struct iperf_stream *sp; 283ec2d0670SJef Poskanzer struct timeval now; 284a951c980SBrian Tierney 285e96ab740SJef Poskanzer if (test->json_output) 286e96ab740SJef Poskanzer if (iperf_json_start(test) < 0) 287e96ab740SJef Poskanzer return -1; 288e96ab740SJef Poskanzer 289e96ab740SJef Poskanzer if (test->json_output) { 290e96ab740SJef Poskanzer cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version)); 291e96ab740SJef Poskanzer cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info())); 292e96ab740SJef Poskanzer } else if (test->verbose) { 293e96ab740SJef Poskanzer printf("%s\n", version); 294e96ab740SJef Poskanzer system("uname -a"); 295e96ab740SJef Poskanzer } 296e96ab740SJef Poskanzer 297982c704aSsethdelliott // Open socket and listen 298982c704aSsethdelliott if (iperf_server_listen(test) < 0) { 299ec2d0670SJef Poskanzer return -1; 300e99faeaeSBrian Tierney } 301a951c980SBrian Tierney 3023331e801Ssethdelliott // Begin calculating CPU utilization 3033331e801Ssethdelliott cpu_util(NULL); 3043331e801Ssethdelliott 305efdc02f7Ssethdelliott test->state = IPERF_START; 306112a907cSsethdelliott streams_accepted = 0; 307a951c980SBrian Tierney 308ec2d0670SJef Poskanzer (void) gettimeofday(&now, NULL); 309873952e3Ssethdelliott while (test->state != IPERF_DONE) { 310982c704aSsethdelliott 3116177a7f1SJef Poskanzer memcpy(&read_set, &test->read_set, sizeof(fd_set)); 3126177a7f1SJef Poskanzer memcpy(&write_set, &test->write_set, sizeof(fd_set)); 313982c704aSsethdelliott 3146177a7f1SJef Poskanzer result = select(test->max_fd + 1, &read_set, &write_set, NULL, tmr_timeout(&now)); 315982c704aSsethdelliott if (result < 0 && errno != EINTR) { 316b60a49ddSsethdelliott i_errno = IESELECT; 317ec2d0670SJef Poskanzer return -1; 318ec2d0670SJef Poskanzer } 319ec2d0670SJef Poskanzer if (result > 0) { 3206177a7f1SJef Poskanzer if (FD_ISSET(test->listener, &read_set)) { 321b68ac00eSsethdelliott if (test->state != CREATE_STREAMS) { 322465b565cSsethdelliott if (iperf_accept(test) < 0) { 323ec2d0670SJef Poskanzer return -1; 324982c704aSsethdelliott } 3256177a7f1SJef Poskanzer FD_CLR(test->listener, &read_set); 326982c704aSsethdelliott } 327b68ac00eSsethdelliott } 3286177a7f1SJef Poskanzer if (FD_ISSET(test->ctrl_sck, &read_set)) { 329b60a49ddSsethdelliott if (iperf_handle_message_server(test) < 0) 330ec2d0670SJef Poskanzer return -1; 3316177a7f1SJef Poskanzer FD_CLR(test->ctrl_sck, &read_set); 332982c704aSsethdelliott } 333465b565cSsethdelliott 334465b565cSsethdelliott if (test->state == CREATE_STREAMS) { 3356177a7f1SJef Poskanzer if (FD_ISSET(test->prot_listener, &read_set)) { 3368a0b5a5dSsethdelliott 33766ce7ad4Ssethdelliott if ((s = test->protocol->accept(test)) < 0) 338ec2d0670SJef Poskanzer return -1; 3398a0b5a5dSsethdelliott 3408a0b5a5dSsethdelliott if (!is_closed(s)) { 34166ce7ad4Ssethdelliott sp = iperf_new_stream(test, s); 3428a0b5a5dSsethdelliott if (!sp) 343ec2d0670SJef Poskanzer return -1; 34466ce7ad4Ssethdelliott 3458a0b5a5dSsethdelliott FD_SET(s, &test->read_set); 3468a0b5a5dSsethdelliott FD_SET(s, &test->write_set); 3478a0b5a5dSsethdelliott test->max_fd = (s > test->max_fd) ? s : test->max_fd; 34866ce7ad4Ssethdelliott 349112a907cSsethdelliott streams_accepted++; 3504df383f6Ssethdelliott if (test->on_new_stream) 3514df383f6Ssethdelliott test->on_new_stream(sp); 352b68ac00eSsethdelliott } 3536177a7f1SJef Poskanzer FD_CLR(test->prot_listener, &read_set); 354b68ac00eSsethdelliott } 3558a0b5a5dSsethdelliott 356112a907cSsethdelliott if (streams_accepted == test->num_streams) { 35766ce7ad4Ssethdelliott if (test->protocol->id != Ptcp) { 3588a0b5a5dSsethdelliott FD_CLR(test->prot_listener, &test->read_set); 3598a0b5a5dSsethdelliott close(test->prot_listener); 36066ce7ad4Ssethdelliott } else { 3610bd8d9daSsethdelliott if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) { 36266ce7ad4Ssethdelliott FD_CLR(test->listener, &test->read_set); 36366ce7ad4Ssethdelliott close(test->listener); 364f99bd3b2Ssethdelliott if ((s = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) { 365b60a49ddSsethdelliott i_errno = IELISTEN; 366ec2d0670SJef Poskanzer return -1; 367c81883bfSsethdelliott } 36866ce7ad4Ssethdelliott test->listener = s; 369c81883bfSsethdelliott test->max_fd = (s > test->max_fd ? s : test->max_fd); 37066ce7ad4Ssethdelliott FD_SET(test->listener, &test->read_set); 371c81883bfSsethdelliott } 3728556db5dSsethdelliott } 3738a0b5a5dSsethdelliott test->prot_listener = -1; 374465b565cSsethdelliott test->state = TEST_START; 375b68ac00eSsethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 376b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 377ec2d0670SJef Poskanzer return -1; 378982c704aSsethdelliott } 379b60a49ddSsethdelliott if (iperf_init_test(test) < 0) 380ec2d0670SJef Poskanzer return -1; 381f1b3bd81Ssethdelliott test->state = TEST_RUNNING; 382f1b3bd81Ssethdelliott if (Nwrite(test->ctrl_sck, &test->state, sizeof(char), Ptcp) < 0) { 383b60a49ddSsethdelliott i_errno = IESENDMESSAGE; 384ec2d0670SJef Poskanzer return -1; 385f1b3bd81Ssethdelliott } 386982c704aSsethdelliott } 387982c704aSsethdelliott } 388982c704aSsethdelliott 389f1b3bd81Ssethdelliott if (test->state == TEST_RUNNING) { 390465b565cSsethdelliott if (test->reverse) { 391465b565cSsethdelliott // Reverse mode. Server sends. 3926177a7f1SJef Poskanzer if (iperf_send(test, &write_set) < 0) 393ec2d0670SJef Poskanzer return -1; 394982c704aSsethdelliott } else { 395465b565cSsethdelliott // Regular mode. Server receives. 3966177a7f1SJef Poskanzer if (iperf_recv(test, &read_set) < 0) 397ec2d0670SJef Poskanzer return -1; 398e99faeaeSBrian Tierney } 399f1b3bd81Ssethdelliott 400ec2d0670SJef Poskanzer /* Run the timers. */ 401ec2d0670SJef Poskanzer (void) gettimeofday(&now, NULL); 402ec2d0670SJef Poskanzer tmr_run(&now); 403f1b3bd81Ssethdelliott } 404a951c980SBrian Tierney } 405982c704aSsethdelliott } 406e99faeaeSBrian Tierney 40702a35f07Ssethdelliott /* Close open test sockets */ 4080bd8d9daSsethdelliott close(test->ctrl_sck); 4090bd8d9daSsethdelliott close(test->listener); 410efdc02f7Ssethdelliott 411e96ab740SJef Poskanzer if (test->json_output) { 412e96ab740SJef Poskanzer if (iperf_json_finish(test) < 0) 413e96ab740SJef Poskanzer return -1; 414e96ab740SJef Poskanzer } 415e96ab740SJef Poskanzer 416ec2d0670SJef Poskanzer return 0; 417a3281a3dSBrian Tierney } 418