int main(int argc, char** argv) { int c; int sockfd, newsockfd; int federated = 0, debug = 0, max_ttl = 10; time_t tt; socklen_t clilen; char* srcname = NULL; char* listenport = LISTEN_PORT; int conn_options = 0; char *ErLogFileName = BASEDIR "/"ERLOGFILE; char *AcLogFileName = BASEDIR "/"ACLOGFILE; struct sockaddr_storage cli_addr; I2Addr listenaddr = NULL; Allowed* ptr; #ifdef AF_INET6 #define GETOPT_LONG_INET6(x) "46"x #else #define GETOPT_LONG_INET6(x) x #endif while ((c = getopt_long(argc, argv, GETOPT_LONG_INET6("dhl:e:p:t:Ff:b:sS:v"), long_options, 0)) != -1) { switch (c) { case '4': conn_options |= OPT_IPV4_ONLY; break; case '6': conn_options |= OPT_IPV6_ONLY; break; case 'd': debug++; break; case 'h': www_long_usage("ANL/Internet2 NDT version " VERSION " (fakewww)"); break; case 'v': printf("ANL/Internet2 NDT version %s (fakewww)\n", VERSION); exit(0); break; case 'l': AcLogFileName = optarg; break; case 'e': ErLogFileName = optarg; break; case 'p': listenport = optarg; break; case 't': max_ttl = atoi(optarg); break; case 'F': federated = 1; break; case 'f': ptr = malloc(sizeof(Allowed)); ptr->filename = optarg; ptr->next = a_root; a_root = ptr; break; case 'b': basedir = optarg; break; case 's': usesyslog = 1; break; case 'S': SysLogFacility = optarg; break; case 301: DefaultTree = optarg; break; #ifdef AF_INET6 case 302: DefaultTree6 = optarg; break; #endif case '?': short_usage(argv[0], ""); break; } } if (optind < argc) { short_usage(argv[0], "Unrecognized non-option elements"); } log_init(argv[0], debug); if (SysLogFacility != NULL) { int i = 0; while (facilitynames[i].c_name) { if (strcmp(facilitynames[i].c_name, SysLogFacility) == 0) { syslogfacility = facilitynames[i].c_val; break; } ++i; } if (facilitynames[i].c_name == NULL) { log_println( 0, "Warning: Unknown syslog facility [%s] --> using default (%d)", SysLogFacility, syslogfacility); SysLogFacility = NULL; } } if (DefaultTree == NULL) { snprintf(dtfn, sizeof(dtfn), "%s/%s", BASEDIR, DFLT_TREE); DefaultTree = dtfn; } #ifdef AF_INET6 if (DefaultTree6 == NULL) { snprintf(dt6fn, sizeof(dtfn), "%s/%s", BASEDIR, DFLT_TREE6); DefaultTree6 = dt6fn; } #endif /* * Bind our local address so that the client can send to us. */ if (srcname && !(listenaddr = I2AddrByNode(get_errhandle(), srcname))) { err_sys("server: Invalid source address specified"); } if ((listenaddr = CreateListenSocket(listenaddr, listenport, conn_options, 0)) == NULL) { err_sys("server: CreateListenSocket failed"); } sockfd = I2AddrFD(listenaddr); tt = time(0); log_println(1, "%15.15s fakewww server started (NDT version %s)", ctime(&tt) + 4, VERSION); log_println(1, "\tport = %d", I2AddrPort(listenaddr)); log_println(1, "\tfederated mode = %s", (federated == 1) ? "on" : "off"); log_println(1, "\taccess log = %s\n\terror log = %s", AcLogFileName, ErLogFileName); log_println(1, "\tbasedir = %s", basedir); if (usesyslog) { log_println(1, "\tsyslog facility = %s (%d)", SysLogFacility ? SysLogFacility : "default", syslogfacility); } log_println(1, "\tdebug level set to %d", debug); logErLog(ErLogFileName, &tt, "notice", "fakewww server started (NDT version %s)", VERSION); logErLog(ErLogFileName, &tt, "notice", "\tport = %d", I2AddrPort(listenaddr)); logErLog(ErLogFileName, &tt, "notice", "\tfederated mode = %s", (federated == 1) ? "on" : "off"); logErLog(ErLogFileName, &tt, "notice", "\taccess log = %s", AcLogFileName); logErLog(ErLogFileName, &tt, "notice", "\terror log = %s", ErLogFileName); logErLog(ErLogFileName, &tt, "notice", "\tbasedir = %s", basedir); if (usesyslog) { logErLog(ErLogFileName, &tt, "notice", "\tsyslog facility = %s (%d)", SysLogFacility ? SysLogFacility : "default", syslogfacility); } logErLog(ErLogFileName, &tt, "notice", "\tdebug level set to %d", debug); if (usesyslog == 1) syslog(LOG_FACILITY | LOG_INFO, "Fakewww (ver %s) process started", VERSION); signal(SIGCHLD, reap); /* get rid of zombies */ /* * Wait for a connection from a client process. * This is an example of a concurrent server. */ for (;;) { clilen = sizeof(cli_addr); newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); if (newsockfd < 0) { if (errno == EINTR) continue; /*sig child */ err_sys("Fakewww server: accept error"); } if (fork() == 0) { /* child */ I2Addr caddr = I2AddrBySAddr(get_errhandle(), (struct sockaddr *) &cli_addr, clilen, 0, 0); alarm(300); /* kill child off after 5 minutes, should never happen */ close(sockfd); dowww(newsockfd, caddr, listenport, AcLogFileName, ErLogFileName, federated, max_ttl); exit(0); } close(newsockfd); } }
int test_mid_clt(int ctlSocket, char tests, char* host, int conn_options, int buf_size, char* tmpstr2) { char buff[BUFFSIZE+1]; int msgLen, msgType; int midport = 3003; I2Addr sec_addr = NULL; int ret, one=1, i, inlth; int in2Socket; double t, spdin; uint32_t bytes; struct timeval sel_tv; fd_set rfd; if (tests & TEST_MID) { log_println(1, " <-- Middlebox test -->"); msgLen = sizeof(buff); if (recv_msg(ctlSocket, &msgType, buff, &msgLen)) { log_println(0, "Protocol error - missed prepare message!"); return 1; } if (check_msg_type("Middlebox test", TEST_PREPARE, msgType, buff, msgLen)) { return 2; } if (msgLen <= 0) { log_println(0, "Improper message"); return 3; } buff[msgLen] = 0; if (check_int(buff, &midport)) { log_println(0, "Invalid port number"); return 4; } log_println(1, " -- port: %d", midport); if ((sec_addr = I2AddrByNode(get_errhandle(), host)) == NULL) { log_println(0, "Unable to resolve server address: %s", strerror(errno)); return -3; } I2AddrSetPort(sec_addr, midport); if (get_debuglvl() > 4) { char tmpbuff[200]; size_t tmpBufLen = 199; memset(tmpbuff, 0, 200); I2AddrNodeName(sec_addr, tmpbuff, &tmpBufLen); log_println(5, "connecting to %s:%d", tmpbuff, I2AddrPort(sec_addr)); } if ((ret = CreateConnectSocket(&in2Socket, NULL, sec_addr, conn_options, buf_size))) { log_println(0, "Connect() for middlebox failed: %s", strerror(errno)); return -10; } printf("Checking for Middleboxes . . . . . . . . . . . . . . . . . . "); fflush(stdout); tmpstr2[0] = '\0'; i = 0; bytes = 0; t = secs() + 5.0; sel_tv.tv_sec = 6; sel_tv.tv_usec = 5; FD_ZERO(&rfd); FD_SET(in2Socket, &rfd); for (;;) { if (secs() > t) break; ret = select(in2Socket+1, &rfd, NULL, NULL, &sel_tv); if (ret > 0) { inlth = read(in2Socket, buff, sizeof(buff)); if (inlth == 0) break; bytes += inlth; continue; } if (ret < 0) { printf("nothing to read, exiting read loop\n"); break; } if (ret == 0) { printf("timer expired, exiting read loop\n"); break; } } t = secs() - t + 5.0; spdin = ((8.0 * bytes) / 1000) / t; msgLen = sizeof(buff); if (recv_msg(ctlSocket, &msgType, buff, &msgLen)) { log_println(0, "Protocol error - missed text message!"); return 1; } if (check_msg_type("Middlebox test results", TEST_MSG, msgType, buff, msgLen)) { return 2; } strncat(tmpstr2, buff, msgLen); memset(buff, 0, 128); sprintf(buff, "%0.0f", spdin); log_println(4, "CWND limited speed = %0.2f kbps", spdin); send_msg(ctlSocket, TEST_MSG, buff, strlen(buff)); printf("Done\n"); I2AddrFree(sec_addr); msgLen = sizeof(buff); if (recv_msg(ctlSocket, &msgType, buff, &msgLen)) { log_println(0, "Protocol error - missed finalize message!"); return 1; } if (check_msg_type("Middlebox test", TEST_FINALIZE, msgType, buff, msgLen)) { return 2; } log_println(1, " <-------------------->"); } return 0; }
static int OpenSocket(I2Addr addr, char* serv, int family, int options) { int fd = -1; int return_code = 0; struct addrinfo *fai = NULL; if (!(fai = I2AddrAddrInfo(addr, NULL, serv))) { return -2; } // Attempt to connect to one of the chosen addresses. struct addrinfo* ai = NULL; for (ai = fai; ai; ai = ai->ai_next) { if (ai->ai_family != family) continue; // create socket with obtained address domain, socket type and protocol fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); // socket create failed. Abandon further activities using this socket if (fd < 0) continue; // allow sockets to reuse local address while binding unless there // is an active listener. If unable to set this option, indicate failure int on = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) { return_code = -2; goto failsock; } // end trying to set socket option to reuse local address #ifdef AF_INET6 #ifdef IPV6_V6ONLY if (family == AF_INET6 && (options & OPT_IPV6_ONLY)) { on = 1; // the IPv6 version socket option setup if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) != 0) { return_code = -2; goto failsock; } } #endif #endif // try to bind to address if (bind(fd, ai->ai_addr, ai->ai_addrlen) == 0) { // successful // set values in "addr" structure if (!I2AddrSetSAddr(addr, ai->ai_addr, ai->ai_addrlen) || !I2AddrSetProtocol(addr, ai->ai_protocol) || !I2AddrSetSocktype(addr, ai->ai_socktype)) { log_println(1, "OpenSocket: Unable to set saddr in address record"); return_code = -1; goto failsock; } // set port if not already done, else return -1 if (!I2AddrPort(addr)) { struct sockaddr_storage tmp_addr; socklen_t tmp_addr_len = sizeof(tmp_addr); I2Addr tmpAddr; if (getsockname(fd, (struct sockaddr*) &tmp_addr, &tmp_addr_len)) { log_println(1, "OpenSocket: Unable to getsockname in address record"); return_code = -1; goto failsock; } tmpAddr = I2AddrBySAddr( get_errhandle(), (struct sockaddr*) &tmp_addr, tmp_addr_len, 0, 0); I2AddrSetPort(addr, I2AddrPort(tmpAddr)); I2AddrFree(tmpAddr); } // save socket file descriptor if (!I2AddrSetFD(addr, fd, True)) { log_println(1, "OpenSocket: Unable to set file descriptor in address " "record"); return_code = -1; goto failsock; } // end setting values in "addr" structure break; } // Address is indicated as being in use. Display actual socket options to // user and return if (errno == EADDRINUSE) { /* RAC debug statemement 10/11/06 */ socklen_t onSize = sizeof(on); getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, &onSize); log_println(1, "bind(%d) failed: Address already in use given as the " "reason, getsockopt() returned %d", fd, on); } else { log_println(1, "bind(%d) failed: %s", fd, strerror(errno)); } return_code = -1; goto failsock; } return fd; // If opening socket failed, print error, and try to close socket // file-descriptor. failsock: while ((close(fd) < 0) && (errno == EINTR)) { } return return_code; }
/** * Perform the C2S Throughput test. This test intends to measure throughput * from the client to the server by performing a 10 seconds memory-to-memory * data transfer. * * Protocol messages are exchanged between the Client and the Server using the * same connection and message format as the NDTP-Control protocol.Throughput * packets are sent on the new connection and do not follow the NDTP-Control * protocol message format. * * When the Client stops streaming the test data (or the server test routine * times out), the Server sends the Client its calculated throughput value. * * @param ctl Client control Connection * @param agent Web100 agent used to track the connection * @param testOptions Test options * @param conn_options connection options * @param c2sspd In-out parameter to store C2S throughput value * @param set_buff enable setting TCP send/recv buffer size to be used (seems * unused in file) * @param window value of TCP send/rcv buffer size intended to be used. * @param autotune autotuning option. Deprecated. * @param device string device name inout parameter * @param options Test Option variables * @param record_reverse integer indicating whether receiver-side statistics * have to be logged * @param count_vars count of web100 variables * @param spds[] [] speed check array * @param spd_index index used for speed check array * @param conn_options Connection options * @param ctx The SSL context (possibly NULL) * @param c2s_ThroughputSnapshots Variable used to set c2s throughput snapshots * @param extended indicates if extended c2s test should be performed * @return 0 on success, an error code otherwise * Error codes: * -1 - Listener socket creation failed * -100 - Timeout while waiting for client to connect to server's * ephemeral port * -101 - Retries exceeded while waiting for client to connect * -102 - Retries exceeded while waiting for data from connected * client * -errno - Other specific socket error numbers */ int test_c2s(Connection *ctl, tcp_stat_agent *agent, TestOptions *testOptions, int conn_options, double *c2sspd, int set_buff, int window, int autotune, char *device, Options *options, int record_reverse, int count_vars, char spds[4][256], int *spd_index, SSL_CTX *ctx, struct throughputSnapshot **c2s_ThroughputSnapshots, int extended) { tcp_stat_connection conn; tcp_stat_group *group = NULL; /* The pipe that will return packet pair results */ int mon_pipe[2]; int packet_trace_running = 0; pid_t c2s_childpid = 0; // child process pids int msgretvalue, read_error; // used during the "read"/"write" process int i; // used as loop iterator int conn_index, attempts; int retvalue = 0; int streamsNum = 1; int activeStreams = 1; int local_errno; struct sockaddr_storage cli_addr[MAX_STREAMS]; Connection c2s_conns[MAX_STREAMS]; struct throughputSnapshot *lastThroughputSnapshot; socklen_t clilen; char tmpstr[256]; // string array used for all sorts of temp storage purposes double start_time, measured_test_duration; double throughputSnapshotTime; // specify the next snapshot time double testDuration = 10; // default test duration double bytes_read = 0; // number of bytes read during the throughput tests struct timeval sel_tv; // time fd_set rfd; // receive file descriptors int max_fd; char buff[BUFFSIZE + 1]; // message "payload" buffer PortPair pair; // socket ports I2Addr c2ssrv_addr = NULL; // c2s test's server address // I2Addr src_addr=NULL; // c2s test source address char listenc2sport[10]; // listening port pthread_t workerThreadId; // snap related variables SnapArgs snapArgs; snapArgs.snap = NULL; #if USE_WEB100 snapArgs.log = NULL; #endif snapArgs.delay = options->snapDelay; // Test ID and status descriptors enum TEST_ID testids = extended ? C2S_EXT : C2S; enum TEST_STATUS_INT teststatuses = TEST_NOT_STARTED; char namesuffix[256] = "c2s_snaplog"; for (i = 0; i < MAX_STREAMS; i++) { c2s_conns[i].socket = 0; c2s_conns[i].ssl = NULL; } if (!extended && testOptions->c2sopt) { setCurrentTest(TEST_C2S); } else if (extended && testOptions->c2sextopt) { setCurrentTest(TEST_C2S_EXT); } else { return 0; } log_println(1, " <-- %d - C2S throughput test -->", testOptions->child0); strlcpy(listenc2sport, PORT2, sizeof(listenc2sport)); // log protocol validation logs teststatuses = TEST_STARTED; protolog_status(testOptions->child0, testids, teststatuses, ctl->socket); // Determine port to be used. Compute based on options set earlier // by reading from config file, or use default port2 (3002). if (testOptions->c2ssockport) { snprintf(listenc2sport, sizeof(listenc2sport), "%d", testOptions->c2ssockport); } else if (testOptions->mainport) { snprintf(listenc2sport, sizeof(listenc2sport), "%d", testOptions->mainport + 1); } if (testOptions->multiple) { strlcpy(listenc2sport, "0", sizeof(listenc2sport)); } // attempt to bind to a new port and obtain address structure with details // of listening port while (c2ssrv_addr == NULL) { c2ssrv_addr = CreateListenSocket( NULL, (testOptions->multiple ? mrange_next(listenc2sport, sizeof(listenc2sport)) : listenc2sport), conn_options, 0); if (strcmp(listenc2sport, "0") == 0) { log_println(1, "WARNING: ephemeral port number was bound"); break; } if (testOptions->multiple == 0) { break; } } if (c2ssrv_addr == NULL) { log_println(0, "Server (C2S throughput test): CreateListenSocket failed: %s", strerror(errno)); snprintf(buff, sizeof(buff), "Server (C2S throughput test): CreateListenSocket failed: %s", strerror(errno)); send_json_message_any(ctl, MSG_ERROR, buff, strlen(buff), testOptions->connection_flags, JSON_SINGLE_VALUE); return -1; } // get socket FD and the ephemeral port number that client will connect to // run tests testOptions->c2ssockfd = I2AddrFD(c2ssrv_addr); testOptions->c2ssockport = I2AddrPort(c2ssrv_addr); log_println(1, " -- c2s %d port: %d", testOptions->child0, testOptions->c2ssockport); if (extended) { log_println(1, " -- c2s ext -- duration = %d", options->c2s_duration); log_println(1, " -- c2s ext -- throughput snapshots: enabled = %s, " "delay = %d, offset = %d", options->c2s_throughputsnaps ? "true" : "false", options->c2s_snapsdelay, options->c2s_snapsoffset); log_println(1, " -- c2s ext -- number of streams: %d", options->c2s_streamsnum); } pair.port1 = testOptions->c2ssockport; pair.port2 = -1; log_println( 1, "listening for Inet connection on testOptions->c2ssockfd, fd=%d", testOptions->c2ssockfd); log_println( 1, "Sending 'GO' signal, to tell client %d to head for the next test", testOptions->child0); snprintf(buff, sizeof(buff), "%d", testOptions->c2ssockport); if (extended) { snprintf(buff, sizeof(buff), "%d %d %d %d %d %d", testOptions->c2ssockport, options->c2s_duration, options->c2s_throughputsnaps, options->c2s_snapsdelay, options->c2s_snapsoffset, options->c2s_streamsnum); lastThroughputSnapshot = NULL; } // send TEST_PREPARE message with ephemeral port detail, indicating start // of tests if ((msgretvalue = send_json_message_any( ctl, TEST_PREPARE, buff, strlen(buff), testOptions->connection_flags, JSON_SINGLE_VALUE)) < 0) { log_println(2, "Child %d could not send details about ephemeral port", getpid()); return msgretvalue; } // Wait on listening socket and read data once ready. // Retry 5 times, waiting for activity on the socket clilen = sizeof(cli_addr); log_println(6, "child %d - sent c2s prepare to client", testOptions->child0); FD_ZERO(&rfd); FD_SET(testOptions->c2ssockfd, &rfd); sel_tv.tv_sec = 5; sel_tv.tv_usec = 0; if (extended) { streamsNum = options->c2s_streamsnum; testDuration = options->c2s_duration / 1000.0; } conn_index = 0; for (attempts = 0; attempts < RETRY_COUNT * streamsNum && conn_index < streamsNum; attempts++) { msgretvalue = select((testOptions->c2ssockfd) + 1, &rfd, NULL, NULL, &sel_tv); // TODO // socket interrupted. continue waiting for activity on socket if ((msgretvalue == -1) && (errno == EINTR)) { continue; } if (msgretvalue == 0) // timeout retvalue = -SOCKET_CONNECT_TIMEOUT; if (msgretvalue < 0) // other socket errors. exit retvalue = -errno; if (!retvalue) { // If a valid connection request is received, client has connected. // Proceed. c2s_conns[conn_index].socket = accept( testOptions->c2ssockfd, (struct sockaddr *)&cli_addr[conn_index], &clilen); // socket interrupted, wait some more if ((c2s_conns[conn_index].socket == -1) && (errno == EINTR)) { log_println( 6, "Child %d interrupted while waiting for accept() to complete", testOptions->child0); continue; } if (c2s_conns[conn_index].socket <= 0) { continue; } log_println(6, "accept(%d/%d) for %d completed", conn_index + 1, streamsNum, testOptions->child0); // log protocol validation indicating client accept protolog_procstatus(testOptions->child0, testids, CONNECT_TYPE, PROCESS_STARTED, c2s_conns[conn_index].socket); if (testOptions->connection_flags & TLS_SUPPORT) { errno = setup_SSL_connection(&c2s_conns[conn_index], ctx); if (errno != 0) return -errno; } // To preserve user privacy, make sure that the HTTP header // processing is done prior to the start of packet capture, as many // browsers have headers that uniquely identify a single user. if (testOptions->connection_flags & WEBSOCKET_SUPPORT) { if (initialize_websocket_connection(&c2s_conns[conn_index], 0, "c2s") != 0) { close_all_connections(c2s_conns, streamsNum); return -EIO; } } conn_index++; } if (retvalue) { log_println( 6, "------- C2S connection setup for %d returned because (%d)", testOptions->child0, retvalue); close_all_connections(c2s_conns, streamsNum); return retvalue; } } // If we haven't created enough streams after the loop is over, quit. if (conn_index != streamsNum) { log_println( 6, "c2s child %d, unable to open connection, return from test", testOptions->child0); close_all_connections(c2s_conns, streamsNum); return RETRY_EXCEEDED_WAITING_DATA; } // Get address associated with the throughput test. Used for packet tracing log_println(6, "child %d - c2s ready for test with fd=%d", testOptions->child0, c2s_conns[0].socket); // commenting out below to move to init_pkttrace function I2Addr src_addr = I2AddrByLocalSockFD(get_errhandle(), c2s_conns[0].socket, 0); // Get tcp_stat connection. Used to collect tcp_stat variable statistics conn = tcp_stat_connection_from_socket(agent, c2s_conns[0].socket); // set up packet tracing. Collected data is used for bottleneck link // calculations if (getuid() == 0) { if (pipe(mon_pipe) != 0) { log_println(0, "C2S test error: can't create pipe."); } else { if ((c2s_childpid = fork()) == 0) { close(testOptions->c2ssockfd); close_all_connections(c2s_conns, streamsNum); // Don't capture more than 14 seconds of packet traces: // 2 seconds of sleep + 10 seconds of test + 2 seconds of slop alarm(testDuration + RACE_CONDITION_WAIT_TIME + 2); log_println( 5, "C2S test Child %d thinks pipe() returned fd0=%d, fd1=%d", testOptions->child0, mon_pipe[0], mon_pipe[1]); log_println(2, "C2S test calling init_pkttrace() with pd=%p", &cli_addr[0]); init_pkttrace(src_addr, cli_addr, streamsNum, clilen, mon_pipe, device, &pair, "c2s", options->c2s_duration / 1000.0); log_println(1, "c2s is exiting gracefully"); /* Close the pipe */ close(mon_pipe[0]); close(mon_pipe[1]); exit(0); /* Packet trace finished, terminate gracefully */ } else if (c2s_childpid < 0) { log_println(0, "C2S test error: can't create child process."); } } packet_trace_running = wait_for_readable_fd(mon_pipe[0]); if (packet_trace_running) { // Get data collected from packet tracing into the C2S "ndttrace" file memset(tmpstr, 0, 256); for (i = 0; i < 5; i++) { msgretvalue = read(mon_pipe[0], tmpstr, 128); if ((msgretvalue == -1) && (errno == EINTR)) continue; break; } if (strlen(tmpstr) > 5) memcpy(meta.c2s_ndttrace, tmpstr, strlen(tmpstr)); // name of nettrace file passed back from pcap child log_println(3, "--tracefile after packet_trace %s", meta.c2s_ndttrace); } else { log_println(0, "Packet trace was unable to be created"); packet_trace_emergency_shutdown(mon_pipe); } } log_println(5, "C2S test Parent thinks pipe() returned fd0=%d, fd1=%d", mon_pipe[0], mon_pipe[1]); // experimental code, delete when finished setCwndlimit(conn, group, agent, options); // Create C->S snaplog directories, and perform some initialization based on // options create_client_logdir((struct sockaddr *) &cli_addr[0], clilen, options->c2s_logname, sizeof(options->c2s_logname), namesuffix, sizeof(namesuffix)); sleep(RACE_CONDITION_WAIT_TIME); // Reset alarm() again. This 10 sec test should finish before this signal is // generated, but sleep() can render existing alarm()s invalid, and alarm() is // our watchdog timer. alarm(30); // send empty TEST_START indicating start of the test send_json_message_any(ctl, TEST_START, "", 0, testOptions->connection_flags, JSON_SINGLE_VALUE); // If snaplog recording is enabled, update meta file to indicate the same // and proceed to get snapshot and log it. // This block is needed here since the meta file stores names without the // full directory but fopen needs full path. Else, it could have gone into // the "start_snap_worker" method if (options->snapshots && options->snaplog) { memcpy(meta.c2s_snaplog, namesuffix, strlen(namesuffix)); /*start_snap_worker(&snapArgs, agent, options->snaplog, &workerLoop, &workerThreadId, meta.c2s_snaplog, options->c2s_logname, conn, group); */ } if (options->snapshots) start_snap_worker(&snapArgs, agent, NULL, options->snaplog, &workerThreadId, options->c2s_logname, conn, group); // Wait on listening socket and read data once ready. start_time = secs(); throughputSnapshotTime = start_time + (options->c2s_snapsoffset / 1000.0); activeStreams = connections_to_fd_set(c2s_conns, streamsNum, &rfd, &max_fd); while (activeStreams > 0 && (secs() - start_time) < testDuration) { // POSIX says "Upon successful completion, the select() function may // modify the object pointed to by the timeout argument." // Therefore sel_tv is undefined afterwards and we must set it every time. sel_tv.tv_sec = testDuration + 1; // time out after test duration + 1sec sel_tv.tv_usec = 0; msgretvalue = select(max_fd + 1, &rfd, NULL, NULL, &sel_tv); if (msgretvalue == -1) { if (errno == EINTR) { // select interrupted. Continue waiting for activity on socket continue; } else { log_println(1, "Error while trying to wait for incoming data in c2s: %s", strerror(errno)); break; } } if (extended && options->c2s_throughputsnaps && secs() > throughputSnapshotTime) { if (lastThroughputSnapshot != NULL) { lastThroughputSnapshot->next = (struct throughputSnapshot*) malloc(sizeof(struct throughputSnapshot)); lastThroughputSnapshot = lastThroughputSnapshot->next; } else { *c2s_ThroughputSnapshots = lastThroughputSnapshot = (struct throughputSnapshot*) malloc(sizeof(struct throughputSnapshot)); } lastThroughputSnapshot->next = NULL; lastThroughputSnapshot->time = secs() - start_time; lastThroughputSnapshot->throughput = (8.e-3 * bytes_read) / (lastThroughputSnapshot->time); // kbps log_println(6, " ---C->S: Throughput at %0.2f secs: Received %0.0f bytes, Spdin= %f", lastThroughputSnapshot->time, bytes_read, lastThroughputSnapshot->throughput); throughputSnapshotTime += options->c2s_snapsdelay / 1000.0; } if (msgretvalue > 0) { read_error = read_ready_streams(&rfd, c2s_conns, streamsNum, buff, sizeof(buff), &bytes_read); if (read_error != 0 && read_error != EINTR) { // EINTR is expected, but all other errors are actually errors log_println(1, "Error while trying to read incoming data in c2s: %s", strerror(read_error)); break; } } // Set up the FD_SET and activeStreams for the next select. activeStreams = connections_to_fd_set(c2s_conns, streamsNum, &rfd, &max_fd); } measured_test_duration = secs() - start_time; // From the NDT spec: // throughput in kilo bits per sec = // (transmitted_byte_count * 8) / (time_duration)*(1000) *c2sspd = (8.0e-3 * bytes_read) / measured_test_duration; // As a kindness to old clients, drain their queues for a second or two. // TODO: Fix web100clt code to eliminate the need for this. In general, // clients that need this line should be removed from their respective // gene pools and this code should be deleted. drain_old_clients(c2s_conns, streamsNum, buff, sizeof(buff)); // c->s throuput value calculated and assigned ! Release resources, conclude // snap writing. if (options->snapshots) stop_snap_worker(&workerThreadId, options->snaplog, &snapArgs); // send the server calculated value of C->S throughput as result to client snprintf(buff, sizeof(buff), "%6.0f kbps outbound for child %d", *c2sspd, testOptions->child0); log_println(1, "%s", buff); snprintf(buff, sizeof(buff), "%0.0f", *c2sspd); if (extended && options->c2s_throughputsnaps && *c2s_ThroughputSnapshots != NULL) { struct throughputSnapshot *snapshotsPtr = *c2s_ThroughputSnapshots; while (snapshotsPtr != NULL) { int currBuffLength = strlen(buff); snprintf(&buff[currBuffLength], sizeof(buff)-currBuffLength, " %0.2f %0.2f", snapshotsPtr->time, snapshotsPtr->throughput); snapshotsPtr = snapshotsPtr->next; } } send_json_message_any(ctl, TEST_MSG, buff, strlen(buff), testOptions->connection_flags, JSON_SINGLE_VALUE); // get receiver side Web100 stats and write them to the log file. close // sockets if (record_reverse == 1) tcp_stat_get_data_recv(c2s_conns[0].socket, agent, conn, count_vars); close_all_connections(c2s_conns, streamsNum); close(testOptions->c2ssockfd); if (packet_trace_running) { log_println(1, "Signal USR1(%d) sent to child [%d]", SIGUSR1, c2s_childpid); testOptions->child1 = c2s_childpid; kill(c2s_childpid, SIGUSR1); i = 0; for (;;) { FD_ZERO(&rfd); FD_SET(mon_pipe[0], &rfd); sel_tv.tv_sec = 1; sel_tv.tv_usec = 100000; msgretvalue = select(mon_pipe[0] + 1, &rfd, NULL, NULL, &sel_tv); if (msgretvalue <= 0) { local_errno = (msgretvalue == -1) ? errno : 0; if (local_errno == EINTR) continue; // Either a timeout or an error that wasn't EINTR... log_println(4, "Failed to read pkt-pair data from C2S flow, " "retcode=%d, reason=%d", msgretvalue, local_errno); snprintf(spds[(*spd_index)++], sizeof(spds[*spd_index]), " -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0.0 0 0 0 0 0 -1"); snprintf(spds[(*spd_index)++], sizeof(spds[*spd_index]), " -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0.0 0 0 0 0 0 -1"); break; } else { /* There is something to read, so get it from the pktpair child. If an * interrupt occurs, just skip the read and go on * RAC 2/8/10 */ if ((msgretvalue = read(mon_pipe[0], spds[*spd_index], sizeof(spds[*spd_index]))) < 0) { snprintf( spds[*spd_index], sizeof(spds[*spd_index]), " -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0.0 0 0 0 0 0 -1"); } log_println(1, "%d bytes read '%s' from C2S monitor pipe", msgretvalue, spds[*spd_index]); (*spd_index)++; if (i++ == 1) break; } } } // An empty TEST_FINALIZE message is sent to conclude the test send_json_message_any(ctl, TEST_FINALIZE, "", 0, testOptions->connection_flags, JSON_SINGLE_VALUE); // Close opened resources for packet capture if (packet_trace_running) { // TODO: Determine whether this shutdown can be performed in a non-blocking // manner, and if so then refactor packet_trace_emergency_shutdown to have // better error handling and use that refactored and renamed function here. stop_packet_trace(mon_pipe); packet_trace_running = 0; } // log end of C->S test log_println(1, " <----------- %d -------------->", testOptions->child0); // protocol logs teststatuses = TEST_ENDED; protolog_status(testOptions->child0, testids, teststatuses, ctl->socket); // set current test status and free address setCurrentTest(TEST_NONE); return 0; }
static int OpenSocket(I2Addr addr, char* serv, int options) { struct addrinfo *fai; struct addrinfo *ai; int on; socklen_t onSize; int fd=-1; if (!(fai = I2AddrAddrInfo(addr, NULL, serv))) { return -2; } for (ai = fai; ai; ai = ai->ai_next) { #ifdef AF_INET6 if (options & OPT_IPV6_ONLY) { if(ai->ai_family != AF_INET6) continue; } #endif if (options & OPT_IPV4_ONLY) { if(ai->ai_family != AF_INET) continue; } fd = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); /* if (meta.family == 0) meta.family = ai->ai_family; */ if (fd < 0) { continue; } on=1; if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) != 0) { goto failsock; } #if defined(AF_INET6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) if ((ai->ai_family == AF_INET6) && (options & OPT_IPV6_ONLY) && setsockopt(fd,IPPROTO_IPV6,IPV6_V6ONLY,&on,sizeof(on)) != 0) { goto failsock; } #endif if (bind(fd,ai->ai_addr,ai->ai_addrlen) == 0) { if (!I2AddrSetSAddr(addr,ai->ai_addr,ai->ai_addrlen) || !I2AddrSetProtocol(addr,ai->ai_protocol) || !I2AddrSetSocktype(addr,ai->ai_socktype)) { log_println(1, "OpenSocket: Unable to set saddr in address record"); return -1; } if (!I2AddrPort(addr)) { struct sockaddr_storage tmp_addr; socklen_t tmp_addr_len = sizeof(tmp_addr); I2Addr tmpAddr; if (getsockname(fd, (struct sockaddr*) &tmp_addr, &tmp_addr_len)) { return -1; } tmpAddr = I2AddrBySAddr(get_errhandle(), (struct sockaddr*) &tmp_addr, tmp_addr_len, 0, 0); I2AddrSetPort(addr, I2AddrPort(tmpAddr)); I2AddrFree(tmpAddr); } if (!I2AddrSetFD(addr,fd,True)) { log_println(1, "OpenSocket: Unable to set file descriptor in address record"); return -1; } break; } if (errno == EADDRINUSE) { /* RAC debug statemement 10/11/06 */ onSize = sizeof(on); getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, &onSize); log_println(1, "bind(%d) failed: Address already in use given as the reason, getsockopt() returned %d", fd, on); return -2; } failsock: /* RAC debug statemement 10/11/06 */ log_println(1, "failsock: Unable to set socket options for fd=%d", fd); while((close(fd) < 0) && (errno == EINTR)); } if (meta.family == 0) meta.family = ai->ai_family; return fd; }
/** * Perform the client part of the middleBox testing. The middlebox test * is a 5.0 second throughput test from the Server to the Client to * check for duplex mismatch conditions. It determines if routers or * switches in the path may be making changes to some TCP parameters. * @param ctlSocket server control socket descriptor * @param tests set of tests to perform * @param host hostname of the server * @param conn_options connection options * @param buf_size TCP send/receive buffer size * @param testresult_str result obtained from server (containing server ip, * client ip, currentMSS, WinSCaleSent, WinScaleRcvd) * @param jsonSupport Indicates if messages should be sent using JSON format * @return integer * => 0 on success * < 0 if error * Return codes used: * 0 = (the test has been finalized) * > 0 if protocol interactions were not as expected: * 1: Unable to receive protocol message successfully * 2: Wrong message type received * 3: Protocol message received was of invalid length * 4: Protocol payload data received was invalid * 5: Protocol message received was invalid * < 0 if generic error: * -3: Unable to resolve server address * -10: creating connection to server failed * */ int test_mid_clt(int ctlSocket, char tests, char* host, int conn_options, int buf_size, char* testresult_str, int jsonSupport) { char buff[BUFFSIZE + 1]; int msgLen, msgType; int midport = atoi(PORT3); I2Addr sec_addr = NULL; int retcode, inlth; int in2Socket; double t, spdin; uint32_t bytes; struct timeval sel_tv; fd_set rfd; char* jsonMsgValue; enum TEST_STATUS_INT teststatuses = TEST_NOT_STARTED; enum TEST_ID testids = MIDDLEBOX; if (tests & TEST_MID) { // middlebox test has to be performed log_println(1, " <-- Middlebox test -->"); setCurrentTest(TEST_MID); // protocol logs teststatuses = TEST_STARTED; protolog_status(getpid(), testids, teststatuses, ctlSocket); // Initially, expecting a TEST_PREPARE message. Any other message // ..type is unexpected at this stage. msgLen = sizeof(buff); if (recv_msg(ctlSocket, &msgType, buff, &msgLen)) { log_println(0, "Protocol error - missed prepare message!"); return 1; } if (check_msg_type(MIDBOX_TEST_LOG, TEST_PREPARE, msgType, buff, msgLen)) { return 2; } // The server is expected to send a message with a valid payload that // contains the port number that server wants client to bind to for this // test buff[msgLen] = 0; if (jsonSupport) { jsonMsgValue = json_read_map_value(buff, DEFAULT_KEY); strlcpy(buff, jsonMsgValue, sizeof(buff)); msgLen = strlen(buff); free(jsonMsgValue); } if (msgLen <= 0) { log_println(0, "Improper message"); return 3; } if (check_int(buff, &midport)) { // obtained message does not contain // integer port# log_println(0, "Invalid port number"); return 4; } // get server address and set port log_println(1, " -- port: %d", midport); if ((sec_addr = I2AddrByNode(get_errhandle(), host)) == NULL) { log_println(0, "Unable to resolve server address: %s", strerror(errno)); return -3; } I2AddrSetPort(sec_addr, midport); // debug to check if correct port was set in addr struct if (get_debuglvl() > 4) { char tmpbuff[200]; size_t tmpBufLen = 199; memset(tmpbuff, 0, 200); I2AddrNodeName(sec_addr, tmpbuff, &tmpBufLen); log_println(5, "connecting to %s:%d", tmpbuff, I2AddrPort(sec_addr)); } // connect to server using port obtained above if ((retcode = CreateConnectSocket(&in2Socket, NULL, sec_addr, conn_options, buf_size))) { log_println(0, "Connect() for middlebox failed: %s", strerror(errno)); return -10; } // start reading throughput test data from server using the connection // created above printf("Checking for Middleboxes . . . . . . . . . . . . . . . . . . "); fflush(stdout); testresult_str[0] = '\0'; bytes = 0; t = secs() + 5.0; // set timer for 5 seconds, and read for 5 seconds sel_tv.tv_sec = 6; // Time out the socket after 6.5 seconds sel_tv.tv_usec = 5; // 500? FD_ZERO(&rfd); FD_SET(in2Socket, &rfd); for (;;) { if (secs() > t) break; retcode = select(in2Socket+1, &rfd, NULL, NULL, &sel_tv); if (retcode > 0) { inlth = read(in2Socket, buff, sizeof(buff)); if (inlth == 0) break; bytes += inlth; continue; } if (retcode < 0) { printf("nothing to read, exiting read loop\n"); break; } if (retcode == 0) { printf("timer expired, exiting read loop\n"); break; } } // get actual time for which test was run t = secs() - t + 5.0; // calculate throughput in Kbps spdin = ((BITS_8_FLOAT * bytes) / KILO) / t; // Test is complete. Now, get results from server (includes CurrentMSS, // WinScaleSent, WinScaleRcvd..). // The results are sent from server in the form of a TEST_MSG object msgLen = sizeof(buff); if (recv_msg(ctlSocket, &msgType, buff, &msgLen)) { log_println(0, "Protocol error - missed text message!"); return 1; } if (check_msg_type(MIDBOX_TEST_LOG " results", TEST_MSG, msgType, buff, msgLen)) { return 2; } buff[msgLen] = 0; strlcat(testresult_str, buff, MIDBOX_TEST_RES_SIZE); memset(buff, 0, sizeof(buff)); // this should work since the throughput results from the server should // ...fit well within BUFFSIZE snprintf(buff, sizeof(buff), "%0.0f", spdin); log_println(4, "CWND limited speed = %0.2f kbps", spdin); // client now sends throughput it calculated above to server, as a TEST_MSG send_json_message(ctlSocket, TEST_MSG, buff, strlen(buff), jsonSupport, JSON_SINGLE_VALUE); printf("Done\n"); I2AddrFree(sec_addr); // Expect an empty TEST_FINALIZE message from server msgLen = sizeof(buff); if (recv_msg(ctlSocket, &msgType, buff, &msgLen)) { log_println(0, "Protocol error - missed finalize message!"); return 1; } if (check_msg_type(MIDBOX_TEST_LOG, TEST_FINALIZE, msgType, buff, msgLen)) { return 2; } log_println(1, " <-------------------->"); // log protocol test ending teststatuses = TEST_ENDED; protolog_status(getpid(), testids, teststatuses, ctlSocket); setCurrentTest(TEST_NONE); } return 0; }