/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipcom_cmd_sockperf.c,v $ $Revision: 1.12.60.3 $ * $Source: /home/interpeak/CVSRoot/ipcom/src/ipcom_cmd_sockperf.c,v $ * $Author: jren $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Kenneth Jonsson ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * * Interpeak network performance tool, supports: * half- and full-duplex communication * any number of active sockets * IPv4 and IPv6 * TCP */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipcom_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #ifdef IP_PORT_INTEGRITY #include "ipcom_ipcom2bsd.h" #endif #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #ifdef IP_PORT_INTEGRITY #include "ipcom_ipcom2bsd.h" #endif /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ typedef struct Ipcom_cmd_sockperf_struct { Ip_bool accept; Ip_bool transmit; Ip_bool receive; Ip_bool testpattern; Ip_bool echo; Ip_u32 buf_len; Ip_u32 num_buf; Ip_u32 num_sock; struct Ip_addrinfo *res; int width; Ip_fd_set read_set; Ip_fd *sock_array; char *buf; struct Ip_linger linger; int sendbuf_size; int recvbuf_size; Ip_u32 *send_pattern; Ip_u32 *recv_pattern; } Ipcom_cmd_sockperf_t; /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ IP_PUBLIC int ipcom_cmd_sockperf(int argc, char **argv); /* **************************************************************************** * 8 DATA **************************************************************************** */ /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipcom_cmd_sockperf_tv_to_msec *=========================================================================== * Description: Returns the number of milliseconds between 2 absolute times. * Parameters: * Returns: */ IP_STATIC Ip_s32 ipcom_cmd_sockperf_tv_to_msec(struct Ip_timeval *start, struct Ip_timeval *stop) { Ip_s32 sec; Ip_s32 usec; usec = (Ip_s32) (stop->tv_usec - start->tv_usec); sec = (Ip_s32) (stop->tv_sec - start->tv_sec); if (usec < 0) { usec += 1000000; sec -= 1; } return IP_MAX(sec * 1000 + usec / 1000, 1); } /* *=========================================================================== * ipcom_cmd_sockperf_echo_buf *=========================================================================== * Description: Prints the contents of the buffer. * Parameters: buf - The buffer to print. * size - The size of 'buf' * pattern_id - The pattern number the buffer should start at. * Returns: */ IP_STATIC void ipcom_cmd_sockperf_echo_buf(char *buf, int size, int pattern_id) { char pbuf[9]; int i; if (size == 0) { ipcom_printf(IP_LF); return; } pbuf[8] = '\0'; for (i = 0; i < size; i += 8) { ipcom_memcpy(pbuf, buf + i, 8); if ((pattern_id % 16) == 0) ipcom_printf("%04d ", i); if ((++pattern_id % 16) == 0) pbuf[7] = '\n'; ipcom_printf(pbuf); } } /* *=========================================================================== * ipcom_cmd_sockperf_run *=========================================================================== * Description: Sends/receives data. * Parameters: * Returns: */ IP_STATIC void ipcom_cmd_sockperf_run(Ipcom_cmd_sockperf_t *cmd) { struct Ip_timeval tmo = { 0, 0 }; struct Ip_timeval *ptmo; struct Ip_timeval start; struct Ip_timeval stop; Ip_u32 i; Ip_u32 c; int num_ready; Ip_fd *s = cmd->sock_array; Ip_u32 total_bytes_to_send = cmd->num_buf * cmd->buf_len; Ip_u32 finished_sockets = 0; Ip_u32 total_bytes_read = 0; Ip_ssize_t bytes; Ip_fd_set read_set; int send_flags = (cmd->transmit && cmd->receive) ? IP_MSG_DONTWAIT : 0; if (cmd->transmit) ipcom_printf("sockperf-t: send buffer is %u"IP_LF, cmd->sendbuf_size); if (cmd->receive) ipcom_printf("sockperf-r: receive buffer is %u"IP_LF, cmd->sendbuf_size); for (i = 0; i < cmd->num_sock; i++) { int on = 1; if (ipcom_setsockopt(s[i], IP_SOL_SOCKET, IP_SO_REUSEADDR, &on, sizeof(on)) < 0) { ipcom_printf("sockperf-c: setsockopt IP_SO_REUSEADDR failed : %s"IP_LF, ipcom_strerror(ipcom_errno)); return; } if (ipcom_setsockopt(s[i], IP_SOL_SOCKET, IP_SO_SNDBUF, &cmd->sendbuf_size, sizeof(cmd->sendbuf_size)) < 0) { ipcom_printf("sockperf-c: setsockopt IP_SO_SNDBUF failed : %s"IP_LF, ipcom_strerror(ipcom_errno)); return; } if (ipcom_setsockopt(s[i], IP_SOL_SOCKET, IP_SO_RCVBUF, &cmd->recvbuf_size, sizeof(cmd->recvbuf_size)) < 0) { ipcom_printf("sockperf-c: setsockopt IP_SO_SNDBUF failed : %s"IP_LF, ipcom_strerror(ipcom_errno)); return; } } if (cmd->receive) { IP_FD_ZERO(&cmd->read_set); cmd->width = s[0]; IP_FD_SET(s[0], &cmd->read_set); for (i = 1; i < cmd->num_sock; i++) if (s[i] != IP_INVALID_SOCKET) { cmd->width = IP_MAX(cmd->width, s[i]); IP_FD_SET(s[i], &cmd->read_set); } } ipcom_microtime(&start); while ((cmd->transmit && total_bytes_to_send) || (cmd->receive && finished_sockets < cmd->num_sock)) { if (cmd->transmit && total_bytes_to_send) { for (i = 0; i < cmd->num_sock; i++) { if (cmd->testpattern) { /* Test patter is "[B|E|D]xxxxxx ", B = first 8 byte in buffer, E = last 8 bytes, D = all other */ for (c = 0; c < cmd->buf_len; c += 8) ipcom_sprintf(cmd->buf + c, "%c%06ld ", c == 0 ? '>' : (c >= cmd->buf_len - 8 ? '<' : '#'), cmd->send_pattern[i]++ % 1000000); } bytes = ipcom_send(s[i], cmd->buf, IP_MIN(cmd->buf_len, total_bytes_to_send), send_flags); if (bytes < 0 && ipcom_errno == IP_ERRNO_EWOULDBLOCK) (void)ipcom_sleep(0); else { if (bytes < 0) { ipcom_printf("sockperf-t: send failed : %s"IP_LF, ipcom_strerror(ipcom_errno)); return; } total_bytes_to_send -= (Ip_u32)bytes; } } if (cmd->receive && total_bytes_to_send == 0) for (i = 0; i < cmd->num_sock; i++) if (ipcom_shutdown(s[i], IP_SHUT_WR) < 0) { ipcom_printf("sockperf-t: shutdown failed: %s"IP_LF, ipcom_strerror(ipcom_errno)); return; } } if (cmd->receive) { ptmo = IP_NULL; while (finished_sockets < cmd->num_sock) { read_set = cmd->read_set; num_ready = ipcom_socketselect(cmd->width + 1, &read_set, IP_NULL, IP_NULL, ptmo); if (num_ready == 0) break; if (num_ready < 0) { ipcom_printf("sockperf-r: select failed: %s"IP_LF, ipcom_strerror(ipcom_errno)); return; } for (i = 0; i < cmd->num_sock; i++) { if (IP_FD_ISSET(s[i], &read_set)) { bytes = ipcom_recv(s[i], cmd->buf, cmd->buf_len, 0); if (bytes < 0) { ipcom_printf("sockperf-r: recv failed: %s"IP_LF, ipcom_strerror(ipcom_errno)); return; } if (cmd->testpattern) { if (cmd->echo) /* coverity[tainted_string] */ ipcom_cmd_sockperf_echo_buf(cmd->buf, (int)bytes, (int)cmd->recv_pattern[i]); for (c = 0; c < (Ip_u32)bytes; c += 8) { if (cmd->buf[c] != '#' && cmd->buf[c] != '>' && cmd->buf[c] != '<') { ipcom_printf("\nsockperf-r: test pattern error, expected B, D or E found %c(%d) offset %ld"IP_LF, cmd->buf[c], (int)cmd->buf[c], c); ipcom_cmd_sockperf_echo_buf(cmd->buf, (int)bytes, 0); return; } if (ipcom_atoi(cmd->buf + c + 1) != (int)(cmd->recv_pattern[i]++ % 1000000)) { ipcom_printf("\nsockperf-r: test pattern error, was %d should be %ld offset %ld"IP_LF, ipcom_atoi(cmd->buf + c + 1), (cmd->recv_pattern[i] - 1) % 1000000, c); ipcom_cmd_sockperf_echo_buf(cmd->buf, (int)bytes, 0); return; } } } if (bytes > 0) total_bytes_read += (Ip_u32)bytes; else { finished_sockets++; IP_FD_CLR(s[i], &cmd->read_set); } } } if (cmd->transmit && total_bytes_to_send) ptmo = &tmo; } } } ipcom_microtime(&stop); if (cmd->transmit) ipcom_printf("sockperf-t: %lu bytes sent in %ld ms (%lu kbyte/s)"IP_LF, cmd->num_buf * cmd->buf_len, ipcom_cmd_sockperf_tv_to_msec(&start, &stop), cmd->num_buf * cmd->buf_len / (Ip_u32)ipcom_cmd_sockperf_tv_to_msec(&start, &stop) * 1000 / 1024); if (cmd->receive) ipcom_printf("sockperf-r: %lu bytes read in %ld ms (%lu kbyte/s)"IP_LF, total_bytes_read, ipcom_cmd_sockperf_tv_to_msec(&start, &stop), total_bytes_read / (Ip_u32)ipcom_cmd_sockperf_tv_to_msec(&start, &stop) * 1000 / 1024); } /* *=========================================================================== * ipcom_cmd_sockperf_connect *=========================================================================== * Description: Active open of socket(s) * Parameters: * Returns: */ IP_STATIC void ipcom_cmd_sockperf_connect(Ipcom_cmd_sockperf_t *cmd) { Ip_fd *s = cmd->sock_array; Ip_u32 created_sockets; struct Ip_timeval start; struct Ip_timeval stop; ipcom_printf("sockperf-c: connecting to %s[%d]"IP_LF, cmd->res->ai_canonname, ip_ntohs(((struct Ip_sockaddr_in *)cmd->res->ai_addr)->sin_port)); ipcom_microtime(&start); for (created_sockets = 0; created_sockets < cmd->num_sock; created_sockets++) { s[created_sockets] = ipcom_socket(cmd->res->ai_family, cmd->res->ai_socktype, cmd->res->ai_protocol); if (s[created_sockets] == IP_SOCKERR) { ipcom_printf("sockperf-c: Failed to create socket: %s"IP_LF, ipcom_strerror(ipcom_errno)); created_sockets--; goto cleanup; } if (ipcom_setsockopt(s[created_sockets], IP_SOL_SOCKET, IP_SO_LINGER, &cmd->linger, sizeof(cmd->linger)) < 0) { ipcom_printf("sockperf-c: setsockopt SO_LINGER failed : %s"IP_LF, ipcom_strerror(ipcom_errno)); goto cleanup; } if (ipcom_connect(s[created_sockets], cmd->res->ai_addr, (Ip_socklen_t)cmd->res->ai_addrlen) < 0) { ipcom_printf("sockperf-c: Failed to connect: %s"IP_LF, ipcom_strerror(ipcom_errno)); goto cleanup; } } ipcom_microtime(&stop); ipcom_printf("sockperf-t: %ld sockets connected in %ld ms"IP_LF, created_sockets, ipcom_cmd_sockperf_tv_to_msec(&start, &stop)); ipcom_cmd_sockperf_run(cmd); cleanup: while (created_sockets > 0) (void)ipcom_socketclose(s[created_sockets--]); } /* *=========================================================================== * ipcom_cmd_sockperf_accept *=========================================================================== * Description: Passive open of socket(s). * Parameters: * Returns: */ IP_STATIC void ipcom_cmd_sockperf_accept(Ipcom_cmd_sockperf_t *cmd) { Ip_fd *s = cmd->sock_array; int listen_sock; Ip_u32 accepted_sockets = 0; struct Ip_timeval start; struct Ip_timeval stop; int on = 1; listen_sock = ipcom_socket(cmd->res->ai_family, cmd->res->ai_socktype, cmd->res->ai_protocol); if (listen_sock == IP_SOCKERR) { ipcom_printf("sockperf-a: Failed to create socket: %s"IP_LF, ipcom_strerror(ipcom_errno)); goto cleanup; } if (ipcom_setsockopt(listen_sock, IP_SOL_SOCKET, IP_SO_REUSEADDR, &on, sizeof(on)) < 0) { ipcom_printf("sockperf-a: setsockopt IP_SO_REUSEADDR failed : %s"IP_LF, ipcom_strerror(ipcom_errno)); return; } if (ipcom_bind(listen_sock, cmd->res->ai_addr, (Ip_socklen_t)cmd->res->ai_addrlen) < 0) { ipcom_printf("sockperf-a: Failed to bind: %s"IP_LF, ipcom_strerror(ipcom_errno)); goto cleanup; } if (ipcom_listen(listen_sock, (int)cmd->num_sock) < 0) { ipcom_printf("sockperf-a: Failed to listen: %s"IP_LF, ipcom_strerror(ipcom_errno)); goto cleanup; } ipcom_printf("sockperf-a: listen on %s[%d]"IP_LF, cmd->res->ai_canonname, ip_ntohs(((struct Ip_sockaddr_in *)cmd->res->ai_addr)->sin_port)); IP_FD_ZERO(&cmd->read_set); IP_FD_SET(listen_sock, &cmd->read_set); if (ipcom_socketselect(listen_sock + 1, &cmd->read_set, IP_NULL, IP_NULL, IP_NULL) != 1) { ipcom_printf("sockperf-a: Select failed for listen socket: %s"IP_LF, ipcom_strerror(ipcom_errno)); goto cleanup; } ipcom_microtime(&start); for (;accepted_sockets < cmd->num_sock; accepted_sockets++) { s[accepted_sockets] = ipcom_accept(listen_sock, IP_NULL, IP_NULL); if (s[accepted_sockets] == IP_SOCKERR) { ipcom_printf("sockperf-a: Failed to create socket: %s"IP_LF, ipcom_strerror(ipcom_errno)); goto cleanup; } if (ipcom_setsockopt(s[accepted_sockets], IP_SOL_SOCKET, IP_SO_LINGER, &cmd->linger, sizeof(cmd->linger)) < 0) { ipcom_printf("sockperf-a: setsockopt SO_LINGER failed : %s"IP_LF, ipcom_strerror(ipcom_errno)); goto cleanup; } } ipcom_microtime(&stop); ipcom_printf("sockperf-a: %ld sockets accepted in %ld ms"IP_LF, accepted_sockets, ipcom_cmd_sockperf_tv_to_msec(&start, &stop)); ipcom_cmd_sockperf_run(cmd); cleanup: if (listen_sock >= 0) (void)ipcom_socketclose(listen_sock); while (accepted_sockets-- > 0) (void)ipcom_socketclose(s[accepted_sockets]); } /* *=========================================================================== * ipcom_cmd_sockperf_print_usage *=========================================================================== * Description: * Parameters: * Returns: */ IP_STATIC void ipcom_cmd_sockperf_print_usage(void) { ipcom_printf("Network performance tool"IP_LF "usage: "IP_LF " sockperf [options] [address]"IP_LF " -6 use IPv6 (default is IPv4)"IP_LF " -a use passive open (default is active open)"IP_LF " -e echo test pattern on shell"IP_LF " -L linger time for all sockets (default is 3s)"IP_LF " -l number of bytes to write/read in each call (default 8192)"IP_LF " -n number of buffers to write (default 2048)"IP_LF " -P create(transmit) or verify(receive) test pattern"IP_LF " -p socket port (default 7373)"IP_LF " -R socket receive buffer size (default 16384)"IP_LF " -r receive data (not mutually exclusive with -t)"IP_LF " -s number of sockets to use, must be same on both ends (default 1)"IP_LF " -T socket send buffer size (default 32767)"IP_LF " -t transmit data (not mutually exclusive with -r)"IP_LF "example:"IP_LF " \"sockperf -r -a\" and \"sockperf -t \" should give about the same"IP_LF " performance value as \"ttcp -r\" and \"ttcp \""IP_LF IP_LF ); } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipcom_cmd_sockperf *=========================================================================== * Description: * Parameters: * Returns: * */ IP_PUBLIC int ipcom_cmd_sockperf(int argc, char **argv) { Ipcom_getopt opt; Ipcom_cmd_sockperf_t cmd; int c; struct Ip_addrinfo hints; char *hostname = "0.0.0.0"; char *servname = "7373"; int socktype = IP_SOCK_STREAM; int domain = IP_AF_INET; if (argc < 2) { ipcom_cmd_sockperf_print_usage(); return 0; } ipcom_memset(&cmd, 0, sizeof(cmd)); cmd.buf_len = 8192; cmd.num_buf = 2048; cmd.num_sock = 1; cmd.linger.l_onoff = 1; cmd.linger.l_linger = 3; cmd.sendbuf_size = 32767; cmd.recvbuf_size = 16384; ipcom_getopt_clear_r(&opt); while ((c = ipcom_getopt_r(argc, argv, "6aeL:l:n:Pp:R:rs:T:tu", &opt)) != -1) { switch(c) { case '6': domain = IP_AF_INET6; hostname = "::"; break; case 'a': cmd.accept = IP_TRUE; break; case 'e': cmd.echo = IP_TRUE; break; case 'L': cmd.linger.l_linger = ipcom_atoi(opt.optarg); break; case 'l': cmd.buf_len = (Ip_u32)ipcom_atoi(opt.optarg); break; case 'n': cmd.num_buf = (Ip_u32)ipcom_atoi(opt.optarg); break; case 'P': cmd.testpattern = IP_TRUE; break; case 'p': servname = opt.optarg; break; case 'R': cmd.recvbuf_size = ipcom_atoi(opt.optarg); break; case 'r': cmd.receive = IP_TRUE; break; case 's': cmd.num_sock = (Ip_u32)ipcom_atoi(opt.optarg); break; case 'T': cmd.sendbuf_size = ipcom_atoi(opt.optarg); break; case 't': cmd.transmit = IP_TRUE; break; case 'u': socktype = IP_SOCK_DGRAM; break; default: ipcom_printf("sockperf: unknown option %c"IP_LF, (char)c); return -1; } } if (cmd.testpattern) { if (cmd.buf_len & 0x7) { ipcom_printf("sockperf: the buffert (set with -l) must be a multiple of 8 when " "using a test pattern"IP_LF); goto cleanup; } if (cmd.transmit) { cmd.send_pattern = ipcom_calloc(cmd.num_sock, sizeof(Ip_u32)); if (cmd.send_pattern == IP_NULL) { ipcom_printf("sockperf: out of memory when allocating send pattern"IP_LF); goto cleanup; } } if (cmd.receive) { cmd.recv_pattern = ipcom_calloc(cmd.num_sock, sizeof(Ip_u32)); if (cmd.recv_pattern == IP_NULL) { ipcom_printf("sockperf: out of memory when allocating receive pattern"IP_LF); goto cleanup; } } } if (opt.optind < argc) hostname = argv[opt.optind]; ipcom_memset(&hints, 0, sizeof(hints)); hints.ai_socktype = socktype; hints.ai_flags = IP_AI_CANONNAME; hints.ai_family = domain; if (ipcom_getaddrinfo(hostname, servname, &hints, &cmd.res) != 0) { ipcom_printf("sockperf: getaddrinfo() failed"IP_LF); goto cleanup; } cmd.buf = ipcom_malloc(cmd.buf_len + 1); if (cmd.buf == IP_NULL) { ipcom_printf("sockperf: Failed to allocate %lu bytes" IP_LF, cmd.buf_len); goto cleanup; } cmd.sock_array = ipcom_malloc(cmd.num_sock * sizeof(int)); if (cmd.sock_array == IP_NULL) { ipcom_printf("sockperf: Failed to allocate %lu bytes for sockets" IP_LF, cmd.num_sock * sizeof(int)); goto cleanup; } if (cmd.accept) ipcom_cmd_sockperf_accept(&cmd); else ipcom_cmd_sockperf_connect(&cmd); cleanup: ipcom_free(cmd.send_pattern); ipcom_free(cmd.recv_pattern); ipcom_free(cmd.sock_array); ipcom_free(cmd.buf); ipcom_freeaddrinfo(cmd.res); return 0; } /* **************************************************************************** * END OF FILE **************************************************************************** */