int main(int argc, char *argv[]) { struct FRAME_HEADERS FRAME_HEADERS; struct TEST_INTERFACE TEST_INTERFACE; struct TEST_PARAMS TEST_PARAMS; struct MTU_TEST MTU_TEST; struct QM_TEST QM_TEST; struct APP_PARAMS APP_PARAMS; char TESTING = true; while(TESTING) { /* ******************************************************* DEFAULT VALUES */ pFRAME_HEADERS = &FRAME_HEADERS; FRAME_HEADERS.SOURCE_MAC[0] = 0x00; FRAME_HEADERS.SOURCE_MAC[1] = 0x00; FRAME_HEADERS.SOURCE_MAC[2] = 0x5E; FRAME_HEADERS.SOURCE_MAC[3] = 0x00; FRAME_HEADERS.SOURCE_MAC[4] = 0x00; FRAME_HEADERS.SOURCE_MAC[5] = 0x01; FRAME_HEADERS.DEST_MAC[0] = 0x00; FRAME_HEADERS.DEST_MAC[1] = 0x00; FRAME_HEADERS.DEST_MAC[2] = 0x5E; FRAME_HEADERS.DEST_MAC[3] = 0x00; FRAME_HEADERS.DEST_MAC[4] = 0x00; FRAME_HEADERS.DEST_MAC[5] = 0x02; FRAME_HEADERS.LENGTH = HEADERS_LEN_DEF; FRAME_HEADERS.ETHERTYPE = ETHERTYPE_DEF; FRAME_HEADERS.PCP = PCP_DEF; FRAME_HEADERS.VLAN_ID = VLAN_ID_DEF; FRAME_HEADERS.QINQ_ID = QINQ_ID_DEF; FRAME_HEADERS.QINQ_PCP = QINQ_PCP_DEF; FRAME_HEADERS.LSP_SOURCE_MAC[0] = 0x00; FRAME_HEADERS.LSP_SOURCE_MAC[1] = 0x00; FRAME_HEADERS.LSP_SOURCE_MAC[2] = 0x00; FRAME_HEADERS.LSP_SOURCE_MAC[3] = 0x00; FRAME_HEADERS.LSP_SOURCE_MAC[4] = 0x00; FRAME_HEADERS.LSP_SOURCE_MAC[5] = 0x00; FRAME_HEADERS.LSP_SOURCE_MAC[6] = 0x00; FRAME_HEADERS.LSP_DEST_MAC[0] = 0x00; FRAME_HEADERS.LSP_DEST_MAC[1] = 0x00; FRAME_HEADERS.LSP_DEST_MAC[2] = 0x00; FRAME_HEADERS.LSP_DEST_MAC[3] = 0x00; FRAME_HEADERS.LSP_DEST_MAC[4] = 0x00; FRAME_HEADERS.LSP_DEST_MAC[5] = 0x00; FRAME_HEADERS.LSP_DEST_MAC[6] = 0x00; FRAME_HEADERS.MPLS_LABELS = 0; for (int i = 0; i<MPLS_LABELS_MAX; i++) { FRAME_HEADERS.MPLS_LABEL[i] = 0; FRAME_HEADERS.MPLS_EXP[i] = 0; FRAME_HEADERS.MPLS_TTL[i] = 0; } FRAME_HEADERS.PWE_CONTROL_WORD = 0; FRAME_HEADERS.MPLS_IGNORE = 0; FRAME_HEADERS.TLV_SIZE = sizeof(char) + sizeof(short) + sizeof(unsigned long); FRAME_HEADERS.SUB_TLV_SIZE = FRAME_HEADERS.TLV_SIZE + sizeof(char) + sizeof(short) + sizeof(unsigned long long); pTEST_INTERFACE = &TEST_INTERFACE; TEST_INTERFACE.IF_INDEX = IF_INDEX_DEF; for (int i = 0; i<IFNAMSIZ; i++) { TEST_INTERFACE.IF_NAME[i] = 0; } TEST_PARAMS.F_SIZE = F_SIZE_DEF; TEST_PARAMS.F_SIZE_TOTAL = F_SIZE_DEF + FRAME_HEADERS.LENGTH; TEST_PARAMS.F_DURATION = F_DURATION_DEF; TEST_PARAMS.F_COUNT = F_COUNT_DEF; TEST_PARAMS.F_BYTES = F_BYTES_DEF; TEST_PARAMS.S_ELAPSED = 0; TEST_PARAMS.B_TX_SPEED_MAX = B_TX_SPEED_MAX_DEF; TEST_PARAMS.B_TX_SPEED_PREV = 0; TEST_PARAMS.F_TX_COUNT = 0; TEST_PARAMS.F_TX_COUNT_PREV = 0; TEST_PARAMS.F_TX_SPEED_MAX = F_TX_SPEED_MAX_DEF; TEST_PARAMS.B_TX = 0; TEST_PARAMS.B_TX_PREV = 0; TEST_PARAMS.F_RX_COUNT = 0; TEST_PARAMS.F_RX_COUNT_PREV = 0; TEST_PARAMS.B_RX = 0; TEST_PARAMS.B_RX_PREV = 0; TEST_PARAMS.F_INDEX_PREV = 0; TEST_PARAMS.F_RX_ONTIME = 0; TEST_PARAMS.F_RX_EARLY = 0; TEST_PARAMS.F_RX_LATE = 0; TEST_PARAMS.F_RX_OTHER = 0; TEST_PARAMS.B_SPEED = 0; TEST_PARAMS.B_SPEED_MAX = 0; TEST_PARAMS.B_SPEED_AVG = 0; TEST_PARAMS.F_ACK = false; TEST_PARAMS.F_WAITING_ACK = false; MTU_TEST.ENABLED = false; MTU_TEST.MTU_TX_MIN = 1400; MTU_TEST.MTU_TX_MAX = 1500; pQM_TEST = &QM_TEST; QM_TEST.ENABLED = false; QM_TEST.INTERVAL = 1000; QM_TEST.INTERVAL_SEC = 0; QM_TEST.INTERVAL_NSEC = 0; QM_TEST.INTERVAL_MIN = 99999.99999; QM_TEST.INTERVAL_MAX = 0.0; QM_TEST.TIMEOUT = 1000; QM_TEST.TIMEOUT_NSEC = 0; QM_TEST.TIMEOUT_SEC = 0; QM_TEST.TIMEOUT_COUNT = 0; QM_TEST.DELAY_TEST_COUNT = 10000; QM_TEST.RTT_MIN = 999999.999999; QM_TEST.RTT_MAX = 0.0; QM_TEST.JITTER_MIN = 999999.999999; QM_TEST.JITTER_MAX = 0.0; QM_TEST.pDELAY_RESULTS = (double*)calloc(QM_TEST.DELAY_TEST_COUNT, sizeof(double)); APP_PARAMS.TX_MODE = true; APP_PARAMS.TX_SYNC = true; APP_PARAMS.TX_DELAY = TX_DELAY_DEF; /* ************************************************************* CLI ARGS */ int CLI_RET_VAL = cli_args(argc, argv, &APP_PARAMS, &FRAME_HEADERS, &TEST_INTERFACE, &TEST_PARAMS, &MTU_TEST, &QM_TEST); if (CLI_RET_VAL == -2) { return EXIT_SUCCESS; } else if (CLI_RET_VAL == -1) { return EX_USAGE; } // Check for root privs because low level socket access is required if (getuid() != 0) { printf("Must be root to use this program!\n"); return EX_NOPERM; } if (APP_PARAMS.TX_MODE) printf("Running in TX mode\n"); else printf("Running in RX mode\n"); /* ****************************************************** INTERFACE SETUP */ TEST_INTERFACE.SOCKET_FD = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); ///// Monitor sock1 for input TEST_INTERFACE.fds[0].fd = TEST_INTERFACE.SOCKET_FD; TEST_INTERFACE.fds[0].events = POLLIN; TEST_INTERFACE.fds[0].revents = 0; if (TEST_INTERFACE.SOCKET_FD < 0 ) { printf("Error defining socket!\n"); perror("socket() "); close(TEST_INTERFACE.SOCKET_FD); return EX_SOFTWARE; } // If the user has supplied an interface index try to use that if (TEST_INTERFACE.IF_INDEX != IF_INDEX_DEF) { TEST_INTERFACE.IF_INDEX = set_sock_interface_index(&TEST_INTERFACE); if (TEST_INTERFACE.IF_INDEX == 0) { printf("Error: Couldn't set interface with index, " "returned index was 0!\n"); return EX_SOFTWARE; } // Or if the user has supplied an interface name try to use that } else if (strcmp(TEST_INTERFACE.IF_NAME, "") != 0) { TEST_INTERFACE.IF_INDEX = set_sock_interface_name(&TEST_INTERFACE); if (TEST_INTERFACE.IF_INDEX == 0) { printf("Error: Couldn't set interface index from name, " "returned index was 0!\n"); return EX_SOFTWARE; } // Otherwise, try and best guess an interface } else if (TEST_INTERFACE.IF_INDEX == IF_INDEX_DEF) { TEST_INTERFACE.IF_INDEX = get_sock_interface(&TEST_INTERFACE); if (TEST_INTERFACE.IF_INDEX == 0) { printf("Error: Couldn't find appropriate interface ID, " "returned ID was 0!\n Try supplying a source MAC address " "with the -s option.\n"); return EX_SOFTWARE; } } // Link layer socket setup TEST_INTERFACE.SOCKET_ADDRESS.sll_family = PF_PACKET; TEST_INTERFACE.SOCKET_ADDRESS.sll_protocol = htons(ETH_P_IP); TEST_INTERFACE.SOCKET_ADDRESS.sll_ifindex = TEST_INTERFACE.IF_INDEX; TEST_INTERFACE.SOCKET_ADDRESS.sll_hatype = ARPHRD_ETHER; TEST_INTERFACE.SOCKET_ADDRESS.sll_pkttype = PACKET_OTHERHOST; TEST_INTERFACE.SOCKET_ADDRESS.sll_halen = ETH_ALEN; TEST_INTERFACE.SOCKET_ADDRESS.sll_addr[0] = FRAME_HEADERS.DEST_MAC[0]; TEST_INTERFACE.SOCKET_ADDRESS.sll_addr[1] = FRAME_HEADERS.DEST_MAC[1]; TEST_INTERFACE.SOCKET_ADDRESS.sll_addr[2] = FRAME_HEADERS.DEST_MAC[2]; TEST_INTERFACE.SOCKET_ADDRESS.sll_addr[3] = FRAME_HEADERS.DEST_MAC[3]; TEST_INTERFACE.SOCKET_ADDRESS.sll_addr[4] = FRAME_HEADERS.DEST_MAC[4]; TEST_INTERFACE.SOCKET_ADDRESS.sll_addr[5] = FRAME_HEADERS.DEST_MAC[5]; TEST_INTERFACE.SOCKET_ADDRESS.sll_addr[6] = 0x00; TEST_INTERFACE.SOCKET_ADDRESS.sll_addr[7] = 0x00; // Send and receive buffers for incoming/outgoing ethernet frames FRAME_HEADERS.RX_BUFFER = (char*)calloc(1, F_SIZE_MAX); FRAME_HEADERS.TX_BUFFER = (char*)calloc(1, F_SIZE_MAX); build_headers(&FRAME_HEADERS); // Total size of the frame data (paylod size+headers), this excludes the // preamble & start frame delimiter, FCS and inter frame gap TEST_PARAMS.F_SIZE_TOTAL = TEST_PARAMS.F_SIZE + FRAME_HEADERS.LENGTH; int PHY_MTU = get_interface_mtu_by_name(&TEST_INTERFACE); if (PHY_MTU==-1) { printf("\nPhysical interface MTU unknown, " "test might exceed physical MTU!\n\n"); } else if (TEST_PARAMS.F_SIZE_TOTAL > PHY_MTU + 14) { printf("\nPhysical interface MTU (%u with headers) is less than\n" "the test frame size (%u with headers). Test frames shall\n" "bo limited to the interface MTU size\n\n", PHY_MTU+14, TEST_PARAMS.F_SIZE_TOTAL); TEST_PARAMS.F_SIZE_TOTAL = PHY_MTU + 14; } // Fill the test frame with some random data for (unsigned int i = 0; i < (F_SIZE_MAX-FRAME_HEADERS.LENGTH); i++) { FRAME_HEADERS.TX_DATA[i] = (char)((255.0*rand()/(RAND_MAX+1.0))); } // Send and receive ret vals int TX_RET_VAL = 0; // Set the network interface to promiscuos mode printf("Entering promiscuous mode\n"); strncpy(ethreq.ifr_name,TEST_INTERFACE.IF_NAME,IFNAMSIZ); if (ioctl(TEST_INTERFACE.SOCKET_FD,SIOCGIFFLAGS,ðreq) == -1) { printf("Error getting socket flags, entering promiscuous mode failed!\n"); perror("ioctl() "); close(TEST_INTERFACE.SOCKET_FD); return EX_SOFTWARE; } ethreq.ifr_flags|=IFF_PROMISC; if (ioctl(TEST_INTERFACE.SOCKET_FD,SIOCSIFFLAGS,ðreq) == -1) { printf("Error setting socket flags, entering promiscuous mode failed!\n"); perror("ioctl() "); close(TEST_INTERFACE.SOCKET_FD); return EX_SOFTWARE; } // Declare sigint handler, TX will signal RX to reset when it quits signal (SIGINT,signal_handler); printf("Source MAC %02x:%02x:%02x:%02x:%02x:%02x\n", FRAME_HEADERS.SOURCE_MAC[0],FRAME_HEADERS.SOURCE_MAC[1], FRAME_HEADERS.SOURCE_MAC[2],FRAME_HEADERS.SOURCE_MAC[3], FRAME_HEADERS.SOURCE_MAC[4],FRAME_HEADERS.SOURCE_MAC[5]); printf("Destination MAC %02x:%02x:%02x:%02x:%02x:%02x\n", FRAME_HEADERS.DEST_MAC[0],FRAME_HEADERS.DEST_MAC[1], FRAME_HEADERS.DEST_MAC[2],FRAME_HEADERS.DEST_MAC[3], FRAME_HEADERS.DEST_MAC[4],FRAME_HEADERS.DEST_MAC[5]); // Broadcacst to populate any TCAM or MAC tables unsigned char TEMP_MAC[6] = { FRAME_HEADERS.DEST_MAC[0], FRAME_HEADERS.DEST_MAC[1], FRAME_HEADERS.DEST_MAC[2], FRAME_HEADERS.DEST_MAC[3], FRAME_HEADERS.DEST_MAC[4], FRAME_HEADERS.DEST_MAC[5], }; FRAME_HEADERS.DEST_MAC[0] = 0xFF; FRAME_HEADERS.DEST_MAC[1] = 0xFF; FRAME_HEADERS.DEST_MAC[2] = 0xFF; FRAME_HEADERS.DEST_MAC[3] = 0xFF; FRAME_HEADERS.DEST_MAC[4] = 0xFF; FRAME_HEADERS.DEST_MAC[5] = 0xFF; // Rebuild frame headers with destination MAC build_headers(&FRAME_HEADERS); build_tlv(&FRAME_HEADERS, htons(TYPE_BROADCAST), htonl(VALUE_PRESENCE)); // Build a dummy sub-TLV to align the buffers and pointers build_sub_tlv(&FRAME_HEADERS, htons(TYPE_APPLICATION_SUB_TLV), htonll(VALUE_DUMMY)); printf("Sending gratuitous broadcasts...\n"); for (int i=1; i<=3; i++) { TX_RET_VAL = sendto(TEST_INTERFACE.SOCKET_FD, FRAME_HEADERS.TX_BUFFER, FRAME_HEADERS.LENGTH+FRAME_HEADERS.TLV_SIZE, 0, (struct sockaddr*)&TEST_INTERFACE.SOCKET_ADDRESS, sizeof(TEST_INTERFACE.SOCKET_ADDRESS)); sleep(1); } FRAME_HEADERS.DEST_MAC[0] = TEMP_MAC[0]; FRAME_HEADERS.DEST_MAC[1] = TEMP_MAC[1]; FRAME_HEADERS.DEST_MAC[2] = TEMP_MAC[2]; FRAME_HEADERS.DEST_MAC[3] = TEMP_MAC[3]; FRAME_HEADERS.DEST_MAC[4] = TEMP_MAC[4]; FRAME_HEADERS.DEST_MAC[5] = TEMP_MAC[5]; build_headers(&FRAME_HEADERS); /////build_tlv(&FRAME_HEADERS, htons(TYPE_BROADCAST), htonl(VALUE_PRESENCE)); /////build_sub_tlv(&FRAME_HEADERS, htons(TYPE_APPLICATION_SUB_TLV), /////htonll(VALUE_DUMMY)); // Total size of the frame data (paylod size+headers), this excludes the // preamble & start frame delimiter, FCS and inter frame gap ///// TEST_PARAMS.F_SIZE_TOTAL = TEST_PARAMS.F_SIZE + FRAME_HEADERS.LENGTH; /* ******************************************************** SETTINGS SYNC */ sync_settings(&APP_PARAMS, &FRAME_HEADERS, &TEST_INTERFACE, &TEST_PARAMS, &MTU_TEST, &QM_TEST); // Pause to allow the RX host to process the sent settings sleep(1); // Rebuild the test frame headers in case any settings have been changed // by the TX host if (!APP_PARAMS.TX_MODE) build_headers(&FRAME_HEADERS); // Try to measure the TX to RX one way delay if (APP_PARAMS.TX_DELAY) delay_test(&APP_PARAMS, &FRAME_HEADERS, &TEST_INTERFACE, &TEST_PARAMS, &QM_TEST); /* ****************************************************** MAIN TEST PHASE */ if (MTU_TEST.ENABLED) { mtu_sweep_test(&APP_PARAMS, &FRAME_HEADERS, &TEST_INTERFACE, &TEST_PARAMS, &MTU_TEST); } else if (QM_TEST.ENABLED) { latency_test(&APP_PARAMS, &FRAME_HEADERS, &TEST_INTERFACE, &TEST_PARAMS, &QM_TEST); } else { speed_test(&APP_PARAMS, &FRAME_HEADERS, &TEST_INTERFACE, &TEST_PARAMS); } // End the testing loop if TX host if (APP_PARAMS.TX_MODE) TESTING=false; } printf("Leaving promiscuous mode\n"); strncpy(ethreq.ifr_name,TEST_INTERFACE.IF_NAME,IFNAMSIZ); if (ioctl(TEST_INTERFACE.SOCKET_FD,SIOCGIFFLAGS,ðreq) == -1) { printf("Error getting socket flags, leaving promiscuous mode failed!\n"); perror("ioctl() "); close(TEST_INTERFACE.SOCKET_FD); return EX_SOFTWARE; } ethreq.ifr_flags &= ~IFF_PROMISC; if (ioctl(TEST_INTERFACE.SOCKET_FD,SIOCSIFFLAGS,ðreq) == -1) { printf("Error setting socket flags, leaving promiscuous mode failed\n"); perror("ioctl() "); close(TEST_INTERFACE.SOCKET_FD); return EX_SOFTWARE; } close(TEST_INTERFACE.SOCKET_FD); free (QM_TEST.pDELAY_RESULTS); free (FRAME_HEADERS.RX_BUFFER); free (FRAME_HEADERS.TX_BUFFER); return EXIT_SUCCESS; }
void speed_test_rx(struct etherate *eth) { int16_t tx_ret = 0; int16_t rx_len = 0; // Wait for the first test frame to be received before starting the test loop uint8_t first_frame = false; while (!first_frame) { rx_len = recv(eth->intf.sock_fd, eth->frm.rx_buffer, eth->params.f_size_total, MSG_PEEK); // Check if this is an Etherate test frame if (ntohl(*eth->frm.rx_tlv_value) == VALUE_TEST_SUB_TLV && ntohs(*eth->frm.rx_sub_tlv_type) == TYPE_FRAMEINDEX) { first_frame = true; } else { // If the frame is not an Etherate frame it needs to be // "consumed" otherwise the next MSG_PEEK will show the // same frame: rx_len = recv(eth->intf.sock_fd, eth->frm.rx_buffer, eth->params.f_size_total, MSG_DONTWAIT); } } clock_gettime(CLOCK_MONOTONIC_RAW, ð->params.elapsed_time); // Rx test loop while (*eth->speed_test.testBase <= *eth->speed_test.testMax) { clock_gettime(CLOCK_MONOTONIC_RAW, ð->params.current_time); // If one second has passed if ((eth->params.current_time.tv_sec - eth->params.elapsed_time.tv_sec) >= 1) { eth->params.s_elapsed += 1; eth->speed_test.b_speed = (double)(eth->speed_test.b_rx - eth->speed_test.b_rx_prev) * 8 / 1000000; eth->speed_test.b_rx_prev = eth->speed_test.b_rx; eth->speed_test.f_speed = (eth->params.f_rx_count - eth->params.f_rx_count_prev); eth->params.f_rx_count_prev = eth->params.f_rx_count; printf("%" PRIu64 "\t\t%.2f\t\t%" PRIu64 "\t\t%" PRIu64 "\t\t%" PRIu64 "\n", eth->params.s_elapsed, eth->speed_test.b_speed, (eth->speed_test.b_rx / 1024) / 1024, (eth->speed_test.f_speed), eth->params.f_rx_count); if (eth->speed_test.b_speed > eth->speed_test.b_speed_max) eth->speed_test.b_speed_max = eth->speed_test.b_speed; if (eth->speed_test.f_speed > eth->speed_test.f_speed_max) eth->speed_test.f_speed_max = eth->speed_test.f_speed; eth->speed_test.b_speed_avg += eth->speed_test.b_speed; eth->speed_test.f_speed_avg += eth->speed_test.f_speed; eth->params.elapsed_time.tv_sec = eth->params.current_time.tv_sec; eth->params.elapsed_time.tv_nsec = eth->params.current_time.tv_nsec; } // Poll has been disabled in favour of a non-blocking recv (for now) rx_len = recv(eth->intf.sock_fd, eth->frm.rx_buffer, eth->params.f_size_total, MSG_DONTWAIT); if (rx_len > 0) { // Check if this is an Etherate test frame if (likely(ntohl(*eth->frm.rx_tlv_value) == VALUE_TEST_SUB_TLV && ntohs(*eth->frm.rx_sub_tlv_type) == TYPE_FRAMEINDEX)) { // If a VLAN tag is used Linux helpfully strips it off if (eth->frm.vlan_id != VLAN_ID_DEF) rx_len += 4; // Update test stats eth->params.f_rx_count += 1; eth->speed_test.b_rx += rx_len; // Record if the frame is in-order, early or late if (likely(ntohll(*eth->frm.rx_sub_tlv_value) == eth->params.f_rx_count)) { eth->params.f_rx_ontime += 1; } else if (ntohll(*eth->frm.rx_sub_tlv_value) > eth->params.f_rx_count) { eth->params.f_rx_early += 1; } else if (ntohll(*eth->frm.rx_sub_tlv_value) < eth->params.f_rx_count) { eth->params.f_rx_late += 1; } // If running in ACK mode Rx needs to ACK to Tx host if (eth->params.f_ack) { build_sub_tlv(ð->frm, htons(TYPE_ACKINDEX), *eth->frm.rx_sub_tlv_value); tx_ret = send(eth->intf.sock_fd, eth->frm.tx_buffer, eth->params.f_size_total, MSG_DONTWAIT); if (tx_ret > 0) { eth->params.f_tx_count += 1; } else { if (errno != EAGAIN || errno != EWOULDBLOCK) { perror("Speed test Tx error "); return; } } } } else { // Received a non-test frame eth->params.f_rx_other += 1; } // Check if Tx host has quit/died; if (unlikely(ntohl(*eth->frm.rx_tlv_value) == VALUE_DYINGGASP)) { printf("\nTx host has quit\n"); speed_test_results(eth); return; } } else { // rx_len <= 0 if (errno != EAGAIN || errno != EWOULDBLOCK) perror("Speed test Rx error "); } } // *testBase<=*testMax }
void speed_test_bps(struct etherate *eth) { int16_t tx_ret = 0; int16_t rx_len = 0; // Get clock times for the speed limit restriction and starting time clock_gettime(CLOCK_MONOTONIC_RAW, ð->params.elapsed_time); // Tx test loop while (*eth->speed_test.testBase <= *eth->speed_test.testMax) { clock_gettime(CLOCK_MONOTONIC_RAW, ð->params.current_time); // One second has passed if ((eth->params.current_time.tv_sec - eth->params.elapsed_time.tv_sec) >= 1) { eth->params.s_elapsed += 1; eth->speed_test.b_speed = (((double)eth->speed_test.b_tx-eth->speed_test.b_tx_prev) * 8) / 1000 / 1000; eth->speed_test.b_tx_prev = eth->speed_test.b_tx; eth->speed_test.f_speed = (eth->params.f_tx_count - eth->params.f_tx_count_prev); eth->params.f_tx_count_prev = eth->params.f_tx_count; printf("%" PRIu64 "\t\t%.2f\t\t%" PRIu64 "\t\t%" PRIu64"\t\t%" PRIu64 "\n", eth->params.s_elapsed, eth->speed_test.b_speed, (eth->speed_test.b_tx / 1024) / 1024, (eth->speed_test.f_speed), eth->params.f_tx_count); if (eth->speed_test.b_speed > eth->speed_test.b_speed_max) eth->speed_test.b_speed_max = eth->speed_test.b_speed; if (eth->speed_test.f_speed > eth->speed_test.f_speed_max) eth->speed_test.f_speed_max = eth->speed_test.f_speed; eth->speed_test.b_speed_avg += eth->speed_test.b_speed; eth->speed_test.f_speed_avg += eth->speed_test.f_speed; eth->params.elapsed_time.tv_sec = eth->params.current_time.tv_sec; eth->params.elapsed_time.tv_nsec = eth->params.current_time.tv_nsec; eth->speed_test.b_tx_speed_prev = 0; } else { // Poll has been disabled in favour of a non-blocking recv (for now) rx_len = recv(eth->intf.sock_fd, eth->frm.rx_buffer, eth->params.f_size_total, MSG_DONTWAIT); if (rx_len > 0) { // Running in ACK mode if (eth->params.f_ack) { if (ntohl(*eth->frm.rx_tlv_value) == VALUE_TEST_SUB_TLV && ntohs(*eth->frm.rx_sub_tlv_type) == TYPE_ACKINDEX) { eth->params.f_rx_count += 1; eth->params.f_waiting_ack = false; // Record if the frame is in-order, early or late if (likely(ntohll(*eth->frm.rx_sub_tlv_value) == eth->params.f_rx_count)) { eth->params.f_rx_ontime += 1; } else if (ntohll(*eth->frm.rx_sub_tlv_value) > eth->params.f_rx_count) { eth->params.f_rx_early += 1; } else if (ntohll(*eth->frm.rx_sub_tlv_value) < eth->params.f_rx_count) { eth->params.f_rx_late += 1; } } else if (ntohs(*eth->frm.rx_tlv_type) == TYPE_APPLICATION && ntohl(*eth->frm.rx_tlv_value) == VALUE_DYINGGASP) { printf("\nRx host has quit\n"); speed_test_results(eth); return; // Received a non-test frame } else { eth->params.f_rx_other += 1; } // Not running in ACK mode } else if (ntohs(*eth->frm.rx_tlv_type) == TYPE_APPLICATION && ntohll(*eth->frm.rx_tlv_value) == VALUE_DYINGGASP) { printf("\nRx host has quit\n"); speed_test_results(eth); return; // Received a non-test frame } else { eth->params.f_rx_other += 1; } } else { // rx_len <= 0 if (errno != EAGAIN || errno != EWOULDBLOCK) perror("Speed test Rx error "); } // If it hasn't been 1 second yet, send more test frames if (eth->params.f_waiting_ack == false) { // Check if sending another frame exceeds the max speed configured if ((eth->speed_test.b_tx_speed_prev + eth->params.f_size_total) <= eth->speed_test.b_tx_speed_max) { build_sub_tlv(ð->frm, htons(TYPE_FRAMEINDEX), htonll(eth->params.f_tx_count+1)); tx_ret = send(eth->intf.sock_fd, eth->frm.tx_buffer, eth->params.f_size_total, MSG_DONTWAIT); if (tx_ret > 0) { eth->params.f_tx_count += 1; eth->speed_test.b_tx += eth->params.f_size_total; eth->speed_test.b_tx_speed_prev += eth->params.f_size_total; if (eth->params.f_ack) eth->params.f_waiting_ack = true; } else { if (errno != EAGAIN || errno != EWOULDBLOCK) { perror("Speed test Tx error "); return; } } } } } // End of current_time.tv_sec - ts_elapsed_TIME.tv_sec } // *testBase<=*testMax }
void speed_test_pacing(struct etherate *eth) { int16_t tx_ret = 0; int16_t rx_len = 0; long double current_time = 0.0; long double last_tx = 0.0; // Get clock times for the speed limit restriction and starting time clock_gettime(CLOCK_MONOTONIC_RAW, ð->params.elapsed_time); // Initialise the "last tx interval" for the frame pacing feature before // current_time to ensure the first check of "time since last tx" passes clock_gettime(CLOCK_MONOTONIC_RAW, ð->params.last_tx); // Tx test loop while (*eth->speed_test.testBase <= *eth->speed_test.testMax) { clock_gettime(CLOCK_MONOTONIC_RAW, ð->params.current_time); // One second has passed if ((eth->params.current_time.tv_sec - eth->params.elapsed_time.tv_sec) >= 1) { eth->params.s_elapsed += 1; eth->speed_test.b_speed = (((double)eth->speed_test.b_tx-eth->speed_test.b_tx_prev) * 8) / 1000 / 1000; eth->speed_test.b_tx_prev = eth->speed_test.b_tx; eth->speed_test.f_speed = (eth->params.f_tx_count - eth->params.f_tx_count_prev); eth->params.f_tx_count_prev = eth->params.f_tx_count; printf("%" PRIu64 "\t\t%.2f\t\t%" PRIu64 "\t\t%" PRIu64"\t\t%" PRIu64 "\n", eth->params.s_elapsed, eth->speed_test.b_speed, (eth->speed_test.b_tx / 1024) / 1024, (eth->speed_test.f_speed), eth->params.f_tx_count); if (eth->speed_test.b_speed > eth->speed_test.b_speed_max) eth->speed_test.b_speed_max = eth->speed_test.b_speed; if (eth->speed_test.f_speed > eth->speed_test.f_speed_max) eth->speed_test.f_speed_max = eth->speed_test.f_speed; eth->speed_test.b_speed_avg += eth->speed_test.b_speed; eth->speed_test.f_speed_avg += eth->speed_test.f_speed; eth->params.elapsed_time.tv_sec = eth->params.current_time.tv_sec; eth->params.elapsed_time.tv_nsec = eth->params.current_time.tv_nsec; eth->speed_test.b_tx_speed_prev = 0; } else { // Poll has been disabled in favour of a non-blocking recv (for now) rx_len = recv(eth->intf.sock_fd, eth->frm.rx_buffer, eth->params.f_size_total, MSG_DONTWAIT); if (rx_len > 0) { // Running in ACK mode if (eth->params.f_ack) { if (ntohl(*eth->frm.rx_tlv_value) == VALUE_TEST_SUB_TLV && ntohs(*eth->frm.rx_sub_tlv_type) == TYPE_ACKINDEX) { eth->params.f_rx_count += 1; eth->params.f_waiting_ack = false; // Record if the frame is in-order, early or late if (likely(ntohll(*eth->frm.rx_sub_tlv_value) == eth->params.f_rx_count)) { eth->params.f_rx_ontime += 1; } else if (ntohll(*eth->frm.rx_sub_tlv_value) > eth->params.f_rx_count) { eth->params.f_rx_early += 1; } else if (ntohll(*eth->frm.rx_sub_tlv_value) < eth->params.f_rx_count) { eth->params.f_rx_late += 1; } } else if (ntohs(*eth->frm.rx_tlv_type) == TYPE_APPLICATION && ntohl(*eth->frm.rx_tlv_value) == VALUE_DYINGGASP) { printf("\nRx host has quit\n"); speed_test_results(eth); return; // Received a non-test frame } else { eth->params.f_rx_other += 1; } // Not running in ACK mode } else if (ntohs(*eth->frm.rx_tlv_type) == TYPE_APPLICATION && ntohll(*eth->frm.rx_tlv_value) == VALUE_DYINGGASP) { printf("\nRx host has quit\n"); speed_test_results(eth); return; // Received a non-test frame } else { eth->params.f_rx_other += 1; } } else { // rx_len <= 0 if (errno != EAGAIN || errno != EWOULDBLOCK) perror("Speed test Rx error "); } // If it hasn't been 1 second yet, send more test frames if (eth->params.f_waiting_ack == false) { // Convert the timespec values into a double. The tv_nsec // value loops around, it is not linearly incrementing indefinitely current_time = eth->params.current_time.tv_sec + ((double)eth->params.current_time.tv_nsec * 1e-9); last_tx = eth->params.last_tx.tv_sec + ((double)eth->params.last_tx.tv_nsec * 1e-9); if (current_time - last_tx > eth->params.f_tx_dly) { //if (pace_diff >= eth->params.f_tx_dly) { // Set the time of the last frame Tx first before // sending the frame, this was the frame Tx time is // included in the inter-frame delay without having // to know the link speed clock_gettime(CLOCK_MONOTONIC_RAW, ð->params.last_tx); build_sub_tlv(ð->frm, htons(TYPE_FRAMEINDEX), htonll(eth->params.f_tx_count+1)); tx_ret = send(eth->intf.sock_fd, eth->frm.tx_buffer, eth->params.f_size_total, MSG_DONTWAIT); if (tx_ret > 0) { eth->params.f_tx_count += 1; eth->speed_test.b_tx += eth->params.f_size_total; eth->speed_test.b_tx_speed_prev += eth->params.f_size_total; if (eth->params.f_ack) eth->params.f_waiting_ack = true; } else { if (errno != EAGAIN || errno != EWOULDBLOCK) { perror("Speed test Tx error "); return; } } } } } // End of current_time.tv_sec - ts_elapsed_TIME.tv_sec } // *testBase<=*testMax }