| #ifndef lint |
| char nettest_id[]="\ |
| @(#)nettest_bsd.c (c) Copyright 1993-2008 Hewlett-Packard Co. Version 2.4.5"; |
| #endif /* lint */ |
| |
| |
| /****************************************************************/ |
| /* */ |
| /* nettest_bsd.c */ |
| /* */ |
| /* the BSD sockets parsing routine... */ |
| /* ...with the addition of Windows NT, this is now also */ |
| /* a Winsock test... sigh :) */ |
| /* */ |
| /* scan_sockets_args() */ |
| /* */ |
| /* the actual test routines... */ |
| /* */ |
| /* send_tcp_stream() perform a tcp stream test */ |
| /* recv_tcp_stream() */ |
| /* send_tcp_maerts() perform a tcp stream test */ |
| /* recv_tcp_maerts() in the other direction */ |
| /* send_tcp_rr() perform a tcp request/response */ |
| /* recv_tcp_rr() */ |
| /* send_tcp_conn_rr() an RR test including connect */ |
| /* recv_tcp_conn_rr() */ |
| /* send_tcp_cc() a connect/disconnect test with */ |
| /* recv_tcp_cc() no RR */ |
| /* send_tcp_mss() just report the mss */ |
| /* send_udp_stream() perform a udp stream test */ |
| /* recv_udp_stream() */ |
| /* send_udp_rr() perform a udp request/response */ |
| /* recv_udp_rr() */ |
| /* loc_cpu_rate() determine the local cpu maxrate */ |
| /* rem_cpu_rate() find the remote cpu maxrate */ |
| /* */ |
| /****************************************************************/ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| #if HAVE_SYS_TYPES_H |
| # include <sys/types.h> |
| #endif |
| #if HAVE_SYS_STAT_H |
| # include <sys/stat.h> |
| #endif |
| #if STDC_HEADERS |
| # include <stdlib.h> |
| # include <stddef.h> |
| #else |
| # if HAVE_STDLIB_H |
| # include <stdlib.h> |
| # endif |
| #endif |
| #if HAVE_STRING_H |
| # if !STDC_HEADERS && HAVE_MEMORY_H |
| # include <memory.h> |
| # endif |
| # include <string.h> |
| #endif |
| #if HAVE_STRINGS_H |
| # include <strings.h> |
| #endif |
| #if HAVE_INTTYPES_H |
| # include <inttypes.h> |
| #else |
| # if HAVE_STDINT_H |
| # include <stdint.h> |
| # endif |
| #endif |
| #if HAVE_UNISTD_H |
| # include <unistd.h> |
| #endif |
| |
| #include <fcntl.h> |
| #ifndef WIN32 |
| #include <errno.h> |
| #include <signal.h> |
| #endif |
| |
| #if TIME_WITH_SYS_TIME |
| # include <sys/time.h> |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| #endif |
| |
| #ifdef NOSTDLIBH |
| #include <malloc.h> |
| #endif /* NOSTDLIBH */ |
| |
| #ifdef WANT_SCTP |
| #include <netinet/sctp.h> |
| #endif |
| |
| #ifndef WIN32 |
| #if !defined(__VMS) && !defined(MSDOS) |
| #include <sys/ipc.h> |
| #endif /* !__VMS && !MSDOS */ |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <netinet/tcp.h> |
| #include <arpa/inet.h> |
| #include <netdb.h> |
| #else /* WIN32 */ |
| #include <process.h> |
| #define netperf_socklen_t socklen_t |
| #include <winsock2.h> |
| |
| /* while it is unlikely that anyone running Windows 2000 or NT 4 is |
| going to be trying to compile this, if they are they will want to |
| define DONT_IPV6 in the sources file */ |
| #ifndef DONT_IPV6 |
| #include <ws2tcpip.h> |
| #endif |
| #include <windows.h> |
| |
| #define sleep(x) Sleep((x)*1000) |
| |
| #define __func__ __FUNCTION__ |
| #endif /* WIN32 */ |
| |
| /* We don't want to use bare constants in the shutdown() call. In the |
| extremely unlikely event that SHUT_WR isn't defined, we will define |
| it to the value we used to be passing to shutdown() anyway. raj |
| 2007-02-08 */ |
| #if !defined(SHUT_WR) |
| #define SHUT_WR 1 |
| #endif |
| |
| #if !defined(HAVE_GETADDRINFO) || !defined(HAVE_GETNAMEINFO) |
| # include "missing/getaddrinfo.h" |
| #endif |
| |
| #include "netlib.h" |
| #include "netsh.h" |
| #include "nettest_bsd.h" |
| |
| #if defined(WANT_HISTOGRAM) || defined(WANT_DEMO) |
| #include "hist.h" |
| #endif /* WANT_HISTOGRAM */ |
| |
| |
| /* make first_burst_size unconditional so we can use it easily enough |
| when calculating transaction latency for the TCP_RR test. raj |
| 2007-06-08 however, change its default value so one can tell in |
| "omni" output whether or not WANT_BURST was enabled. raj |
| 2008-01-28 */ |
| #if defined(WANT_FIRST_BURST) |
| int first_burst_size=0; |
| #else |
| int first_burst_size=-1; |
| #endif |
| |
| #if defined(HAVE_SENDFILE) && (defined(__linux) || defined(__sun)) |
| #include <sys/sendfile.h> |
| #endif /* HAVE_SENDFILE && (__linux || __sun) */ |
| |
| |
| |
| /* these variables are specific to the BSD sockets tests, but can |
| * be used elsewhere if needed. They are externed through nettest_bsd.h |
| */ |
| |
| int |
| socket_type, /* used initially by the "omni" tests */ |
| rss_size_req = -1, /* requested remote socket send buffer size */ |
| rsr_size_req = -1, /* requested remote socket recv buffer size */ |
| rss_size, /* initial remote socket send buffer size */ |
| rsr_size, /* initial remote socket recv buffer size */ |
| rss_size_end = -1, /* final remote socket send buffer size */ |
| rsr_size_end = -1, /* final remote socket recv buffer size */ |
| lss_size_req = -1, /* requested local socket send buffer size */ |
| lsr_size_req = -1, /* requested local socket recv buffer size */ |
| lss_size, /* local socket send buffer size */ |
| lsr_size, /* local socket recv buffer size */ |
| lss_size_end = -1, /* final local socket send buffer size */ |
| lsr_size_end = -1, /* final local socket recv buffer size */ |
| req_size = 1, /* request size */ |
| rsp_size = 1, /* response size */ |
| send_size, /* how big are individual sends */ |
| recv_size; /* how big are individual receives */ |
| |
| static int confidence_iteration; |
| static char local_cpu_method; |
| static char remote_cpu_method; |
| |
| /* these will control the width of port numbers we try to use in the */ |
| /* TCP_CRR and/or TCP_TRR tests. raj 3/95 */ |
| static int client_port_min = 5000; |
| static int client_port_max = 65535; |
| |
| /* different options for the sockets */ |
| |
| int |
| loc_nodelay, /* don't/do use NODELAY locally */ |
| rem_nodelay, /* don't/do use NODELAY remotely */ |
| #ifdef TCP_CORK |
| loc_tcpcork=0, /* don't/do use TCP_CORK locally */ |
| rem_tcpcork=0, /* don't/do use TCP_CORK remotely */ |
| #else |
| loc_tcpcork=-1, |
| rem_tcpcork=-1, |
| #endif /* TCP_CORK */ |
| loc_sndavoid, /* avoid send copies locally */ |
| loc_rcvavoid, /* avoid recv copies locally */ |
| rem_sndavoid, /* avoid send copies remotely */ |
| rem_rcvavoid, /* avoid recv_copies remotely */ |
| local_connected = 0, /* local socket type, connected/non-connected */ |
| remote_connected = 0; /* remote socket type, connected/non-connected */ |
| |
| #ifdef WANT_HISTOGRAM |
| #ifdef HAVE_GETHRTIME |
| static hrtime_t time_one; |
| static hrtime_t time_two; |
| #elif HAVE_GET_HRT |
| #include "hrt.h" |
| static hrt_t time_one; |
| static hrt_t time_two; |
| #elif defined(WIN32) |
| static LARGE_INTEGER time_one; |
| static LARGE_INTEGER time_two; |
| #else |
| static struct timeval time_one; |
| static struct timeval time_two; |
| #endif /* HAVE_GETHRTIME */ |
| static HIST time_hist; |
| #endif /* WANT_HISTOGRAM */ |
| |
| #ifdef WANT_INTERVALS |
| int interval_count; |
| #ifndef WANT_SPIN |
| sigset_t signal_set; |
| #define INTERVALS_INIT() \ |
| if (interval_burst) { \ |
| /* zero means that we never pause, so we never should need the \ |
| interval timer. we used to use it for demo mode, but we deal \ |
| with that with a variant on watching the clock rather than \ |
| waiting for a timer. raj 2006-02-06 */ \ |
| start_itimer(interval_wate); \ |
| } \ |
| interval_count = interval_burst; \ |
| /* get the signal set for the call to sigsuspend */ \ |
| if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &signal_set) != 0) { \ |
| fprintf(where, \ |
| "%s: unable to get sigmask errno %d\n", \ |
| __func__, \ |
| errno); \ |
| fflush(where); \ |
| exit(1); \ |
| } |
| |
| #define INTERVALS_WAIT() \ |
| /* in this case, the interval count is the count-down couter \ |
| to decide to sleep for a little bit */ \ |
| if ((interval_burst) && (--interval_count == 0)) { \ |
| /* call sigsuspend and wait for the interval timer to get us \ |
| out */ \ |
| if (debug > 1) { \ |
| fprintf(where,"about to suspend\n"); \ |
| fflush(where); \ |
| } \ |
| if (sigsuspend(&signal_set) == EFAULT) { \ |
| fprintf(where, \ |
| "%s: fault with sigsuspend.\n", \ |
| __func__); \ |
| fflush(where); \ |
| exit(1); \ |
| } \ |
| interval_count = interval_burst; \ |
| } |
| #else |
| /* first out timestamp */ |
| #ifdef HAVE_GETHRTIME |
| static hrtime_t intvl_one; |
| static hrtime_t intvl_two; |
| static hrtime_t *intvl_one_ptr = &intvl_one; |
| static hrtime_t *intvl_two_ptr = &intvl_two; |
| static hrtime_t *temp_intvl_ptr = &intvl_one; |
| #elif defined(WIN32) |
| static LARGE_INTEGER intvl_one; |
| static LARGE_INTEGER intvl_two; |
| static LARGE_INTEGER *intvl_one_ptr = &intvl_one; |
| static LARGE_INTEGER *intvl_two_ptr = &intvl_two; |
| static LARGE_INTEGER *temp_intvl_ptr = &intvl_one; |
| #else |
| static struct timeval intvl_one; |
| static struct timeval intvl_two; |
| static struct timeval *intvl_one_ptr = &intvl_one; |
| static struct timeval *intvl_two_ptr = &intvl_two; |
| static struct timeval *temp_intvl_ptr = &intvl_one; |
| #endif |
| |
| #define INTERVALS_INIT() \ |
| if (interval_burst) { \ |
| HIST_timestamp(intvl_one_ptr); \ |
| } \ |
| interval_count = interval_burst; \ |
| |
| #define INTERVALS_WAIT() \ |
| /* in this case, the interval count is the count-down couter \ |
| to decide to sleep for a little bit */ \ |
| if ((interval_burst) && (--interval_count == 0)) { \ |
| /* call sigsuspend and wait for the interval timer to get us \ |
| out */ \ |
| if (debug > 1) { \ |
| fprintf(where,"about to spin suspend\n"); \ |
| fflush(where); \ |
| } \ |
| HIST_timestamp(intvl_two_ptr); \ |
| while(delta_micro(intvl_one_ptr,intvl_two_ptr) < interval_usecs) { \ |
| HIST_timestamp(intvl_two_ptr); \ |
| } \ |
| temp_intvl_ptr = intvl_one_ptr; \ |
| intvl_one_ptr = intvl_two_ptr; \ |
| intvl_two_ptr = temp_intvl_ptr; \ |
| interval_count = interval_burst; \ |
| } |
| #endif |
| #endif |
| |
| #ifdef WANT_DEMO |
| #ifdef HAVE_GETHRTIME |
| static hrtime_t demo_one; |
| static hrtime_t demo_two; |
| static hrtime_t *demo_one_ptr = &demo_one; |
| static hrtime_t *demo_two_ptr = &demo_two; |
| static hrtime_t *temp_demo_ptr = &demo_one; |
| #elif defined(WIN32) |
| static LARGE_INTEGER demo_one; |
| static LARGE_INTEGER demo_two; |
| static LARGE_INTEGER *demo_one_ptr = &demo_one; |
| static LARGE_INTEGER *demo_two_ptr = &demo_two; |
| static LARGE_INTEGER *temp_demo_ptr = &demo_one; |
| #else |
| static struct timeval demo_one; |
| static struct timeval demo_two; |
| static struct timeval *demo_one_ptr = &demo_one; |
| static struct timeval *demo_two_ptr = &demo_two; |
| static struct timeval *temp_demo_ptr = &demo_one; |
| #endif |
| |
| /* for a _STREAM test, "a" should be lss_size and "b" should be |
| rsr_size. for a _MAERTS test, "a" should be lsr_size and "b" should |
| be rss_size. raj 2005-04-06 */ |
| #define DEMO_STREAM_SETUP(a,b) \ |
| if ((demo_mode) && (demo_units == 0)) { \ |
| /* take our default value of demo_units to be the larger of \ |
| twice the remote's SO_RCVBUF or twice our SO_SNDBUF */ \ |
| if (a > b) { \ |
| demo_units = 2*a; \ |
| } \ |
| else { \ |
| demo_units = 2*b; \ |
| } \ |
| } |
| |
| /* now that calc_thruput_interval knows about transactions as a format |
| we can merge DEMO_STREAM_INTERVAL and DEMO_RR_INTERVAL since the |
| are the same */ |
| |
| #define DEMO_INTERVAL(units) \ |
| if (demo_mode) { \ |
| double actual_interval; \ |
| units_this_tick += units; \ |
| if (units_this_tick >= demo_units) { \ |
| /* time to possibly update demo_units and maybe output an \ |
| interim result */ \ |
| HIST_timestamp(demo_two_ptr); \ |
| actual_interval = delta_micro(demo_one_ptr,demo_two_ptr); \ |
| /* we always want to fine-tune demo_units here whether we \ |
| emit an interim result or not. if we are short, this \ |
| will lengthen demo_units. if we are long, this will \ |
| shorten it */ \ |
| demo_units = demo_units * (demo_interval / actual_interval); \ |
| if (actual_interval >= demo_interval) { \ |
| /* time to emit an interim result */ \ |
| fprintf(where, \ |
| "Interim result: %.2f %s/s over %.2f seconds\n", \ |
| calc_thruput_interval(units_this_tick, \ |
| actual_interval/1000000.0), \ |
| format_units(), \ |
| actual_interval/1000000.0); \ |
| units_this_tick = 0.0; \ |
| /* now get a new starting timestamp. we could be clever \ |
| and swap pointers - the math we do probably does not \ |
| take all that long, but for now this will suffice */ \ |
| temp_demo_ptr = demo_one_ptr; \ |
| demo_one_ptr = demo_two_ptr; \ |
| demo_two_ptr = temp_demo_ptr; \ |
| } \ |
| } \ |
| } |
| |
| #define DEMO_STREAM_INTERVAL(units) DEMO_INTERVAL(units) |
| |
| #define DEMO_RR_SETUP(a) \ |
| if ((demo_mode) && (demo_units == 0)) { \ |
| /* take whatever we are given */ \ |
| demo_units = a; \ |
| } |
| |
| #define DEMO_RR_INTERVAL(units) DEMO_INTERVAL(units) |
| |
| #endif |
| |
| char sockets_usage[] = "\n\ |
| Usage: netperf [global options] -- [test options] \n\ |
| \n\ |
| TCP/UDP BSD Sockets Test Options:\n\ |
| -b number Send number requests at start of _RR tests\n\ |
| -C Set TCP_CORK when available\n\ |
| -D [L][,R] Set TCP_NODELAY locally and/or remotely (TCP_*)\n\ |
| -h Display this text\n\ |
| -H name,fam Use name (or IP) and family as target of data connection\n\ |
| -L name,fam Use name (or IP) and family as source of data connection\n\ |
| -m bytes Set the send size (TCP_STREAM, UDP_STREAM)\n\ |
| -M bytes Set the recv size (TCP_STREAM, UDP_STREAM)\n\ |
| -n Use the connected socket for UDP locally\n\ |
| -N Use the connected socket for UDP remotely\n\ |
| -p min[,max] Set the min/max port numbers for TCP_CRR, TCP_TRR\n\ |
| -P local[,remote] Set the local/remote port for the data socket\n\ |
| -r req,[rsp] Set request/response sizes (TCP_RR, UDP_RR)\n\ |
| -s send[,recv] Set local socket send/recv buffer sizes\n\ |
| -S send[,recv] Set remote socket send/recv buffer sizes\n\ |
| -4 Use AF_INET (eg IPv4) on both ends of the data conn\n\ |
| -6 Use AF_INET6 (eg IPv6) on both ends of the data conn\n\ |
| \n\ |
| For those options taking two parms, at least one must be specified;\n\ |
| specifying one value without a comma will set both parms to that\n\ |
| value, specifying a value with a leading comma will set just the second\n\ |
| parm, a value with a trailing comma will set just the first. To set\n\ |
| each parm to unique values, specify both and separate them with a\n\ |
| comma.\n"; |
| |
| |
| |
| /* these routines convert between the AF address space and the NF |
| address space since the numeric values of AF_mumble are not the |
| same across the platforms. raj 2005-02-08 */ |
| |
| int |
| nf_to_af(int nf) { |
| switch(nf) { |
| case NF_INET: |
| return AF_INET; |
| break; |
| case NF_UNSPEC: |
| return AF_UNSPEC; |
| break; |
| case NF_INET6: |
| #if defined(AF_INET6) |
| return AF_INET6; |
| #else |
| return AF_UNSPEC; |
| #endif |
| break; |
| default: |
| return AF_UNSPEC; |
| break; |
| } |
| } |
| |
| int |
| af_to_nf(int af) { |
| |
| switch(af) { |
| case AF_INET: |
| return NF_INET; |
| break; |
| case AF_UNSPEC: |
| return NF_UNSPEC; |
| break; |
| #if defined(AF_INET6) |
| case AF_INET6: |
| return NF_INET6; |
| break; |
| #endif |
| default: |
| return NF_UNSPEC; |
| break; |
| } |
| } |
| |
| |
| /* these routines will convert between the hosts' socket types and |
| those netperf uses. we need this because different platforms can |
| have different values for SOCK_STREAM, SOCK_DGRAM and the |
| like... */ |
| |
| int |
| nst_to_hst(int nst) { |
| switch(nst) { |
| #ifdef SOCK_STREAM |
| case NST_STREAM: |
| return SOCK_STREAM; |
| break; /* ok, this may not be necessary :) */ |
| #endif |
| #ifdef SOCK_DGRAM |
| case NST_DGRAM: |
| return SOCK_DGRAM; |
| break; |
| #endif |
| #ifdef SOCK_DCCP |
| case NST_DCCP: |
| return SOCK_DCCP; |
| break; |
| #endif |
| default: |
| return -1; |
| } |
| } |
| |
| int |
| hst_to_nst(int hst) { |
| |
| switch(hst) { |
| #ifdef SOCK_STREAM |
| case SOCK_STREAM: |
| return NST_STREAM; |
| break; |
| #endif |
| #ifdef SOCK_DGRAM |
| case SOCK_DGRAM: |
| return NST_DGRAM; |
| break; |
| #endif |
| #ifdef SOCK_DCCP |
| case SOCK_DCCP: |
| return NST_DCCP; |
| break; |
| #endif |
| default: |
| return NST_UNKN; |
| } |
| } |
| char * |
| hst_to_str(int hst) { |
| |
| switch(hst) { |
| #ifdef SOCK_STREAM |
| case SOCK_STREAM: |
| return "Stream"; |
| break; |
| #endif |
| #ifdef SOCK_DGRAM |
| case SOCK_DGRAM: |
| return "Datagram"; |
| break; |
| #endif |
| #ifdef SOCK_DCCP |
| case SOCK_DCCP: |
| return "DCCP"; |
| break; |
| #endif |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| char * |
| protocol_to_str(int protocol) { |
| switch(protocol) { |
| #ifdef IPPROTO_TCP |
| case IPPROTO_TCP: |
| return "TCP"; |
| #endif |
| #ifdef IPPROTO_UDP |
| case IPPROTO_UDP: |
| return "UDP"; |
| #endif |
| #ifdef IPPROTO_SCTP |
| case IPPROTO_SCTP: |
| return "SCTP"; |
| #endif |
| #ifdef IPPROTO_DCCP |
| case IPPROTO_DCCP: |
| return "DCCP"; |
| #endif |
| #ifdef IPPROTO_SDP |
| case IPPROTO_SDP: |
| return "SDP"; |
| #endif |
| default: |
| return "Unknown Protocol"; |
| } |
| } |
| |
| |
| /* This routine is intended to retrieve interesting aspects of tcp */ |
| /* for the data connection. at first, it attempts to retrieve the */ |
| /* maximum segment size. later, it might be modified to retrieve */ |
| /* other information, but it must be information that can be */ |
| /* retrieved quickly as it is called during the timing of the test. */ |
| /* for that reason, a second routine may be created that can be */ |
| /* called outside of the timing loop */ |
| static |
| void |
| get_tcp_info(SOCKET socket, int *mss) |
| { |
| |
| #ifdef TCP_MAXSEG |
| netperf_socklen_t sock_opt_len; |
| |
| sock_opt_len = sizeof(netperf_socklen_t); |
| if (getsockopt(socket, |
| getprotobyname("tcp")->p_proto, |
| TCP_MAXSEG, |
| (char *)mss, |
| &sock_opt_len) == SOCKET_ERROR) { |
| fprintf(where, |
| "netperf: get_tcp_info: getsockopt TCP_MAXSEG: errno %d\n", |
| errno); |
| fflush(where); |
| *mss = -1; |
| } |
| #else |
| *mss = -1; |
| #endif /* TCP_MAXSEG */ |
| } |
| |
| |
| /* return a pointer to a completed addrinfo chain - prefer |
| data_address to controlhost and utilize the specified address |
| family */ |
| |
| struct addrinfo * |
| complete_addrinfo(char *controlhost, char *data_address, char *port, int family, int type, int protocol, int flags) |
| { |
| struct addrinfo hints; |
| struct addrinfo *res; |
| struct addrinfo *temp_res; |
| |
| #define CHANGED_SOCK_TYPE 0x1 |
| #define CHANGED_PROTOCOL 0x2 |
| #define CHANGED_SCTP 0x4 |
| #define CHANGED_DCCP 0x8 |
| #define CHANGED_DCCP_SOCK 0x10 |
| |
| int change_info = 0; |
| static int change_warning_displayed = 0; |
| |
| int count = 0; |
| int error = 0; |
| |
| char *hostname; |
| |
| /* take data-address over controlhost */ |
| if (data_address) |
| hostname = data_address; |
| else |
| hostname = controlhost; |
| |
| if (debug) { |
| fprintf(where, |
| "complete_addrinfo using hostname %s port %s family %s type %s prot %s flags 0x%x\n", |
| hostname, |
| port, |
| inet_ftos(family), |
| inet_ttos(type), |
| inet_ptos(protocol), |
| flags); |
| fflush(where); |
| } |
| |
| memset(&hints, 0, sizeof(hints)); |
| hints.ai_family = family; |
| hints.ai_socktype = type; |
| hints.ai_protocol = protocol; |
| hints.ai_flags = flags|AI_CANONNAME; |
| |
| count = 0; |
| do { |
| error = getaddrinfo((char *)hostname, |
| (char *)port, |
| &hints, |
| &res); |
| count += 1; |
| if (error == EAI_AGAIN) { |
| if (debug) { |
| fprintf(where,"Sleeping on getaddrinfo EAI_AGAIN\n"); |
| fflush(where); |
| } |
| sleep(1); |
| } |
| /* while you see this kludge first, it is actually the second, the |
| first being the one for Solaris below. The need for this kludge |
| came after implementing the Solaris broken getaddrinfo kludge - |
| now we see a kludge in Linux getaddrinfo where if it is given |
| SOCK_STREAM and IPPROTO_SCTP it barfs with a -7 |
| EAI_SOCKTYPE. so, we check if the error was EAI_SOCKTYPE and if |
| we were asking for IPPROTO_SCTP and if so, kludge, again... raj |
| 200?-10-13 and of course, requiring the kludge for SCTP, it is |
| no surprise that linux needs a kludge for DCCP...actually not |
| only does it need the ai_protocol kludge, it needs an |
| ai_socktype kludge too... sigh raj 2008-02-01 */ |
| #if defined(IPPROTO_SCTP) || defined (IPPROTO_DCCP) |
| if (EAI_SOCKTYPE == error |
| #ifdef EAI_BADHINTS |
| || EAI_BADHINTS == error |
| #endif |
| ) { |
| /* we ass-u-me this is the Linux getaddrinfo bug, clear the |
| hints.ai_protocol field, and set some state "remembering" |
| that we did this so the code for the Solaris kludge can do |
| the fix-up for us. also flip error over to EAI_AGAIN and |
| make sure we don't "count" this time around the loop. */ |
| #if defined(IPPROTO_DCCP) |
| /* only tweak on this one the second time around, after we've |
| kludged the ai_protocol field */ |
| if ((hints.ai_socktype == SOCK_DCCP) && |
| (hints.ai_protocol == 0)) { |
| change_info |= CHANGED_DCCP_SOCK; |
| hints.ai_socktype = 0; |
| /* we need to give it some sort of IPPROTO or it gets unhappy, |
| so for now, pick one from deep within the colon and use |
| IPPROTO_TCP */ |
| hints.ai_protocol = IPPROTO_TCP; |
| } |
| |
| if (hints.ai_protocol == IPPROTO_DCCP) { |
| change_info |= CHANGED_DCCP; |
| hints.ai_protocol = 0; |
| } |
| |
| #endif |
| #if defined(IPPROTO_SCTP) |
| if (hints.ai_protocol == IPPROTO_SCTP) { |
| change_info |= CHANGED_SCTP; |
| hints.ai_protocol = 0; |
| } |
| #endif |
| |
| error = EAI_AGAIN; |
| count -= 1; |
| } |
| #endif |
| } while ((error == EAI_AGAIN) && (count <= 5)); |
| |
| if (error) { |
| fprintf(where, |
| "complete_addrinfo: could not resolve '%s' port '%s' af %d", |
| hostname, |
| port, |
| family); |
| fprintf(where, |
| "\n\tgetaddrinfo returned %d %s\n", |
| error, |
| gai_strerror(error)); |
| fflush(where); |
| exit(-1); |
| } |
| |
| /* there exists at least one platform - Solaris 10 - that does not |
| seem to completely honor the ai_protocol and/or ai_socktype one |
| sets in the hints parm to the getaddrinfo call. so, we need to |
| walk the list of entries returned and if either of those do not |
| match what we asked for, we need to go ahead and set them |
| "correctly" this is based in part on some earlier SCTP-only code |
| from previous revisions. raj 2006-10-09 */ |
| |
| temp_res = res; |
| |
| while (temp_res) { |
| |
| if ((type) && |
| (temp_res->ai_socktype != type)) { |
| change_info |= CHANGED_SOCK_TYPE; |
| if (debug) { |
| fprintf(where, |
| "WARNING! Changed bogus getaddrinfo socket type %d to %d\n", |
| temp_res->ai_socktype, |
| type); |
| fflush(where); |
| } |
| temp_res->ai_socktype = type; |
| } |
| |
| if ((protocol) && |
| (temp_res->ai_protocol != protocol)) { |
| change_info |= CHANGED_PROTOCOL; |
| if (debug) { |
| fprintf(where, |
| "WARNING! Changed bogus getaddrinfo protocol %d to %d\n", |
| temp_res->ai_protocol, |
| protocol); |
| fflush(where); |
| } |
| temp_res->ai_protocol = protocol; |
| } |
| temp_res = temp_res->ai_next; |
| } |
| |
| if ((change_info & CHANGED_SOCK_TYPE) && |
| !(change_warning_displayed & CHANGED_SOCK_TYPE)) { |
| change_warning_displayed |= CHANGED_SOCK_TYPE; |
| fprintf(where, |
| "WARNING! getaddrinfo returned a socket type which did not\n"); |
| fprintf(where, |
| "match the requested type. Please contact your vendor for\n"); |
| fprintf(where, |
| "a fix to this bug in getaddrinfo()\n"); |
| fflush(where); |
| } |
| |
| /* if we dropped the protocol hint, it would be for a protocol that |
| getaddrinfo() wasn't supporting yet, not for the bug that it took |
| our hint and still returned zero. raj 2006-10-16 */ |
| /* as there is now an open bug against (Open)Solaris (id 6847733) on |
| this behaviour we will only emit this warning if debug is set |
| under Solaris and will continue to emit it under any circumstance |
| on other platforms should it arise. raj 2009-06-03 */ |
| if ((change_info & CHANGED_PROTOCOL) && |
| !(change_warning_displayed & CHANGED_PROTOCOL) && |
| #ifdef __sun |
| (debug) && |
| #endif |
| (hints.ai_protocol != 0)) { |
| change_warning_displayed |= CHANGED_PROTOCOL; |
| fprintf(where, |
| "WARNING! getaddrinfo returned a protocol other than the\n"); |
| fprintf(where, |
| "requested protocol. Please contact your vendor for\n"); |
| fprintf(where, |
| "a fix to this bug in getaddrinfo()\n"); |
| fflush(where); |
| } |
| |
| if ((change_info & CHANGED_SCTP) && |
| !(change_warning_displayed & CHANGED_SCTP)) { |
| change_warning_displayed |= CHANGED_SCTP; |
| fprintf(where, |
| "WARNING! getaddrinfo on this platform does not accept IPPROTO_SCTP!\n"); |
| fprintf(where, |
| "Please contact your vendor for a fix to this bug in getaddrinfo().\n"); |
| fflush(where); |
| } |
| |
| if ((change_info & CHANGED_DCCP) && |
| !(change_warning_displayed & CHANGED_DCCP)) { |
| change_warning_displayed |= CHANGED_DCCP; |
| fprintf(where, |
| "WARNING! getaddrinfo on this platform does not accept IPPROTO_DCCP!\n"); |
| fprintf(where, |
| "Please contact your vendor for a fix to this bug in getaddrinfo().\n"); |
| fflush(where); |
| } |
| |
| |
| if (debug) { |
| dump_addrinfo(where, res, hostname, port, family); |
| } |
| |
| return(res); |
| } |
| |
| void |
| complete_addrinfos(struct addrinfo **remote,struct addrinfo **local, char remote_host[], int type, int protocol, int flags) { |
| |
| *remote = complete_addrinfo(remote_host, |
| remote_data_address, |
| remote_data_port, |
| remote_data_family, |
| type, |
| protocol, |
| flags); |
| |
| /* OK, if the user has not specified a local data endpoint address |
| (test-specific -L), pick the local data endpoint address based on |
| the remote data family info (test-specific -H or -4 or -6 |
| option). if the user has not specified remote data addressing |
| info (test-specific -H, -4 -6) pick something based on the local |
| control connection address (ie the global -L option). */ |
| |
| if (NULL == local_data_address) { |
| local_data_address = malloc(HOSTNAMESIZE); |
| if (NULL == remote_data_address) { |
| if (debug) { |
| fprintf(where, |
| "local_data_address not set, using local_host_name of '%s'\n", |
| local_host_name); |
| fflush(where); |
| } |
| strcpy(local_data_address,local_host_name); |
| } |
| else { |
| if (debug) { |
| fprintf(where, |
| "local_data_address not set, using address family info\n"); |
| fflush(where); |
| } |
| /* by default, use 0.0.0.0 - assume IPv4 */ |
| strcpy(local_data_address,"0.0.0.0"); |
| #if defined(AF_INET6) |
| if ((AF_INET6 == local_data_family) || |
| ((AF_UNSPEC == local_data_family) && |
| (AF_INET6 == remote_data_family)) || |
| ((AF_UNSPEC == local_data_family) && |
| (AF_INET6 == (*remote)->ai_family))) { |
| strcpy(local_data_address,"::0"); |
| } |
| #endif |
| } |
| } |
| |
| *local = complete_addrinfo("what to put here?", |
| local_data_address, |
| local_data_port, |
| local_data_family, |
| type, |
| protocol, |
| flags|AI_PASSIVE); |
| |
| /* OK, at this point, if remote_data_address is NULL, we know that |
| we used the value of remote_host (the control connection) for the |
| remote, which means we can/should set remote_data_address to |
| remote_host so the "omni" output routines can use that global |
| variable. at least i think I can get away with that :) I'm sure |
| that at some point I'll find-out that I need to allocate |
| something for it rather than mess with the pointers, but that can |
| wait. famous last words of raj 2008-01-25 */ |
| if (remote_data_address == NULL) |
| remote_data_address = remote_host; |
| } |
| |
| void |
| set_hostname_and_port(char *hostname, char *portstr, int family, int port) |
| { |
| strcpy(hostname,"0.0.0.0"); |
| #if defined AF_INET6 |
| if (AF_INET6 == family) { |
| strcpy(hostname,"::0"); |
| } |
| #endif |
| |
| sprintf(portstr, "%u", port); |
| |
| } |
| |
| static unsigned short |
| get_port_number(struct addrinfo *res) |
| { |
| switch(res->ai_family) { |
| case AF_INET: { |
| struct sockaddr_in *foo = (struct sockaddr_in *)res->ai_addr; |
| return(ntohs(foo->sin_port)); |
| break; |
| } |
| #if defined(AF_INET6) |
| case AF_INET6: { |
| struct sockaddr_in6 *foo = (struct sockaddr_in6 *)res->ai_addr; |
| return(ntohs(foo->sin6_port)); |
| break; |
| } |
| #endif |
| default: |
| fprintf(where, |
| "Given Unexpected Address Family of %u\n",res->ai_family); |
| fflush(where); |
| exit(-1); |
| } |
| } |
| |
| static void |
| extract_inet_address_and_port(struct addrinfo *res, void *addr, int len, int *port) |
| { |
| switch(res->ai_family) { |
| case AF_INET: { |
| struct sockaddr_in *foo = (struct sockaddr_in *)res->ai_addr; |
| *port = foo->sin_port; |
| memcpy(addr,&(foo->sin_addr),min(len,sizeof(foo->sin_addr))); |
| break; |
| } |
| #if defined(AF_INET6) |
| case AF_INET6: { |
| struct sockaddr_in6 *foo = (struct sockaddr_in6 *)res->ai_addr; |
| *port = foo->sin6_port; |
| memcpy(addr,&(foo->sin6_addr),min(len,sizeof(foo->sin6_addr))); |
| break; |
| } |
| #endif |
| default: |
| *port = 0xDEADBEEF; |
| strncpy(addr,"UNKN FAMILY",len); |
| } |
| } |
| |
| /* this routine will set the port number of the sockaddr in the |
| addrinfo to the specified value, based on the address family */ |
| void |
| set_port_number(struct addrinfo *res, unsigned short port) |
| { |
| switch(res->ai_family) { |
| case AF_INET: { |
| struct sockaddr_in *foo = (struct sockaddr_in *)res->ai_addr; |
| foo->sin_port = htons(port); |
| break; |
| } |
| #if defined(AF_INET6) |
| case AF_INET6: { |
| struct sockaddr_in6 *foo = (struct sockaddr_in6 *)res->ai_addr; |
| foo->sin6_port = htons(port); |
| break; |
| } |
| #endif |
| default: |
| fprintf(where, |
| "set_port_number Unexpected Address Family of %u\n",res->ai_family); |
| fflush(where); |
| exit(-1); |
| } |
| } |
| |
| /* stuff the address family, port number and address into a |
| sockaddr. for now, we will go ahead and zero-out the sockaddr |
| first */ |
| void |
| set_sockaddr_family_addr_port(struct sockaddr_storage *sockaddr, int family, void *addr, int port) { |
| |
| memset(sockaddr,0,sizeof(struct sockaddr_storage)); |
| |
| switch (family) { |
| case AF_INET: { |
| struct sockaddr_in *foo = (struct sockaddr_in *)sockaddr; |
| foo->sin_port = htons((unsigned short) port); |
| foo->sin_family = (unsigned short) family; |
| memcpy(&(foo->sin_addr),addr,sizeof(foo->sin_addr)); |
| *(int *)addr = htonl(*(int *)addr); |
| break; |
| } |
| #if defined(AF_INET6) |
| case AF_INET6: { |
| struct sockaddr_in6 *foo = (struct sockaddr_in6 *)sockaddr; |
| int *bar; |
| int i; |
| foo->sin6_port = htons((unsigned short) port); |
| foo->sin6_family = (unsigned short) family; |
| memcpy(&(foo->sin6_addr),addr,sizeof(foo->sin6_addr)); |
| /* how to put this into "host" order? */ |
| for (i = sizeof(foo->sin6_addr)/sizeof(int), bar=addr; i > 0; i--) { |
| bar[i] = htonl(bar[i]); |
| } |
| break; |
| } |
| #endif |
| default: |
| fprintf(where, |
| "set_sockaddr_family_addr_port Unexpected Address Family of %u\n",family); |
| fflush(where); |
| exit(-1); |
| } |
| } |
| |
| /* pull the port and address out of the sockaddr in host format */ |
| int |
| get_sockaddr_family_addr_port(struct sockaddr_storage *sockaddr, int family, void *addr, int *port) |
| { |
| struct sockaddr_in *sin = (struct sockaddr_in *)sockaddr; |
| |
| int ret = 0; |
| if (sin->sin_family != family) { |
| fprintf(where, |
| "get_sockaddr_family_addr_port family mismatch %d vs %d\n", |
| sin->sin_family, |
| family); |
| fflush(where); |
| return -1; |
| } |
| |
| switch(family) { |
| case AF_INET: { |
| *port = ntohs(sin->sin_port); |
| memcpy(addr,&(sin->sin_addr),sizeof(sin->sin_addr)); |
| if (*(int *)addr == INADDR_ANY) ret = 1; |
| *(int *)addr = ntohl(*(int *)addr); |
| break; |
| } |
| #ifdef AF_INET6 |
| case AF_INET6: { |
| int *foo; |
| int i; |
| struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sockaddr; |
| ret = 0; |
| *port = ntohs(sin6->sin6_port); |
| memcpy(addr,&(sin6->sin6_addr), sizeof(sin6->sin6_addr)); |
| /* how to put this into "host" order? */ |
| for (i = sizeof(sin6->sin6_addr)/sizeof(int), foo=addr; i > 0; i--) { |
| if (foo[i] != 0) ret = 1; |
| foo[i] = ntohl(foo[i]); |
| } |
| break; |
| } |
| #endif |
| default: |
| fprintf(where, |
| "get_sockaddr_family_addr_port: Unexpected Address Family of %u\n",family); |
| fflush(where); |
| exit(-1); |
| } |
| return ret; |
| } |
| |
| |
| /* This routine will create a data (listen) socket with the |
| apropriate options set and return it to the caller. this replaces |
| all the duplicate code in each of the test routines and should help |
| make things a little easier to understand. since this routine can be |
| called by either the netperf or netserver programs, all output |
| should be directed towards "where." family is generally AF_INET and |
| type will be either SOCK_STREAM or SOCK_DGRAM. This routine will |
| also be used by the "SCTP" tests, hence the slightly strange-looking |
| SCTP stuff in the classic bsd sockets test file... vlad/raj |
| 2005-03-15 */ |
| |
| SOCKET |
| create_data_socket(struct addrinfo *res) |
| { |
| |
| SOCKET temp_socket; |
| int one; |
| int on = 1; |
| |
| |
| /*set up the data socket */ |
| temp_socket = socket(res->ai_family, |
| res->ai_socktype, |
| res->ai_protocol); |
| |
| if (temp_socket == INVALID_SOCKET){ |
| fprintf(where, |
| "netperf: create_data_socket: socket: errno %d fam %s type %s prot %s errmsg %s\n", |
| errno, |
| inet_ftos(res->ai_family), |
| inet_ttos(res->ai_socktype), |
| inet_ptos(res->ai_protocol), |
| strerror(errno)); |
| fflush(where); |
| exit(1); |
| } |
| |
| if (debug) { |
| fprintf(where,"create_data_socket: socket %d obtained...\n",temp_socket); |
| fflush(where); |
| } |
| |
| /* Modify the local socket size. The reason we alter the send buffer |
| size here rather than when the connection is made is to take care |
| of decreases in buffer size. Decreasing the window size after |
| connection establishment is a TCP no-no. Also, by setting the |
| buffer (window) size before the connection is established, we can |
| control the TCP MSS (segment size). The MSS is never (well, should |
| never be) more that 1/2 the minimum receive buffer size at each |
| half of the connection. This is why we are altering the receive |
| buffer size on the sending size of a unidirectional transfer. If |
| the user has not requested that the socket buffers be altered, we |
| will try to find-out what their values are. If we cannot touch the |
| socket buffer in any way, we will set the values to -1 to indicate |
| that. */ |
| |
| /* all the oogy nitty gritty stuff moved from here into the routine |
| being called below, per patches from davidm to workaround the bug |
| in Linux getsockopt(). raj 2004-06-15 */ |
| set_sock_buffer (temp_socket, SEND_BUFFER, lss_size_req, &lss_size); |
| set_sock_buffer (temp_socket, RECV_BUFFER, lsr_size_req, &lsr_size); |
| |
| /* now, we may wish to enable the copy avoidance features on the */ |
| /* local system. of course, this may not be possible... */ |
| |
| #ifdef SO_RCV_COPYAVOID |
| if (loc_rcvavoid) { |
| if (setsockopt(temp_socket, |
| SOL_SOCKET, |
| SO_RCV_COPYAVOID, |
| (const char *)&loc_rcvavoid, |
| sizeof(int)) == SOCKET_ERROR) { |
| fprintf(where, |
| "netperf: create_data_socket: Could not enable receive copy avoidance"); |
| fflush(where); |
| loc_rcvavoid = 0; |
| } |
| } |
| #else |
| /* it wasn't compiled in... */ |
| loc_rcvavoid = 0; |
| #endif /* SO_RCV_COPYAVOID */ |
| |
| #ifdef SO_SND_COPYAVOID |
| if (loc_sndavoid) { |
| if (setsockopt(temp_socket, |
| SOL_SOCKET, |
| SO_SND_COPYAVOID, |
| (const char *)&loc_sndavoid, |
| sizeof(int)) == SOCKET_ERROR) { |
| fprintf(where, |
| "netperf: create_data_socket: Could not enable send copy avoidance"); |
| fflush(where); |
| loc_sndavoid = 0; |
| } |
| } |
| #else |
| /* it was not compiled in... */ |
| loc_sndavoid = 0; |
| #endif |
| |
| /* Now, we will see about setting the TCP_NODELAY flag on the local */ |
| /* socket. We will only do this for those systems that actually */ |
| /* support the option. If it fails, note the fact, but keep going. */ |
| /* If the user tries to enable TCP_NODELAY on a UDP socket, this */ |
| /* will cause an error to be displayed */ |
| |
| /* well..... long ago and far away that would have happened, in |
| particular because we would always use IPPROTO_TCP here. |
| however, now we are using res->ai_protocol, which will be |
| IPPROT_UDP, and while HP-UX, and I suspect no-one else on the |
| planet has a UDP_mumble option that overlaps with TCP_NODELAY, |
| sure as knuth made little green programs, linux has a UDP_CORK |
| option that is defined as a value of 1, which is the same a |
| TCP_NODELAY under Linux. So, when asking for -D and |
| "TCP_NODELAY" under Linux, we are actually setting UDP_CORK |
| instead of getting an error like every other OS on the |
| planet. joy and rupture. this stops a UDP_RR test cold sooo we |
| have to make sure that res->ai_protocol actually makes sense for |
| a _NODELAY setsockopt() or a UDP_RR test on Linux where someone |
| mistakenly sets -D will hang. raj 2005-04-21 */ |
| |
| #if defined(TCP_NODELAY) || defined(SCTP_NODELAY) |
| if ((loc_nodelay) && (res->ai_protocol != IPPROTO_UDP)) { |
| |
| /* strictly speaking, since the if defined above is an OR, we |
| should probably check against TCP_NODELAY being defined here. |
| however, the likelihood of SCTP_NODELAY being defined and |
| TCP_NODELAY _NOT_ being defined is, probably :), epsilon. raj |
| 2005-03-15 */ |
| |
| int option = TCP_NODELAY; |
| |
| /* I suspect that WANT_SCTP would suffice here since that is the |
| only time we would have called getaddrinfo with a hints asking |
| for SCTP, but just in case there is an SCTP implementation out |
| there _without_ SCTP_NODELAY... raj 2005-03-15 */ |
| /* change this to IPPROTO_SCTP rather than WANT_SCTP to better fit |
| with the modus operendi (sp) of the new "omni" tests. raj |
| 2008-02-04 */ |
| #if defined(IPPROTO_SCTP) && defined(SCTP_NODELAY) |
| if (IPPROTO_SCTP == res->ai_protocol) { |
| option = SCTP_NODELAY; |
| } |
| #endif |
| |
| one = 1; |
| if(setsockopt(temp_socket, |
| res->ai_protocol, |
| option, |
| (char *)&one, |
| sizeof(one)) == SOCKET_ERROR) { |
| fprintf(where, |
| "netperf: create_data_socket: nodelay: errno %d\n", |
| errno); |
| fflush(where); |
| } |
| |
| if (debug > 1) { |
| fprintf(where, |
| "netperf: create_data_socket: [TCP|SCTP]_NODELAY requested...\n"); |
| fflush(where); |
| } |
| } |
| #else /* TCP_NODELAY */ |
| |
| loc_nodelay = 0; |
| |
| #endif /* TCP_NODELAY */ |
| |
| #if defined(TCP_CORK) |
| |
| if (loc_tcpcork > 0) { |
| /* the user wishes for us to set TCP_CORK on the socket */ |
| int one = 1; |
| if (setsockopt(temp_socket, |
| getprotobyname("tcp")->p_proto, |
| TCP_CORK, |
| (char *)&one, |
| sizeof(one)) == SOCKET_ERROR) { |
| perror("netperf: create_data_socket: tcp_cork"); |
| exit(1); |
| } |
| if (debug) { |
| fprintf(where,"create_data_socket: tcp_cork...\n"); |
| } |
| } |
| |
| #endif /* TCP_CORK */ |
| |
| /* since some of the UDP tests do not do anything to cause an |
| implicit bind() call, we need to be rather explicit about our |
| bind() call here. even if the address and/or the port are zero |
| (INADDR_ANY etc). raj 2004-07-20 */ |
| |
| if (setsockopt(temp_socket, |
| #ifdef IPPROTO_DCCP |
| /* it is REALLY SILLY THAT THIS SHOULD BE NEEDED!! I |
| should be able to use SOL_SOCKET for this just |
| like TCP and SCTP */ |
| /* IT IS EVEN SILLIER THAT THERE COULD BE SYSTEMS |
| WITH IPPROTO_DCCP and no SOL_DCCP */ |
| #ifndef SOL_DCCP |
| #define SOL_DCCP SOL_SOCKET |
| #define NETPERF_NEED_CLEANUP 1 |
| #endif |
| (res->ai_protocol == IPPROTO_DCCP) ? SOL_DCCP : SOL_SOCKET, |
| #ifdef NETPERF_NEED_CLEANUP |
| #undef SOL_DCCP |
| #undef NETPERF_NEED_CLEANUP |
| #endif |
| |
| #else |
| SOL_SOCKET, |
| #endif |
| SO_REUSEADDR, |
| (const char *)&on, |
| sizeof(on)) < 0) { |
| fprintf(where, |
| "netperf: create_data_socket: SO_REUSEADDR failed %d\n", |
| errno); |
| fflush(where); |
| } |
| |
| if (bind(temp_socket, |
| res->ai_addr, |
| res->ai_addrlen) < 0) { |
| if (debug) { |
| fprintf(where, |
| "netperf: create_data_socket: data socket bind failed errno %d\n", |
| errno); |
| fprintf(where," port: %d\n",get_port_number(res)); |
| fflush(where); |
| } |
| } |
| |
| |
| return(temp_socket); |
| |
| } |
| |
| #ifdef KLUDGE_SOCKET_OPTIONS |
| |
| |
| /* This routine is for those BROKEN systems which do not correctly */ |
| /* pass socket attributes through calls such as accept(). It should */ |
| /* only be called for those broken systems. I *really* don't want to */ |
| /* have this, but even broken systems must be measured. raj 11/95 */ |
| void |
| kludge_socket_options(int temp_socket) |
| { |
| |
| set_sock_buffer(temp_socket, SEND_BUFFER, lss_size_req, &lss_size); |
| set_sock_buffer(temp_socket, RECV_BUFFER, lsr_size_req, &lsr_size); |
| |
| /* now, we may wish to enable the copy avoidance features on the */ |
| /* local system. of course, this may not be possible... */ |
| /* those calls were only valid for HP-UX, and I know that HP-UX is */ |
| /* written correctly, and so we do not need to include those calls */ |
| /* in this kludgy routine. raj 11/95 */ |
| |
| |
| /* Now, we will see about setting the TCP_NODELAY flag on the local */ |
| /* socket. We will only do this for those systems that actually */ |
| /* support the option. If it fails, note the fact, but keep going. */ |
| /* If the user tries to enable TCP_NODELAY on a UDP socket, this */ |
| /* will cause an error to be displayed */ |
| |
| #ifdef TCP_NODELAY |
| if (loc_nodelay) { |
| one = 1; |
| if(setsockopt(temp_socket, |
| getprotobyname("tcp")->p_proto, |
| TCP_NODELAY, |
| (char *)&one, |
| sizeof(one)) == SOCKET_ERROR) { |
| fprintf(where,"netperf: kludge_socket_options: nodelay: errno %d\n", |
| errno); |
| fflush(where); |
| } |
| |
| if (debug > 1) { |
| fprintf(where, |
| "netperf: kludge_socket_options: TCP_NODELAY requested...\n"); |
| fflush(where); |
| } |
| } |
| #else /* TCP_NODELAY */ |
| |
| loc_nodelay = 0; |
| |
| #endif /* TCP_NODELAY */ |
| |
| } |
| |
| #endif /* KLUDGE_SOCKET_OPTIONS */ |
| |
| |
| static void * |
| get_address_address(struct addrinfo *info) |
| { |
| struct sockaddr_in *sin; |
| #if defined(AF_INET6) |
| struct sockaddr_in6 *sin6; |
| #endif |
| |
| switch(info->ai_family) { |
| case AF_INET: |
| sin = (struct sockaddr_in *)info->ai_addr; |
| return(&(sin->sin_addr)); |
| break; |
| #if defined(AF_INET6) |
| case AF_INET6: |
| sin6 = (struct sockaddr_in6 *)info->ai_addr; |
| return(&(sin6->sin6_addr)); |
| break; |
| #endif |
| default: |
| fprintf(stderr,"we never expected to get here in get_address_address\n"); |
| fflush(stderr); |
| exit(-1); |
| } |
| } |
| |
| #if defined(WIN32) |
| #if !defined(InetNtop) |
| /* +*+ Why isn't this in the winsock headers yet? */ |
| const char * |
| inet_ntop(int af, const void *src, char *dst, size_t size); |
| #endif |
| #endif |
| |
| /* This routine is a generic test header printer for the topmost header */ |
| void |
| print_top_test_header(char test_name[], struct addrinfo *source, struct addrinfo *destination) |
| { |
| |
| #if defined(AF_INET6) |
| char address_buf[INET6_ADDRSTRLEN]; |
| #else |
| char address_buf[16]; /* magic constant */ |
| #endif |
| |
| /* we want to have some additional, interesting information in */ |
| /* the headers. we know some of it here, but not all, so we will */ |
| /* only print the test title here and will print the results */ |
| /* titles after the test is finished */ |
| fprintf(where,test_name); |
| address_buf[0] = '\0'; |
| inet_ntop(source->ai_family,get_address_address(source),address_buf,sizeof(address_buf)); |
| fprintf(where, |
| " from %s (%s) port %u %s", |
| source->ai_canonname, |
| address_buf, |
| get_port_number(source), |
| inet_ftos(source->ai_family)); |
| address_buf[0] = '\0'; |
| inet_ntop(destination->ai_family,get_address_address(destination),address_buf,sizeof(address_buf)); |
| fprintf(where, |
| " to %s (%s) port %u %s", |
| destination->ai_canonname, |
| address_buf, |
| get_port_number(destination), |
| inet_ftos(destination->ai_family)); |
| |
| if (iteration_max > 1) { |
| fprintf(where, |
| " : +/-%.3f%% @ %2d%% conf. %s", |
| interval/0.02, |
| confidence_level, |
| result_confidence_only ? " on result only" : ""); |
| } |
| if ((loc_nodelay > 0) || (rem_nodelay > 0)) { |
| fprintf(where," : nodelay"); |
| } |
| if ((loc_sndavoid > 0) || |
| (loc_rcvavoid > 0) || |
| (rem_sndavoid > 0) || |
| (rem_rcvavoid > 0)) { |
| fprintf(where," : copy avoidance"); |
| } |
| |
| if (no_control) { |
| fprintf(where," : no control"); |
| } |
| |
| #ifdef WANT_HISTOGRAM |
| fprintf(where," : histogram"); |
| #endif /* WANT_HISTOGRAM */ |
| |
| #ifdef WANT_INTERVALS |
| #ifndef WANT_SPIN |
| fprintf(where," : interval"); |
| #else |
| fprintf(where," : spin interval"); |
| #endif |
| #endif /* WANT_INTERVALS */ |
| |
| #ifdef DIRTY |
| fprintf(where," : dirty data"); |
| #endif /* DIRTY */ |
| #ifdef WANT_DEMO |
| fprintf(where," : demo"); |
| #endif |
| #ifdef WANT_FIRST_BURST |
| /* a little hokey perhaps, but we really only want this to be |
| emitted for tests where it actually is used, which means a |
| "REQUEST/RESPONSE" test. raj 2005-11-10 */ |
| if (strstr(test_name,"REQUEST/RESPONSE")) { |
| fprintf(where," : first burst %d",first_burst_size); |
| } |
| #endif |
| if (cpu_binding_requested) { |
| fprintf(where," : cpu bind"); |
| } |
| fprintf(where,"\n"); |
| |
| } |
| |
| |
| /* This routine implements the TCP unidirectional data transfer test */ |
| /* (a.k.a. stream) for the sockets interface. It receives its */ |
| /* parameters via global variables from the shell and writes its */ |
| /* output to the standard output. */ |
| |
| |
| void |
| send_tcp_stream(char remote_host[]) |
| { |
| |
| char *tput_title = "\ |
| Recv Send Send \n\ |
| Socket Socket Message Elapsed \n\ |
| Size Size Size Time Throughput \n\ |
| bytes bytes bytes secs. %s/sec \n\n"; |
| |
| char *tput_fmt_0 = |
| "%7.2f %s\n"; |
| |
| char *tput_fmt_1 = |
| "%6d %6d %6d %-6.2f %7.2f %s\n"; |
| |
| char *cpu_title = "\ |
| Recv Send Send Utilization Service Demand\n\ |
| Socket Socket Message Elapsed Send Recv Send Recv\n\ |
| Size Size Size Time Throughput local remote local remote\n\ |
| bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n"; |
| |
| char *cpu_fmt_0 = |
| "%6.3f %c %s\n"; |
| |
| char *cpu_fmt_1 = |
| "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\n"; |
| |
| char *ksink_fmt = "\n\ |
| Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\ |
| Local Remote Local Remote Xfered Per Per\n\ |
| Send Recv Send Recv Send (avg) Recv (avg)\n\ |
| %5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; |
| |
| char *ksink_fmt2 = "\n\ |
| Maximum\n\ |
| Segment\n\ |
| Size (bytes)\n\ |
| %6d\n"; |
| |
| |
| float elapsed_time; |
| |
| /* what we want is to have a buffer space that is at least one */ |
| /* send-size greater than our send window. this will insure that we */ |
| /* are never trying to re-use a buffer that may still be in the hands */ |
| /* of the transport. This buffer will be malloc'd after we have found */ |
| /* the size of the local senc socket buffer. We will want to deal */ |
| /* with alignment and offset concerns as well. */ |
| |
| struct ring_elt *send_ring; |
| |
| int len; |
| unsigned int nummessages = 0; |
| SOCKET send_socket; |
| int bytes_remaining; |
| int tcp_mss = -1; /* possibly uninitialized on printf far below */ |
| |
| /* with links like fddi, one can send > 32 bits worth of bytes */ |
| /* during a test... ;-) at some point, this should probably become a */ |
| /* 64bit integral type, but those are not entirely common yet */ |
| |
| unsigned long long local_bytes_sent = 0; |
| double bytes_sent = 0.0; |
| |
| float local_cpu_utilization; |
| float local_service_demand; |
| float remote_cpu_utilization; |
| float remote_service_demand; |
| |
| double thruput; |
| |
| struct addrinfo *remote_res; |
| struct addrinfo *local_res; |
| |
| struct tcp_stream_request_struct *tcp_stream_request; |
| struct tcp_stream_response_struct *tcp_stream_response; |
| struct tcp_stream_results_struct *tcp_stream_result; |
| |
| tcp_stream_request = |
| (struct tcp_stream_request_struct *)netperf_request.content.test_specific_data; |
| tcp_stream_response = |
| (struct tcp_stream_response_struct *)netperf_response.content.test_specific_data; |
| tcp_stream_result = |
| (struct tcp_stream_results_struct *)netperf_response.content.test_specific_data; |
| |
| #ifdef WANT_HISTOGRAM |
| if (verbosity > 1) { |
| time_hist = HIST_new(); |
| } |
| #endif /* WANT_HISTOGRAM */ |
| /* since we are now disconnected from the code that established the */ |
| /* control socket, and since we want to be able to use different */ |
| /* protocols and such, we are passed the name of the remote host and */ |
| /* must turn that into the test specific addressing information. */ |
| |
| /* complete_addrinfos will either succede or exit the process */ |
| complete_addrinfos(&remote_res, |
| &local_res, |
| remote_host, |
| SOCK_STREAM, |
| IPPROTO_TCP, |
| 0); |
| |
| if ( print_headers ) { |
| print_top_test_header("TCP STREAM TEST",local_res,remote_res); |
| } |
| |
| send_ring = NULL; |
| confidence_iteration = 1; |
| init_stat(); |
| |
| /* we have a great-big while loop which controls the number of times */ |
| /* we run a particular test. this is for the calculation of a */ |
| /* confidence interval (I really should have stayed awake during */ |
| /* probstats :). If the user did not request confidence measurement */ |
| /* (no confidence is the default) then we will only go though the */ |
| /* loop once. the confidence stuff originates from the folks at IBM */ |
| |
| while (((confidence < 0) && (confidence_iteration < iteration_max)) || |
| (confidence_iteration <= iteration_min)) { |
| |
| /* initialize a few counters. we have to remember that we might be */ |
| /* going through the loop more than once. */ |
| |
| nummessages = 0; |
| bytes_sent = 0.0; |
| times_up = 0; |
| |
| /*set up the data socket */ |
| send_socket = create_data_socket(local_res); |
| |
| if (send_socket == INVALID_SOCKET){ |
| perror("netperf: send_tcp_stream: tcp stream data socket"); |
| exit(1); |
| } |
| |
| if (debug) { |
| fprintf(where,"send_tcp_stream: send_socket obtained...\n"); |
| } |
| |
| /* at this point, we have either retrieved the socket buffer sizes, */ |
| /* or have tried to set them, so now, we may want to set the send */ |
| /* size based on that (because the user either did not use a -m */ |
| /* option, or used one with an argument of 0). If the socket buffer */ |
| /* size is not available, we will set the send size to 4KB - no */ |
| /* particular reason, just arbitrary... */ |
| if (send_size == 0) { |
| if (lss_size > 0) { |
| send_size = lss_size; |
| } |
| else { |
| send_size = 4096; |
| } |
| } |
| |
| /* set-up the data buffer ring with the requested alignment and offset. */ |
| /* note also that we have allocated a quantity */ |
| /* of memory that is at least one send-size greater than our socket */ |
| /* buffer size. We want to be sure that there are at least two */ |
| /* buffers allocated - this can be a bit of a problem when the */ |
| /* send_size is bigger than the socket size, so we must check... the */ |
| /* user may have wanted to explicitly set the "width" of our send */ |
| /* buffers, we should respect that wish... */ |
| if (send_width == 0) { |
| send_width = (lss_size/send_size) + 1; |
| if (send_width == 1) send_width++; |
| } |
| |
| if (send_ring == NULL) { |
| /* only allocate the send ring once. this is a networking test, */ |
| /* not a memory allocation test. this way, we do not need a */ |
| /* deallocate_buffer_ring() routine, and I don't feel like */ |
| /* writing one anyway :) raj 11/94 */ |
| send_ring = allocate_buffer_ring(send_width, |
| send_size, |
| local_send_align, |
| local_send_offset); |
| } |
| |
| /* If the user has requested cpu utilization measurements, we must */ |
| /* calibrate the cpu(s). We will perform this task within the tests */ |
| /* themselves. If the user has specified the cpu rate, then */ |
| /* calibrate_local_cpu will return rather quickly as it will have */ |
| /* nothing to do. If local_cpu_rate is zero, then we will go through */ |
| /* all the "normal" calibration stuff and return the rate back. */ |
| |
| if (local_cpu_usage) { |
| local_cpu_rate = calibrate_local_cpu(local_cpu_rate); |
| } |
| |
| if (!no_control) { |
| /* Tell the remote end to do a listen. The server alters the |
| socket paramters on the other side at this point, hence the |
| reason for all the values being passed in the setup |
| message. If the user did not specify any of the parameters, |
| they will be passed as 0, which will indicate to the remote |
| that no changes beyond the system's default should be |
| used. Alignment is the exception, it will default to 1, which |
| will be no alignment alterations. */ |
| |
| netperf_request.content.request_type = DO_TCP_STREAM; |
| tcp_stream_request->send_buf_size = rss_size_req; |
| tcp_stream_request->recv_buf_size = rsr_size_req; |
| tcp_stream_request->receive_size = recv_size; |
| tcp_stream_request->no_delay = rem_nodelay; |
| tcp_stream_request->recv_alignment = remote_recv_align; |
| tcp_stream_request->recv_offset = remote_recv_offset; |
| tcp_stream_request->measure_cpu = remote_cpu_usage; |
| tcp_stream_request->cpu_rate = remote_cpu_rate; |
| if (test_time) { |
| tcp_stream_request->test_length = test_time; |
| } |
| else { |
| tcp_stream_request->test_length = test_bytes; |
| } |
| tcp_stream_request->so_rcvavoid = rem_rcvavoid; |
| tcp_stream_request->so_sndavoid = rem_sndavoid; |
| #ifdef DIRTY |
| tcp_stream_request->dirty_count = rem_dirty_count; |
| tcp_stream_request->clean_count = rem_clean_count; |
| #endif /* DIRTY */ |
| tcp_stream_request->port = atoi(remote_data_port); |
| tcp_stream_request->ipfamily = af_to_nf(remote_res->ai_family); |
| if (debug > 1) { |
| fprintf(where, |
| "netperf: send_tcp_stream: requesting TCP stream test\n"); |
| } |
| |
| send_request(); |
| |
| /* The response from the remote will contain all of the relevant |
| socket parameters for this test type. We will put them back |
| into the variables here so they can be displayed if desired. |
| The remote will have calibrated CPU if necessary, and will |
| have done all the needed set-up we will have calibrated the |
| cpu locally before sending the request, and will grab the |
| counter value right after the connect returns. The remote |
| will grab the counter right after the accept call. This saves |
| the hassle of extra messages being sent for the TCP |
| tests. */ |
| |
| recv_response(); |
| |
| if (!netperf_response.content.serv_errno) { |
| if (debug) |
| fprintf(where,"remote listen done.\n"); |
| rsr_size = tcp_stream_response->recv_buf_size; |
| rss_size = tcp_stream_response->send_buf_size; |
| rem_nodelay = tcp_stream_response->no_delay; |
| remote_cpu_usage= tcp_stream_response->measure_cpu; |
| remote_cpu_rate = tcp_stream_response->cpu_rate; |
| |
| /* we have to make sure that the server port number is in |
| network order */ |
| set_port_number(remote_res, |
| (short)tcp_stream_response->data_port_number); |
| |
| rem_rcvavoid = tcp_stream_response->so_rcvavoid; |
| rem_sndavoid = tcp_stream_response->so_sndavoid; |
| } |
| else { |
| Set_errno(netperf_response.content.serv_errno); |
| fprintf(where, |
| "netperf: remote error %d", |
| netperf_response.content.serv_errno); |
| perror(""); |
| fflush(where); |
| |
| exit(1); |
| } |
| } |
| |
| #ifdef WANT_DEMO |
| DEMO_STREAM_SETUP(lss_size,rsr_size) |
| #endif |
| |
| /*Connect up to the remote port on the data socket */ |
| if (connect(send_socket, |
| remote_res->ai_addr, |
| remote_res->ai_addrlen) == INVALID_SOCKET){ |
| perror("netperf: send_tcp_stream: data socket connect failed"); |
| exit(1); |
| } |
| |
| #ifdef WIN32 |
| /* this is used so the timer thread can close the socket out from */ |
| /* under us, which to date is the easiest/cleanest/least */ |
| /* Windows-specific way I can find to force the winsock calls to */ |
| /* return WSAEINTR with the test is over. anything that will run on */ |
| /* 95 and NT and is closer to what netperf expects from Unix signals */ |
| /* and such would be appreciated raj 1/96 */ |
| win_kludge_socket = send_socket; |
| #endif /* WIN32 */ |
| |
| /* Data Socket set-up is finished. If there were problems, either */ |
| /* the connect would have failed, or the previous response would */ |
| /* have indicated a problem. I failed to see the value of the */ |
| /* extra message after the accept on the remote. If it failed, */ |
| /* we'll see it here. If it didn't, we might as well start pumping */ |
| /* data. */ |
| |
| /* Set-up the test end conditions. For a stream test, they can be */ |
| /* either time or byte-count based. */ |
| |
| if (test_time) { |
| /* The user wanted to end the test after a period of time. */ |
| times_up = 0; |
| bytes_remaining = 0; |
| /* in previous revisions, we had the same code repeated throught */ |
| /* all the test suites. this was unnecessary, and meant more */ |
| /* work for me when I wanted to switch to POSIX signals, so I */ |
| /* have abstracted this out into a routine in netlib.c. if you */ |
| /* are experiencing signal problems, you might want to look */ |
| /* there. raj 11/94 */ |
| start_timer(test_time); |
| } |
| else { |
| /* The tester wanted to send a number of bytes. */ |
| bytes_remaining = test_bytes; |
| times_up = 1; |
| } |
| |
| /* The cpu_start routine will grab the current time and possibly */ |
| /* value of the idle counter for later use in measuring cpu */ |
| /* utilization and/or service demand and thruput. */ |
| |
| cpu_start(local_cpu_usage); |
| |
| /* we only start the interval timer if we are using the |
| timer-timed intervals rather than the sit and spin ones. raj |
| 2006-02-06 */ |
| #if defined(WANT_INTERVALS) |
| INTERVALS_INIT(); |
| #endif /* WANT_INTERVALS */ |
| |
| /* before we start, initialize a few variables */ |
| |
| #ifdef WANT_DEMO |
| if (demo_mode) { |
| HIST_timestamp(demo_one_ptr); |
| } |
| #endif |
| |
| |
| /* We use an "OR" to control test execution. When the test is */ |
| /* controlled by time, the byte count check will always return false. */ |
| /* When the test is controlled by byte count, the time test will */ |
| /* always return false. When the test is finished, the whole */ |
| /* expression will go false and we will stop sending data. */ |
| |
| while ((!times_up) || (bytes_remaining > 0)) { |
| |
| #ifdef DIRTY |
| access_buffer(send_ring->buffer_ptr, |
| send_size, |
| loc_dirty_count, |
| loc_clean_count); |
| #endif /* DIRTY */ |
| |
| #ifdef WANT_HISTOGRAM |
| if (verbosity > 1) { |
| /* timestamp just before we go into send and then again just |
| after we come out raj 8/94 */ |
| /* but lets only do this if there is going to be a histogram |
| displayed */ |
| HIST_timestamp(&time_one); |
| } |
| #endif /* WANT_HISTOGRAM */ |
| |
| if((len=send(send_socket, |
| send_ring->buffer_ptr, |
| send_size, |
| 0)) != send_size) { |
| if ((len >=0) || SOCKET_EINTR(len)) { |
| /* the test was interrupted, must be the end of test */ |
| break; |
| } |
| perror("netperf: data send error"); |
| printf("len was %d\n",len); |
| exit(1); |
| } |
| |
| local_bytes_sent += send_size; |
| |
| #ifdef WANT_HISTOGRAM |
| if (verbosity > 1) { |
| /* timestamp the exit from the send call and update the histogram */ |
| HIST_timestamp(&time_two); |
| HIST_add(time_hist,delta_micro(&time_one,&time_two)); |
| } |
| #endif /* WANT_HISTOGRAM */ |
| |
| #ifdef WANT_DEMO |
| DEMO_STREAM_INTERVAL(send_size) |
| #endif |
| |
| #if defined(WANT_INTERVALS) |
| INTERVALS_WAIT(); |
| #endif /* WANT_INTERVALS */ |
| |
| /* now we want to move our pointer to the next position in the */ |
| /* data buffer...we may also want to wrap back to the "beginning" */ |
| /* of the bufferspace, so we will mod the number of messages sent */ |
| /* by the send width, and use that to calculate the offset to add */ |
| /* to the base pointer. */ |
| nummessages++; |
| send_ring = send_ring->next; |
| if (bytes_remaining) { |
| bytes_remaining -= send_size; |
| } |
| } |
| |
| /* The test is over. Flush the buffers to the remote end. We do a */ |
| /* graceful release to insure that all data has been taken by the */ |
| /* remote. */ |
| |
| /* but first, if the verbosity is greater than 1, find-out what */ |
| /* the TCP maximum segment_size was (if possible) */ |
| if (verbosity > 1) { |
| tcp_mss = -1; |
| get_tcp_info(send_socket,&tcp_mss); |
| } |
| |
| if (shutdown(send_socket,SHUT_WR) == SOCKET_ERROR) { |
| perror("netperf: cannot shutdown tcp stream socket"); |
| exit(1); |
| } |
| |
| /* hang a recv() off the socket to block until the remote has */ |
| /* brought all the data up into the application. it will do a */ |
| /* shutdown to cause a FIN to be sent our way. We will assume that */ |
| /* any exit from the recv() call is good... raj 4/93 */ |
| |
| recv(send_socket, send_ring->buffer_ptr, send_size, 0); |
| |
| /* this call will always give us the elapsed time for the test, and */ |
| /* will also store-away the necessaries for cpu utilization */ |
| |
| cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ |
| /* measured and how */ |
| /* long did we really */ |
| /* run? */ |
| |
| /* we are finished with the socket, so close it to prevent hitting */ |
| /* the limit on maximum open files. */ |
| |
| close(send_socket); |
| |
| if (!no_control) { |
| /* Get the statistics from the remote end. The remote will have |
| calculated service demand and all those interesting |
| things. If it wasn't supposed to care, it will return obvious |
| values. */ |
| |
| recv_response(); |
| if (!netperf_response.content.serv_errno) { |
| if (debug) |
| fprintf(where, |
| "remote reporting results for %.2f seconds\n", |
| tcp_stream_result->elapsed_time); |
| } |
| else { |
| Set_errno(netperf_response.content.serv_errno); |
| fprintf(where, |
| "netperf: remote error %d", |
| netperf_response.content.serv_errno); |
| perror(""); |
| fflush(where); |
| |
| exit(1); |
| } |
| |
| /* We now calculate what our thruput was for the test. In the |
| future, we may want to include a calculation of the thruput |
| measured by the remote, but it should be the case that for a |
| TCP stream test, that the two numbers should be *very* |
| close... We calculate bytes_sent regardless of the way the |
| test length was controlled. If it was time, we needed to, |
| and if it was by bytes, the user may have specified a number |
| of bytes that wasn't a multiple of the send_size, so we |
| really didn't send what he asked for ;-) */ |
| |
| bytes_sent = ntohd(tcp_stream_result->bytes_received); |
| } |
| else { |
| bytes_sent = (double)local_bytes_sent; |
| } |
| |
| thruput = calc_thruput(bytes_sent); |
| |
| if (local_cpu_usage || remote_cpu_usage) { |
| /* We must now do a little math for service demand and cpu */ |
| /* utilization for the system(s) */ |
| /* Of course, some of the information might be bogus because */ |
| /* there was no idle counter in the kernel(s). We need to make */ |
| /* a note of this for the user's benefit...*/ |
| if (local_cpu_usage) { |
| |
| local_cpu_utilization = calc_cpu_util(0.0); |
| local_service_demand = calc_service_demand(bytes_sent, |
| 0.0, |
| 0.0, |
| 0); |
| } |
| else { |
| local_cpu_utilization = (float) -1.0; |
| local_service_demand = (float) -1.0; |
| } |
| |
| if (remote_cpu_usage) { |
| |
| remote_cpu_utilization = tcp_stream_result->cpu_util; |
| remote_service_demand = calc_service_demand(bytes_sent, |
| 0.0, |
| remote_cpu_utilization, |
| tcp_stream_result->num_cpus); |
| } |
| else { |
| remote_cpu_utilization = (float) -1.0; |
| remote_service_demand = (float) -1.0; |
| } |
| } |
| else { |
| /* we were not measuring cpu, for the confidence stuff, we */ |
| /* should make it -1.0 */ |
| local_cpu_utilization = (float) -1.0; |
| local_service_demand = (float) -1.0; |
| remote_cpu_utilization = (float) -1.0; |
| remote_service_demand = (float) -1.0; |
| } |
| |
| /* at this point, we want to calculate the confidence information. */ |
| /* if debugging is on, calculate_confidence will print-out the */ |
| /* parameters we pass it */ |
| |
| calculate_confidence(confidence_iteration, |
| elapsed_time, |
| thruput, |
| local_cpu_utilization, |
| remote_cpu_utilization, |
| local_service_demand, |
| remote_service_demand); |
| |
| |
| confidence_iteration++; |
| } |
| |
| /* at this point, we have finished making all the runs that we */ |
| /* will be making. so, we should extract what the calcuated values */ |
| /* are for all the confidence stuff. we could make the values */ |
| /* global, but that seemed a little messy, and it did not seem worth */ |
| /* all the mucking with header files. so, we create a routine much */ |
| /* like calcualte_confidence, which just returns the mean values. */ |
| /* raj 11/94 */ |
| |
| retrieve_confident_values(&elapsed_time, |
| &thruput, |
| &local_cpu_utilization, |
| &remote_cpu_utilization, |
| &local_service_demand, |
| &remote_service_demand); |
| |
| /* We are now ready to print all the information. If the user */ |
| /* has specified zero-level verbosity, we will just print the */ |
| /* local service demand, or the remote service demand. If the */ |
| /* user has requested verbosity level 1, he will get the basic */ |
| /* "streamperf" numbers. If the user has specified a verbosity */ |
| /* of greater than 1, we will display a veritable plethora of */ |
| /* background information from outside of this block as it it */ |
| /* not cpu_measurement specific... */ |
| |
| if (confidence < 0) { |
| /* we did not hit confidence, but were we asked to look for it? */ |
| if (iteration_max > 1) { |
| display_confidence(); |
| } |
| } |
| |
| if (local_cpu_usage || remote_cpu_usage) { |
| local_cpu_method = format_cpu_method(cpu_method); |
| remote_cpu_method = format_cpu_method(tcp_stream_result->cpu_method); |
| |
| switch (verbosity) { |
| case 0: |
| if (local_cpu_usage) { |
| fprintf(where, |
| cpu_fmt_0, |
| local_service_demand, |
| local_cpu_method, |
| ((print_headers) || |
| (result_brand == NULL)) ? "" : result_brand); |
| } |
| else { |
| fprintf(where, |
| cpu_fmt_0, |
| remote_service_demand, |
| remote_cpu_method, |
| ((print_headers) || |
| (result_brand == NULL)) ? "" : result_brand); |
| } |
| break; |
| case 1: |
| case 2: |
| if (print_headers) { |
| fprintf(where, |
| cpu_title, |
| format_units(), |
| local_cpu_method, |
| remote_cpu_method); |
| } |
| |
| fprintf(where, |
| cpu_fmt_1, /* the format string */ |
| rsr_size, /* remote recvbuf size */ |
| lss_size, /* local sendbuf size */ |
| send_size, /* how large were the sends */ |
| elapsed_time, /* how long was the test */ |
| thruput, /* what was the xfer rate */ |
| local_cpu_utilization, /* local cpu */ |
| remote_cpu_utilization, /* remote cpu */ |
| local_service_demand, /* local service demand */ |
| remote_service_demand, /* remote service demand */ |
| ((print_headers) || |
| (result_brand == NULL)) ? "" : result_brand); |
| break; |
| } |
| } |
| else { |
| /* The tester did not wish to measure service demand. */ |
| |
| switch (verbosity) { |
| case 0: |
| fprintf(where, |
| tput_fmt_0, |
| thruput, |
| ((print_headers) || |
| (result_brand == NULL)) ? "" : result_brand); |
| break; |
| case 1: |
| case 2: |
| if (print_headers) { |
| fprintf(where,tput_title,format_units()); |
| } |
| fprintf(where, |
| tput_fmt_1, /* the format string */ |
| rsr_size, /* remote recvbuf size */ |
| lss_size, /* local sendbuf size */ |
| send_size, /* how large were the sends */ |
| elapsed_time, /* how long did it take */ |
| thruput, /* how fast did it go */ |
| ((print_headers) || |
| (result_brand == NULL)) ? "" : result_brand); |
| break; |
| } |
| } |
| |
| /* it would be a good thing to include information about some of the */ |
| /* other parameters that may have been set for this test, but at the */ |
| /* moment, I do not wish to figure-out all the formatting, so I will */ |
| /* just put this comment here to help remind me that it is something */ |
| /* that should be done at a later time. */ |
| |
| if (verbosity > 1) { |
| /* The user wanted to know it all, so we will give it to him. */ |
| /* This information will include as much as we can find about */ |
| /* TCP statistics, the alignments of the sends and receives */ |
| /* and all that sort of rot... */ |
| |
| /* this stuff needs to be worked-out in the presence of confidence */ |
| /* intervals and multiple iterations of the test... raj 11/94 */ |
| |
| fprintf(where, |
| ksink_fmt, |
| "Bytes", |
| "Bytes", |
| "Bytes", |
| local_send_align, |
| remote_recv_align, |
| local_send_offset, |
| remote_recv_offset, |
| bytes_sent, |
| bytes_sent / (double)nummessages, |
| nummessages, |
| bytes_sent / (double)tcp_stream_result->recv_calls, |
| tcp_stream_result->recv_calls); |
| fprintf(where, |
| ksink_fmt2, |
| tcp_mss); |
| fflush(where); |
| #ifdef WANT_HISTOGRAM |
| fprintf(where,"\n\nHistogram of time spent in send() call.\n"); |
| fflush(where); |
| HIST_report(time_hist); |
| #endif /* WANT_HISTOGRAM */ |
| } |
| |
| } |
| |
| |
| |
| /* This routine implements the netperf-side TCP unidirectional data |
| transfer test (a.k.a. stream) for the sockets interface where the |
| data flow is from the netserver to the netperf. It receives its |
| parameters via global variables from the shell and writes its |
| output to the standard output. */ |
| |
| |
| void |
| send_tcp_maerts(char remote_host[]) |
| { |
| |
| char *tput_title = "\ |
| Recv Send Send \n\ |
| Socket Socket Message Elapsed \n\ |
| Size Size Size Time Throughput \n\ |
| bytes bytes bytes secs. %s/sec \n\n"; |
| |
| char *tput_fmt_0 = |
| "%7.2f %s\n"; |
| |
| char *tput_fmt_1 = |
| "%6d %6d %6d %-6.2f %7.2f %s\n"; |
| |
| char *cpu_title = "\ |
| Recv Send Send Utilization Service Demand\n\ |
| Socket Socket Message Elapsed Recv Send Recv Send\n\ |
| Size Size Size Time Throughput local remote local remote\n\ |
| bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n"; |
| |
| char *cpu_fmt_0 = |
| "%6.3f %c %s\n"; |
| |
| char *cpu_fmt_1 = |
| "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f %s\n"; |
| |
| char *ksink_fmt = "\n\ |
| Alignment Offset %-8.8s %-8.8s Recvs %-8.8s Sends\n\ |
| Local Remote Local Remote Xfered Per Per\n\ |
| Recv Send Recv Send Recv (avg) Send (avg)\n\ |
| %5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; |
| |
| char *ksink_fmt2 = "\n\ |
| Maximum\n\ |
| Segment\n\ |
| Size (bytes)\n\ |
| %6d\n"; |
| |
| |
| float elapsed_time; |
| |
| /* what we want is to have a buffer space that is at least one */ |
| /* recv-size greater than our recv window. this will insure that we */ |
| /* are never trying to re-use a buffer that may still be in the hands */ |
| /* of the transport. This buffer will be malloc'd after we have found */ |
| /* the size of the local senc socket buffer. We will want to deal */ |
| /* with alignment and offset concerns as well. */ |
| |
| struct ring_elt *recv_ring; |
| |
| int len; |
| unsigned int nummessages = 0; |
| SOCKET recv_socket; |
| int bytes_remaining; |
| int tcp_mss = -1; /* possibly uninitialized on printf far below */ |
| |
| /* with links like fddi, one can recv > 32 bits worth of bytes */ |
| /* during a test... ;-) at some point, this should probably become a */ |
| /* 64bit integral type, but those are not entirely common yet */ |
| double bytes_sent = 0.0; |
| unsigned long long local_bytes_recvd = 0; |
| |
| float local_cpu_utilization; |
| float local_service_demand; |
| float remote_cpu_utilization; |
| float remote_service_demand; |
| |
| double thruput; |
| |
| struct addrinfo *remote_res; |
| struct addrinfo *local_res; |
| |
| struct tcp_maerts_request_struct *tcp_maerts_request; |
| struct tcp_maerts_response_struct *tcp_maerts_response; |
| struct tcp_maerts_results_struct *tcp_maerts_result; |
| |
| tcp_maerts_request = |
| (struct tcp_maerts_request_struct *)netperf_request.content.test_specific_data; |
| tcp_maerts_response = |
| (struct tcp_maerts_response_struct *)netperf_response.content.test_specific_data; |
| tcp_maerts_result = |
| (struct tcp_maerts_results_struct *)netperf_response.content.test_specific_data; |
| |
| #ifdef WANT_HISTOGRAM |
| if (verbosity > 1) { |
| time_hist = HIST_new(); |
| } |
| #endif /* WANT_HISTOGRAM */ |
| /* since we are now disconnected from the code that established the */ |
| /* control socket, and since we want to be able to use different */ |
| /* protocols and such, we are passed the name of the remote host and */ |
| /* must turn that into the test specific addressing information. */ |
| |
| complete_addrinfos(&remote_res, |
| &local_res, |
| remote_host, |
| SOCK_STREAM, |
| IPPROTO_TCP, |
| 0); |
| |
| if ( print_headers ) { |
| print_top_test_header("TCP MAERTS TEST",local_res,remote_res); |
| } |
| |
| recv_ring = NULL; |
| confidence_iteration = 1; |
| init_stat(); |
| |
| /* we have a great-big while loop which controls the number of times */ |
| /* we run a particular test. this is for the calculation of a */ |
| /* confidence interval (I really should have stayed awake during */ |
| /* probstats :). If the user did not request confidence measurement */ |
| /* (no confidence is the default) then we will only go though the */ |
| /* loop once. the confidence stuff originates from the folks at IBM */ |
| |
| while (((confidence < 0) && (confidence_iteration < iteration_max)) || |
| (confidence_iteration <= iteration_min)) { |
| |
| /* initialize a few counters. we have to remember that we might be */ |
| /* going through the loop more than once. */ |
| |
| nummessages = 0; |
| bytes_sent = 0.0; |
| times_up = 0; |
| |
| /*set up the data socket */ |
| recv_socket = create_data_socket(local_res); |
| |
| if (recv_socket == INVALID_SOCKET){ |
| perror("netperf: send_tcp_maerts: tcp stream data socket"); |
| exit(1); |
| } |
| |
| if (debug) { |
| fprintf(where,"send_tcp_maerts: recv_socket obtained...\n"); |
| } |
| |
| /* at this point, we have either retrieved the socket buffer sizes, */ |
| /* or have tried to set them, so now, we may want to set the recv */ |
| /* size based on that (because the user either did not use a -m */ |
| /* option, or used one with an argument of 0). If the socket buffer */ |
| /* size is not available, we will set the recv size to 4KB - no */ |
| /* particular reason, just arbitrary... */ |
| if (recv_size == 0) { |
| if (lsr_size > 0) { |
| recv_size = lsr_size; |
| } |
| else { |
| recv_size = 4096; |
| } |
| } |
| |
| /* set-up the data buffer ring with the requested alignment and offset. */ |
| /* note also that we have allocated a quantity */ |
| /* of memory that is at least one recv-size greater than our socket */ |
| /* buffer size. We want to be sure that there are at least two */ |
| /* buffers allocated - this can be a bit of a problem when the */ |
| /* recv_size is bigger than the socket size, so we must check... the */ |
| /* user may have wanted to explicitly set the "width" of our recv */ |
| /* buffers, we should respect that wish... */ |
| if (recv_width == 0) { |
| recv_width = (lsr_size/recv_size) + 1; |
| if (recv_width == 1) recv_width++; |
| } |
| |
| if (recv_ring == NULL) { |
| /* only allocate the recv ring once. this is a networking test, */ |
| /* not a memory allocation test. this way, we do not need a */ |
| /* deallocate_buffer_ring() routine, and I don't feel like */ |
| /* writing one anyway :) raj 11/94 */ |
| recv_ring = allocate_buffer_ring(recv_width, |
| recv_size, |
| local_recv_align, |
| local_recv_offset); |
| } |
| |
| /* If the user has requested cpu utilization measurements, we must */ |
| /* calibrate the cpu(s). We will perform this task within the tests */ |
| /* themselves. If the user has specified the cpu rate, then */ |
| /* calibrate_local_cpu will return rather quickly as it will have */ |
| /* nothing to do. If local_cpu_rate is zero, then we will go through */ |
| /* all the "normal" calibration stuff and return the rate back. */ |
| |
| if (local_cpu_usage) { |
| local_cpu_rate = calibrate_local_cpu(local_cpu_rate); |
| } |
| |
| if (!no_control) { |
| /* Tell the remote end to do a listen. The server alters the |
| socket paramters on the other side at this point, hence the |
| reason for all the values being passed in the setup |
| message. If the user did not specify any of the parameters, |
| they will be passed as 0, which will indicate to the remote |
| that no changes beyond the system's default should be |
| used. Alignment is the exception, it will default to 1, which |
| will be no alignment alterations. */ |
| |
| netperf_request.content.request_type = DO_TCP_MAERTS; |
| tcp_maerts_request->send_buf_size = rss_size_req; |
| tcp_maerts_request->recv_buf_size = rsr_size_req; |
| tcp_maerts_request->send_size = send_size; |
| tcp_maerts_request->no_delay = rem_nodelay; |
| tcp_maerts_request->send_alignment = remote_send_align; |
| tcp_maerts_request->send_offset = remote_send_offset; |
| tcp_maerts_request->measure_cpu = remote_cpu_usage; |
| tcp_maerts_request->cpu_rate = remote_cpu_rate; |
| if (test_time) { |
| tcp_maerts_request->test_length = test_time; |
| } |
| else { |
| tcp_maerts_request->test_length = test_bytes; |
| } |
| tcp_maerts_request->so_rcvavoid = rem_rcvavoid; |
| tcp_maerts_request->so_sndavoid = rem_sndavoid; |
| #ifdef DIRTY |
| tcp_maerts_request->dirty_count = rem_dirty_count; |
| tcp_maerts_request->clean_count = rem_clean_count; |
| #endif /* DIRTY */ |
| tcp_maerts_request->port = atoi(remote_data_port); |
| tcp_maerts_request->ipfamily = af_to_nf(remote_res->ai_family); |
| if (debug > 1) { |
| fprintf(where, |
| "netperf: send_tcp_maerts: requesting TCP maerts test\n"); |
| } |
| |
| send_request(); |
| |
| /* The response from the remote will contain all of the relevant |
| socket parameters for this test type. We will put them back |
| into the variables here so they can be displayed if desired. |
| The remote will have calibrated CPU if necessary, and will |
| have done all the needed set-up we will have calibrated the |
| cpu locally before sending the request, and will grab the |
| counter value right after the connect returns. The remote |
| will grab the counter right after the accept call. This saves |
| the hassle of extra messages being sent for the TCP |
| tests. */ |
| |
| recv_response(); |
| |
| if (!netperf_response.content.serv_errno) { |
| if (debug) |
| fprintf(where,"remote listen done.\n"); |
| rsr_size = tcp_maerts_response->recv_buf_size; |
| rss_size = tcp_maerts_response->send_buf_size; |
| rem_nodelay = tcp_maerts_response->no_delay; |
| remote_cpu_usage= tcp_maerts_response->measure_cpu; |
| remote_cpu_rate = tcp_maerts_response->cpu_rate; |
| send_size = tcp_maerts_response->send_size; |
| |
| /* we have to make sure that the server port number is in |
| network order */ |
| set_port_number(remote_res, |
| (short)tcp_maerts_response->data_port_number); |
| rem_rcvavoid = tcp_maerts_response->so_rcvavoid; |
| rem_sndavoid = tcp_maerts_response->so_sndavoid; |
| } |
| else { |
| Set_errno(netperf_response.content.serv_errno); |
| fprintf(where, |
| "netperf: remote error %d", |
| netperf_response.content.serv_errno); |
| perror(""); |
| fflush(where); |
| |
| exit(1); |
| } |
| } |
| |
| #ifdef WANT_DEMO |
| DEMO_STREAM_SETUP(lsr_size,rss_size) |
| #endif |
| |
| /*Connect up to the remote port on the data socket */ |
| if (connect(recv_socket, |
| remote_res->ai_addr, |
| remote_res->ai_addrlen) == INVALID_SOCKET){ |
| perror("netperf: send_tcp_maerts: data socket connect failed"); |
| exit(1); |
| } |
| |
| #ifdef WIN32 |
| /* this is used so the timer thread can close the socket out from */ |
| /* under us, which to date is the easiest/cleanest/least */ |
| /* Windows-specific way I can find to force the winsock calls to */ |
| /* return WSAEINTR with the test is over. anything that will run on */ |
| /* 95 and NT and is closer to what netperf expects from Unix signals */ |
| /* and such would be appreciated raj 1/96 */ |
| win_kludge_socket = recv_socket; |
| #endif /* WIN32 */ |
| |
| /* Data Socket set-up is finished. If there were problems, either */ |
| /* the connect would have failed, or the previous response would */ |
| /* have indicated a problem. I failed to see the value of the */ |
| /* extra message after the accept on the remote. If it failed, */ |
| /* we'll see it here. If it didn't, we might as well start pumping */ |
| /* data. */ |
| |
| /* Set-up the test end conditions. For a maerts test, they can be */ |
| /* either time or byte-count based. */ |
| |
| if (test_time) { |
| /* The user wanted to end the test after a period of time. */ |
| times_up = 0; |
| bytes_remaining = 0; |
| /* in previous revisions, we had the same code repeated throught */ |
| /* all the test suites. this was unnecessary, and meant more */ |
| /* work for me when I wanted to switch to POSIX signals, so I */ |
| /* have abstracted this out into a routine in netlib.c. if you */ |
| /* are experiencing signal problems, you might want to look */ |
| /* there. raj 11/94 */ |
| if (!no_control) { |
| /* this is a netperf to netserver test, netserver will close |
| to tell us the test is over, so use PAD_TIME to avoid |
| causing the netserver fits. */ |
| start_timer(test_time + PAD_TIME); |
| } |
| else { |
| /* this is a netperf to data source test, no PAD_TIME */ |
| start_timer(test_time); |
| } |
| } |
| else { |
| /* The tester wanted to recv a number of bytes. we don't do that |
| in a TCP_MAERTS test. sorry. raj 2002-06-21 */ |
| printf("netperf: send_tcp_maerts: test must be timed\n"); |
| exit(1); |
| } |
| |
| /* The cpu_start routine will grab the current time and possibly */ |
| /* value of the idle counter for later use in measuring cpu */ |
| /* utilization and/or service demand and thruput. */ |
| |
| cpu_start(local_cpu_usage); |
| |
| #ifdef WANT_INTERVALS |
| INTERVALS_INIT(); |
| #endif /* WANT_INTERVALS */ |
| |
| /* before we start, initialize a few variables */ |
| |
| #ifdef WANT_DEMO |
| if (demo_mode) { |
| HIST_timestamp(demo_one_ptr); |
| } |
| #endif |
| |
| /* the test will continue until we either get a zero-byte recv() |
| on the socket or our failsafe timer expires. most of the time |
| we trust that we get a zero-byte recieve from the socket. raj |
| 2002-06-21 */ |
| |
| #ifdef WANT_HISTOGRAM |
| if (verbosity > 1) { |
| /* timestamp just before we go into recv and then again just |
| after we come out raj 8/94 */ |
| /* but only if we are actually going to display a histogram. raj |
| 2006-02-07 */ |
| HIST_timestamp(&time_one); |
| } |
| #endif /* WANT_HISTOGRAM */ |
| |
| while ((!times_up) && (len=recv(recv_socket, |
| recv_ring->buffer_ptr, |
| recv_size, |
| 0)) > 0 ) { |
| |
| #ifdef WANT_HISTOGRAM |
| if (verbosity > 1) { |
| /* timestamp the exit from the recv call and update the histogram */ |
| HIST_timestamp(&time_two); |
| HIST_add(time_hist,delta_micro(&time_one,&time_two)); |
| } |
| #endif /* WANT_HISTOGRAM */ |
| |
| #ifdef DIRTY |
| access_buffer(recv_ring->buffer_ptr, |
| recv_size, |
| loc_dirty_count, |
| loc_clean_count); |
| #endif /* DIRTY */ |
| |
| #ifdef WANT_DEMO |
| DEMO_STREAM_INTERVAL(len); |
| #endif |
| |
| #ifdef WANT_INTERVALS |
| INTERVALS_WAIT(); |
| #endif /* WANT_INTERVALS */ |
| |
| /* now we want to move our pointer to the next position in the */ |
| /* data buffer...we may also want to wrap back to the "beginning" */ |
| /* of the bufferspace, so we will mod the number of messages sent */ |
| /* by the recv width, and use that to calculate the offset to add */ |
| /* to the base pointer. */ |
| nummessages++; |
| recv_ring = recv_ring->next; |
| if (bytes_remaining) { |
| bytes_remaining -= len; |
| } |
| |
| local_bytes_recvd += len; |
| |
| #ifdef WANT_HISTOGRAM |
| if (verbosity > 1) { |
| /* make sure we timestamp just before we go into recv */ |
| /* raj 2004-06-15 */ |
| HIST_timestamp(&time_one); |
| } |
| #endif /* WANT_HISTOGRAM */ |
| |
| } |
| |
| /* an EINTR is to be expected when this is a no_control test */ |
| if (((len < 0) || SOCKET_EINTR(len)) && (!no_control)) { |
| perror("send_tcp_maerts: data recv error"); |
| printf("len was %d\n",len); |
| exit(1); |
| } |
| |
| /* if we get here, it must mean we had a recv return of 0 before |
| the watchdog timer expired, or the watchdog timer expired and |
| this was a no_control test */ |
| |
| /* The test is over. Flush the buffers to the remote end. We do a |
| graceful release to tell the remote we have all the data. */ |
| |
| /* but first, if the verbosity is greater than 1, find-out what */ |
| /* the TCP maximum segment_size was (if possible) */ |
| if (verbosity > 1) { |
| tcp_mss = -1; |
| get_tcp_info(recv_socket,&tcp_mss); |
| } |
| |
| if (shutdown(recv_socket,SHUT_WR) == SOCKET_ERROR) { |
| perror("netperf: cannot shutdown tcp maerts socket"); |
| exit(1); |
| } |
| |
| stop_timer(); |
| |
| /* this call will always give us the local elapsed time for the |
| test, and will also store-away the necessaries for cpu |
| utilization */ |
| |
| cpu_stop(local_cpu_usage,&elapsed_time); /* was cpu being */ |
| /* measured and how */ |
| /* long did we really */ |
| /* run? */ |
| |
| /* we are finished with the socket, so close it to prevent hitting */ |
| /* the limit on maximum open files. */ |
| |
| close(recv_socket); |
| |
| if (!no_control) { |
| /* Get the statistics from the remote end. The remote will have |
| calculated service demand and all those interesting |
| things. If it wasn't supposed to care, it will return obvious |
| values. */ |
| |
| recv_response(); |
| if (!netperf_response.content.serv_errno) { |
| if (debug) |
| fprintf(where,"remote results obtained\n"); |
| } |
| else { |
| Set_errno(netperf_response.content.serv_errno); |
| fprintf(where, |
| "netperf: remote error %d", |
| netperf_response.content.serv_errno); |
| perror(""); |
| fflush(where); |
| |
| exit(1); |
| } |
| |
| /* We now calculate what our thruput was for the test. In the |
| future, we may want to include a calculation of the thruput |
| measured by the remote, but it should be the case that for a |
| TCP maerts test, that the two numbers should be *very* |
| close... We calculate bytes_sent regardless of the way the |
| test length was controlled. If it was time, we needed to, |
| and if it was by bytes, the user may have specified a number |
| of bytes that wasn't a multiple of the recv_size, so we |
| really didn't recv what he asked for ;-) */ |
| |
| bytes_sent = ntohd(tcp_maerts_result->bytes_sent); |
| } |
| else { |
| bytes_sent = (double)local_bytes_recvd; |
| } |
| |
| |
| thruput = calc_thruput(bytes_sent); |
| |
| if (local_cpu_usage || remote_cpu_usage) { |
| /* We must now do a little math for service demand and cpu */ |
| /* utilization for the system(s) */ |
| /* Of course, some of the information might be bogus because */ |
| /* there was no idle counter in the kernel(s). We need to make */ |
| /* a note of this for the user's benefit...*/ |
| if (local_cpu_usage) { |
| |
| local_cpu_utilization = calc_cpu_util(0.0); |
| local_service_demand = calc_service_demand(bytes_sent, |
| 0.0, |
| 0.0, |
| 0); |
| } |
| else { |
| local_cpu_utilization = (float) -1.0; |
| local_service_demand = (float) -1.0; |
| } |
| |
| if (remote_cpu_usage) { |
| |
| remote_cpu_utilization = tcp_maerts_result->cpu_util; |
| remote_service_demand = calc_service_demand(bytes_sent, |
| 0.0, |
| remote_cpu_utilization, |
| tcp_maerts_result->num_cpus); |
| } |
| else { |
| remote_cpu_utilization = (float) -1.0; |
| remote_service_demand = (float) -1.0; |
| } |
| } |
| else { |
| /* we were not measuring cpu, for the confidence stuff, we */ |
| /* should make it -1.0 */ |
| local_cpu_utilization = (float) -1.0; |
| local_service_demand = (float) -1.0; |
| remote_cpu_utilization = (float) -1.0; |
| remote_service_demand = (float) -1.0; |
| } |
| |
| /* at this point, we want to calculate the confidence information. */ |
| /* if debugging is on, calculate_confidence will print-out the */ |
| /* parameters we pass it */ |
| |
| calculate_confidence(confidence_iteration, |
| elapsed_time, |
| thruput, |
| local_cpu_utilization, |
| remote_cpu_utilization, |
| local_service_demand, |
| remote_service_demand); |
| |
| |
| confidence_iteration++; |
| } |
| |
| /* at this point, we have finished making all the runs that we */ |
| /* will be making. so, we should extract what the calcuated values */ |
| /* are for all the confidence stuff. we could make the values */ |
| /* global, but that seemed a little messy, and it did not seem worth */ |
| /* all the mucking with header files. so, we create a routine much */ |
| /* like calcualte_confidence, which just returns the mean values. */ |
| /* raj 11/94 */ |
| |
| retrieve_confident_values(&elapsed_time, |
| &thruput, |
| &local_cpu_utilization, |
| &remote_cpu_utilization, |
| &local_service_demand, |
| &remote_service_demand); |
| |
| /* We are now ready to print all the information. If the user */ |
| /* has specified zero-level verbosity, we will just print the */ |
| /* local service demand, or the remote service demand. If the */ |
| /* user has requested verbosity level 1, he will get the basic */ |
| /* "streamperf" numbers. If the user has specified a verbosity */ |
| /* of greater than 1, we will display a veritable plethora of */ |
| /* background information from outside of this block as it it */ |
| /* not cpu_measurement specific... */ |
| |
| if (confidence < 0) { |
| /* we did not hit confidence, but were we asked to look for it? */ |
| if (iteration_max > 1) { |
| display_confidence(); |
| } |
| } |
| |
| if (local_cpu_usage || remote_cpu_usage) { |
| local_cpu_method = format_cpu_method(cpu_method); |
| remote_cpu_method = format_cpu_method(tcp_maerts_result->cpu_method); |
| |
| switch (verbosity) { |
| case 0: |
| if (local_cpu_usage) { |
| fprintf(where, |
| cpu_fmt_0, |
| local_service_demand, |
| local_cpu_method, |
| ((print_headers) || |
| (result_brand == NULL)) ? "" : result_brand); |
| } |
| else { |
| fprintf(where, |
| cpu_fmt_0, |
| remote_service_demand, |
| remote_cpu_method, |
| ((print_headers) || |
| (result_brand == NULL)) ? "" : result_brand); |
| } |
| break; |
| case 1: |
| case 2: |
| if (print_headers) { |
| fprintf(where, |
| cpu_title, |
| format_units(), |
| local_cpu_method, |
| remote_cpu_method); |
| } |
| |
| fprintf(where, |
| cpu_fmt_1, /* the format string */ |
| rsr_size, /* remote recvbuf size */ |
| lss_size, /* local sendbuf size */ |
| send_size, /* how large were the recvs */ |
| elapsed_time, /* how long was the test */ |
| thruput, /* what was the xfer rate */ |
| local_cpu_utilization, /* local cpu */ |
| remote_cpu_utilization, /* remote cpu */ |
| local_service_demand, /* local service demand */ |
| remote_service_demand, /* remote service demand */ |
| ((print_headers) || |
| (result_brand == NULL)) ? "" : result_brand); |
| break; |
| } |
| } |
| else { |
| /* The tester did not wish to measure service demand. */ |
| |
| switch (verbosity) { |
| case 0: |
| fprintf(where, |
| tput_fmt_0, |
| thruput, |
| ((print_headers) || |
| (result_brand == NULL)) ? "" : result_brand); |
| break; |
| case 1: |
| case 2: |
| if (print_headers) { |
| fprintf(where,tput_title,format_units()); |
| } |
| fprintf(where, |
| tput_fmt_1, /* the format string */ |
| lsr_size, /* local recvbuf size */ |
| rss_size, /* remot sendbuf size */ |
| send_size, /* how large were the recvs */ |
| elapsed_time, /* how long did it take */ |
| thruput, /* how fast did it go */ |
| ((print_headers) || |
| (result_brand == NULL)) ? "" : result_brand); |
| break; |
| } |
| } |
| |
| /* it would be a good thing to include information about some of the */ |
| /* other parameters that may have been set for this test, but at the */ |
| /* moment, I do not wish to figure-out all the formatting, so I will */ |
| /* just put this comment here to help remind me that it is something */ |
| /* that should be done at a later time. */ |
| |
| if (verbosity > 1) { |
| /* The user wanted to know it all, so we will give it to him. */ |
| /* This information will include as much as we can find about */ |
| /* TCP statistics, the alignments of the sends and receives */ |
| /* and all that sort of rot... */ |
| |
| /* this stuff needs to be worked-out in the presence of confidence */ |
| /* intervals and multiple iterations of the test... raj 11/94 */ |
| |
| fprintf(where, |
| ksink_fmt, |
| "Bytes", |
| "Bytes", |
| "Bytes", |
| local_recv_align, |
| remote_recv_align, |
| local_recv_offset, |
| remote_recv_offset, |
| bytes_sent, |
| bytes_sent / (double)nummessages, |
| nummessages, |
| bytes_sent / (double)tcp_maerts_result->send_calls, |
| tcp_maerts_result->send_calls); |
| fprintf(where, |
| ksink_fmt2, |
| tcp_mss); |
| fflush(where); |
| #ifdef WANT_HISTOGRAM |
| fprintf(where,"\n\nHistogram of time spent in recv() call.\n"); |
| fflush(where); |
| HIST_report(time_hist); |
| #endif /* WANT_HISTOGRAM */ |
| } |
| |
| } |
| |
| |
| |
| /* this routine implements the TCP_MSS test. All it does is pretend |
| to be a TCP_STREAM test and report the TCP_MSS for the data |
| connection. No actual data is transferred. raj 2007-11-07 |
| */ |
| void |
| send_tcp_mss(char remote_host[]) |
| { |
| |
| char *mss_title = "\ |
| Maximum\n\ |
| Segment\n\ |
| Size (bytes)\n\n"; |
| |
| char *mss_fmt_0 = |
| "%d %s\n"; |
| |
| SOCKET send_socket; |
| int tcp_mss = -1; /* possibly uninitialized on printf far below */ |
| |
| struct addrinfo *remote_res; |
| struct addrinfo *local_res; |
| |
| struct tcp_stream_request_struct *tcp_stream_request; |
| struct tcp_stream_response_struct *tcp_stream_response; |
| struct tcp_stream_results_struct *tcp_stream_result; |
| |
| tcp_stream_request = |
| (struct tcp_stream_request_struct *)netperf_request.content.test_specific_data; |
| tcp_stream_response = |
| (struct tcp_stream_response_struct *)netperf_response.content.test_specific_data; |
| tcp_stream_result = |
| (struct tcp_stream_results_struct *)netperf_response.content.test_specific_data; |
| |
| /* since we are now disconnected from the code that established the */ |
| /* control socket, and since we want to be able to use different */ |
| /* protocols and such, we are passed the name of the remote host and */ |
| /* must turn that into the test specific addressing information. */ |
| |
| /* complete_addrinfos will either succede or exit the process */ |
| complete_addrinfos(&remote_res, |
| &local_res, |
| remote_host, |
| SOCK_STREAM, |
| IPPROTO_TCP, |
| 0); |
| |
| if ( print_headers ) { |
| print_top_test_header("TCP MSS TEST",local_res,remote_res); |
| } |
| |
| /*set up the data socket */ |
| send_socket = create_data_socket(local_res); |
| |
| if (send_socket == INVALID_SOCKET){ |
| perror("netperf: send_tcp_stream: tcp stream data socket"); |
| exit(1); |
| } |
| |
| if (debug) { |
| fprintf(where,"send_tcp_stream: send_socket obtained...\n"); |
| } |
| |
| |
| if (!no_control) { |
| /* Tell the remote end to do a listen. The server alters the |
| socket paramters on the other side at this point, hence the |
| reason for all the values being passed in the setup |
| message. If the user did not specify any of the parameters, |
| they will be passed as 0, which will indicate to the remote |
| that no changes beyond the system's default should be |
| used. Alignment is the exception, it will default to 1, which |
| will be no alignment alterations. */ |
| |
| netperf_request.content.request_type = DO_TCP_STREAM; |
| tcp_stream_request->send_buf_size = rss_size_req; |
| tcp_stream_request->recv_buf_size = rsr_size_req; |
| tcp_stream_request->receive_size = recv_size; |
| tcp_stream_request->no_delay = rem_nodelay; |
| tcp_stream_request->recv_alignment = remote_recv_align; |
| tcp_stream_request->recv_offset = remote_recv_offset; |
| tcp_stream_request->measure_cpu = remote_cpu_usage; |
| tcp_stream_request->cpu_rate = remote_cpu_rate; |
| if (test_time) { |
| tcp_stream_request->test_length = test_time; |
| } |
| else { |
| tcp_stream_request->test_length = test_bytes; |
| } |
| tcp_stream_request->so_rcvavoid = rem_rcvavoid; |
| tcp_stream_request->so_sndavoid = rem_sndavoid; |
| #ifdef DIRTY |
| tcp_stream_request->dirty_count = rem_dirty_count; |
| tcp_stream_request->clean_count = rem_clean_count; |
| #endif /* DIRTY */ |
| tcp_stream_request->port = atoi(remote_data_port); |
| tcp_stream_request->ipfamily = af_to_nf(remote_res->ai_family); |
| if (debug > 1) { |
| fprintf(where, |
| "netperf: send_tcp_mss: requesting TCP stream test\n"); |
| } |
| |
| send_request(); |
| |
| /* The response from the remote will contain all of the relevant |
| socket parameters for this test type. We will put them back |
| into the variables here so they can be displayed if desired. |
| The remote will have calibrated CPU if necessary, and will |
| have done all the needed set-up we will have calibrated the |
| cpu locally before sending the request, and will grab the |
| counter value right after the connect returns. The remote |
| will grab the counter right after the accept call. This saves |
| the hassle of extra messages being sent for the TCP |
| tests. */ |
| |
| recv_response(); |
| |
| if (!netperf_response.content.serv_errno) { |
| if (debug) |
| fprintf(where,"remote listen done.\n"); |
| rsr_size = tcp_stream_response->recv_buf_size; |
| rss_size = tcp_stream_response->send_buf_size; |
| rem_nodelay = tcp_stream_response->no_delay; |
| remote_cpu_usage= tcp_stream_response->measure_cpu; |
| remote_cpu_rate = tcp_stream_response->cpu_rate; |
| |
| /* we have to make sure that the server port number is in |
| network order */ |
| set_port_number(remote_res, |
| (short)tcp_stream_response->data_port_number); |
| |
| rem_rcvavoid = tcp_stream_response->so_rcvavoid; |
| rem_sndavoid = tcp_stream_response->so_sndavoid; |
| } |
| else { |
| Set_errno(netperf_response.content.serv_errno); |
| fprintf(where, |
| "netperf: remote error %d", |
| netperf_response.content.serv_errno); |
| perror(""); |
| fflush(where); |
| |
| exit(1); |
| } |
| } |
| |
| /*Connect up to the remote port on the data socket */ |
| if (connect(send_socket, |
| remote_res->ai_addr, |
| remote_res->ai_addrlen) == INVALID_SOCKET){ |
| perror("netperf: send_tcp_mss: data socket connect failed"); |
| exit(1); |
| } |
| |
| |
| /* find-out what the TCP maximum segment_size was (if possible) */ |
| tcp_mss = -1; |
| get_tcp_info(send_socket,&tcp_mss); |
| |
| /* just go ahead and close the socket, the remote should figure it |
| out */ |
| close(send_socket); |
| |
| /* statistics? we don't need no stinking statistics */ |
| |
| |
| switch (verbosity) { |
| case 0: |
| fprintf(where, |
| mss_fmt_0, |
| tcp_mss, |
| ((print_headers) || |
| (result_brand == NULL)) ? "" : result_brand); |
| break; |
| case 1: |
| case 2: |
| if (print_headers) { |
| fprintf(where,mss_title); |
| } |
| fprintf(where, |
| mss_fmt_0, /* the format string */ |
| tcp_mss, |
| ((print_headers) || |
| (result_brand == NULL)) ? "" : result_brand); |
| break; |
| } |
| |
| |
| } |
| |
| |
| |
| #ifdef HAVE_ICSC_EXS |
| |
| #include <sys/exs.h> |
| |
| |
| /* This routine implements the TCP unidirectional data transfer test */ |
| /* (a.k.a. stream) for the sockets interface. It receives its */ |
| /* parameters via global variables from the shell and writes its */ |
| /* output to the standard output. */ |
| |
| void |
| send_exs_tcp_stream(char remote_host[]) |
| { |
| |
| char *tput_title = "\ |
| Recv Send Send \n\ |
| Socket Socket Message Elapsed \n\ |
| Size Size Size Time Throughput \n\ |
| bytes bytes bytes secs. %s/sec \n\n"; |
| |
| char *tput_fmt_0 = |
| "%7.2f\n"; |
| |
| char *tput_fmt_1 = |
| "%6d %6d %6d %-6.2f %7.2f \n"; |
| |
| char *cpu_title = "\ |
| Recv Send Send Utilization Service Demand\n\ |
| Socket Socket Message Elapsed Send Recv Send Recv\n\ |
| Size Size Size Time Throughput local remote local remote\n\ |
| bytes bytes bytes secs. %-8.8s/s %% %c %% %c us/KB us/KB\n\n"; |
| |
| char *cpu_fmt_0 = |
| "%6.3f %c\n"; |
| |
| char *cpu_fmt_1 = |
| "%6d %6d %6d %-6.2f %7.2f %-6.2f %-6.2f %-6.3f %-6.3f\n"; |
| |
| char *ksink_fmt = "\n\ |
| Alignment Offset %-8.8s %-8.8s Sends %-8.8s Recvs\n\ |
| Local Remote Local Remote Xfered Per Per\n\ |
| Send Recv Send Recv Send (avg) Recv (avg)\n\ |
| %5d %5d %5d %5d %6.4g %6.2f %6d %6.2f %6d\n"; |
| |
| char *ksink_fmt2 = "\n\ |
| Maximum\n\ |
| Segment\n\ |
| Size (bytes)\n\ |
| %6d\n"; |
| |
| |
| float elapsed_time; |
| |
| /* what we want is to have a buffer space that is at least one */ |
| /* send-size greater than our send window. this will insure that we */ |
| /* are never trying to re-use a buffer that may still be in the hands */ |
| /* of the transport. This buffer will be malloc'd after we have found */ |
| /* the size of the local senc socket buffer. We will want to deal */ |
| /* with alignment and offset concerns as well. */ |
| |
| struct ring_elt *send_ring; |
| |
| int len; |
| unsigned int nummessages = 0; |
| SOCKET send_socket; |
| int bytes_remaining; |
| int tcp_mss = -1; /* possibly uninitialized on printf far below */ |
| |
| exs_mhandle_t exs_mhandle; |
| exs_qhandle_t exs_qhandle; |
| #define NETPERF_EXS_PENDING 16 |
| int exs_aio_pending; |
| int exs_aio_eagain; |
| int exs_aio_dequeued; |
| int exs_aio_dequeuecnt; |
| int exs_evtcnt; |
| #define NETPERF_EXS_QSIZE 128 |
| exs_event_t exs_evtvec[NETPERF_EXS_QSIZE]; |
| |
| /* with links like fddi, one can send > 32 bits worth of bytes */ |
| /* during a test... ;-) at some point, this should probably become a */ |
| /* 64bit integral type, but those are not entirely common yet */ |
| |
| double bytes_sent = 0.0; |
| |
| float local_cpu_utilization; |
| float local_service_demand; |
| float remote_cpu_utilization; |
| float remote_service_demand; |
| |
| double thruput; |
| |
| struct addrinfo *remote_res; |
| struct addrinfo *local_res; |
| |
| struct tcp_stream_request_struct *tcp_stream_request; |
| struct tcp_stream_response_struct *tcp_stream_response; |
| struct tcp_stream_results_struct *tcp_stream_result; |
| |
| tcp_stream_request = |
| (struct tcp_stream_request_struct *)netperf_request.content.test_specific_data; |
| tcp_stream_response = |
| (struct tcp_stream_response_struct *)netperf_response.content.test_specific_data; |
| tcp_stream_result = |
| (struct tcp_stream_results_struct *)netperf_response.content.test_specific_data; |
| |
| #if 0 /* def WANT_HISTOGRAM */ |
| time_hist = HIST_new(); |
| #endif /* WANT_HISTOGRAM */ |
| /* since we are now disconnected from the code that established the */ |
| /* control socket, and since we want to be able to use different */ |
| /* protocols and such, we are passed the name of the remote host and */ |
| /* must turn that into the test specific addressing information. */ |
| |
| /* complete_addrinfos will either succede or exit the process */ |
| complete_addrinfos(&remote_res, |
| &local_res, |
| remote_host, |
| SOCK_STREAM, |
| IPPROTO_TCP, |
| 0); |
| |
| if ( print_headers ) { |
| print_top_test_header("EXS TCP STREAM TEST",local_res,remote_res); |
| } |
| |
| send_ring = NULL; |
| confidence_iteration = 1; |
| init_stat(); |
| |
| /* initialize EXS API and create event queue */ |
| if (exs_init (EXS_VERSION) == -1) { |
| perror ("netperf: send_exs_tcp_stream: exs_init failed"); |
| exit (1); |
| } |
| |
| if ((exs_qhandle = exs_qcreate (NETPERF_EXS_QSIZE)) == EXS_QHANDLE_INVALID) { |
| perror ("netperf: send_exs_tcp_stream: exs_qcreate failed"); |
| exit (1); |
| } |
| if (debug) { |
| fprintf (where, "send_exs_tcp_stream: qhandle=%d\n", exs_qhandle); |
| } |
| |
| /* we have a great-big while loop which controls the number of times */ |
| /* we run a particular test. this is for the calculation of a */ |
| /* confidence interval (I really should have stayed awake during */ |
| /* probstats :). If the user did not request confidence measurement */ |
| /* (no confidence is the default) then we will only go though the */ |
| /* loop once. the confidence stuff originates from the folks at IBM */ |
| |
| while (((confidence < 0) && (confidence_iteration < iteration_max)) || |
| (confidence_iteration <= iteration_min)) { |
| |
| /* initialize a few counters. we have to remember that we might be */ |
| /* going through the loop more than once. */ |
| |
| nummessages = 0; |
| bytes_sent = 0.0; |
| times_up = 0; |
| |
| /*set up the data socket */ |
| send_socket = create_data_socket(local_res); |
| |
| if (send_socket == INVALID_SOCKET){ |
| perror("netperf: send_tcp_stream: tcp stream data socket"); |
| exit(1); |
| } |
| |
| if (debug) { |
| fprintf(where,"send_tcp_stream: send_socket obtained...\n"); |
| } |
| |
| /* at this point, we have either retrieved the socket buffer sizes, */ |
| /* or have tried to set them, so now, we may want to set the send */ |
| /* size based on that (because the user either did not use a -m */ |
| /* option, or used one with an argument of 0). If the socket buffer */ |
| /* size is not available, we will set the send size to 4KB - no */ |
| /* particular reason, just arbitrary... */ |
| if (send_size == 0) { |
| if (lss_size > 0) { |
| send_size = lss_size; |
| } |
| else { |
| send_size = 4096; |
| } |
| } |
| |
| /* set-up the data buffer ring with the requested alignment and offset. */ |
| /* note also that we have allocated a quantity */ |
| /* of memory that is at least one send-size greater than our socket */ |
| /* buffer size. We want to be sure that there are at least two */ |
| /* buffers allocated - this can be a bit of a problem when the */ |
| /* send_size is bigger than the socket size, so we must check... the */ |
| /* user may have wanted to explicitly set the "width" of our send */ |
|