/* * XXUDP * * Copyright 2019 VMS Software, Inc. * * Sends UDP packets through a specified socket to a specified IP address. * If xxudp is running on the target (whether a remote device or a local * device on the the system), it receives each packet and either discards * it or loops it back to the sender. It also does a data compare on the * received data, if requested. * * Multiple initiators can be directed at a single target instance. * * To get help: * * xxudp -h * * Packet data format: * 31 ... 0 * +---------------------------------+ * | Packet Length | 0 * +---------------------------------+ * | Port number | 4 * +---------------------------------+ * | | 8 * +-- Sequence Number --+ * | | 12 * +---------------------------------+ * : Packet Data : 16 * : (16-bit counting pattern) : * : (or as supplied) : * +---------------------------------+ * * To build: * cc xxudp+xx_support+sys$library:sys$lib_c.tlb/lib * link xxudp */ #define _SOCKADDR_LEN 1 #include #include #include #include #include #include "socket.h" #include "xx_support.h" #define socklen_t uint #define BUF_SIZ sizeof(XX_BUFFER) #define PKT_MIN 18 /* pk_len + port_num + seq_num + 16-bits of packet data */ #define PKT_MAX 65507 /* If fragmentation supported */ #define HDRPLUSCRC (14+20+8+4) #define HDR_INTERVAL 10 #define XX_CMP_ERR 1 #define XX_SEQ_ERR 2 #define XX_LEN_ERR 4 typedef struct xx_buffer { int32_t pk_len; /* Packet length */ int32_t port_num; /* Port number */ uint64_t seq_num; /* Packet sequence number */ uint16_t pk_data[32768]; /* Packet data (counting pattern) */ } XX_BUFFER; /* Statistics */ uint64_t stat_xpk = 0, stat_xby = 0, stat_rpk = 0, stat_rby = 0; uint64_t stat_xfull = 0, stat_out = 0, stat_lost = 0; uint64_t stat_cmp_err = 0, stat_seq_err = 0, stat_len_err = 0; uint64_t last_stat_xpk = 0, last_stat_xby = 0, last_stat_rpk = 0, last_stat_rby = 0; uint64_t last_stat_xfull = 0, last_stat_lost = 0; uint64_t last_stat_cmp_err = 0, last_stat_seq_err = 0, last_stat_len_err = 0; uint64_t interval_stat_xpk, interval_stat_xby, interval_stat_rpk, interval_stat_rby; uint64_t interval_stat_xfull, interval_stat_lost; uint64_t interval_stat_cmp_err, interval_stat_seq_err, interval_stat_len_err; double bwx, bwr, bwpx, bwpr, bwxt, bwrt, bwpxt, bwprt; /* Send and receive context */ XX_BUFFER xx_buf; XX_BUFFER exp_buf; uint16_t pattern[65536]; uint64 pipeline = 0; int maxxmt = 0; uint64_t req_transmits = 0; int pkt_len = 1000, pkt_max, pkt_mod; uint64_t xmt_seq = 0; /* Sequence number of last packet sent */ uint64_t rcv_seq = 0; /* Sequence number of last packet received */ char dest_ip[40]; /* Socket information */ int sockfd, socktarget; struct sockaddr_in sockaddr, sockrecv; int sockport = 1234; int sobuflen_send = -1; /* Requesting default setting */ int sobuflen_recv = -1; /* Requesting default setting */ /* Process send errors */ int check_send_error (int err) { if (err < 0) { int tmp; unsigned int tmplen; if ((errno == EAGAIN) || (errno == ENOBUFS) || (errno == EWOULDBLOCK)) { stat_xfull++; // tmplen = sizeof(tmp); // getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &tmp, &tmplen); // sprintf(pline, "? xfull, sndbuf %d", tmp); // print_line(pline, pline); } else { sprintf(pline, "? Send error %d (%s)", errno, strerror(errno)); print_line(pline, pline); return SS$_FAIL; } } return SS$_NORMAL; } /* Process receive errors */ int check_recv_error (int err) { if ((err < 0) && (errno != EAGAIN) && (errno != EDEADLK) && (errno != EWOULDBLOCK)) { sprintf(pline, "? Recv error %d (%s)", errno, strerror(errno)); print_line(pline, pline); return SS$_FAIL; /* If remote side closed connection */ } else if (err == 0) return SS$_FAIL; /* Else informational error */ return SS$_NORMAL; } /* socket_setup_initiator (client): * * - Create the socket * - Connect to requested IP address and port number */ int socket_setup_initiator () { int option = 1; memset(&sockaddr, 0, sizeof(sockaddr)); sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons(sockport); sockaddr.sin_addr.s_addr = inet_addr(dest_ip); /* Create the socket */ if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { sprintf(pline, "? Failed to create socket %d (%s)", errno, strerror(errno)); print_line(pline, pline); return SS$_FAIL; } if ((sobuflen_send > 0) && setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sobuflen_send, sizeof(sobuflen_send))) { sprintf(pline, "? Failed to set socket sndbuf size, error %d (%s)", errno, strerror(errno)); print_line(pline, pline); sobuflen_send = -1; } if ((sobuflen_recv > 0) && setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &sobuflen_recv, sizeof(sobuflen_recv))) { sprintf(pline, "? Failed to set socket rcvbuf size, error %d (%s)", errno, strerror(errno)); print_line(pline, pline); sobuflen_recv = -1; } if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option))) { sprintf(pline, "? Failed to set socket reuseaddr, error %d (%s)", errno, strerror(errno)); print_line(pline, pline); } return SS$_NORMAL; } /* socket_setup_target (server): * * - Create the socket * - Listen to any IP address and requested port number */ int socket_setup_target () { int option = 1; memset(&sockaddr, 0, sizeof(sockaddr)); sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons(sockport); sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Create the socket */ if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { sprintf(pline, "? Failed to create socket %d (%s)", errno, strerror(errno)); print_line(pline, pline); return SS$_FAIL; } if ((sobuflen_send > 0) && setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sobuflen_send, sizeof(sobuflen_send))) { sprintf(pline, "? Failed to set socket sndbuf size, error %d (%s)", errno, strerror(errno)); print_line(pline, pline); sobuflen_send = -1; } if ((sobuflen_recv > 0) && setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &sobuflen_recv, sizeof(sobuflen_recv))) { sprintf(pline, "? Failed to set socket rcvbuf size, error %d (%s)", errno, strerror(errno)); print_line(pline, pline); sobuflen_recv = -1; } if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option))) { sprintf(pline, "? Failed to set socket reuseaddr, error %d (%s)", errno, strerror(errno)); print_line(pline, pline); } /* Listen to any host and requested port number */ if (bind(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) { sprintf(pline, "? Failed to bind socket %d (%s)", errno, strerror(errno)); print_line(pline, pline); return SS$_FAIL; } return SS$_NORMAL; } /* pk_echo - Echo packet data for the length specified */ void pk_echo (char *what, XX_BUFFER *buf, struct sockaddr_in * socket_address, int pkt_len) { int echo_len = (pkt_len > echo_size) ? echo_size : pkt_len; uint32_t *pk = (uint32_t *)buf; struct timeval echo_time; uint8_t *ipaddr = (uint8_t *)&socket_address->sin_addr.s_addr; echo_num--; /* Print packet context */ gettimeofday(&echo_time, NULL); sprintf(pline, "%lld.%06lld %s %d.%d.%d.%d, len %d", echo_time.tv_sec, echo_time.tv_usec, what, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3], pkt_len); print_line(pline, pline); /* Print packet contents if specified */ pk_dump_packet(pk, echo_len); } /* pk_comp - Do data compare including sequence number check */ int pk_comp (char *what, XX_BUFFER *buf, struct sockaddr_in *socket_address, int pkt_len, uint64_t exp_seq) { int pk_data_len; int err = 0; /* Get length of data for data compare and check for length mismatch */ pk_data_len = buf->pk_len - (BUF_SIZ - sizeof(buf->pk_data)); if (buf->pk_len != pkt_len) { sprintf(pline, "%s: Length mismatch, expected pklen %d, actual %d", what, buf->pk_len, pkt_len); print_line(pline, pline); pk_data_len = pkt_len - (sizeof(*buf) - sizeof(buf->pk_data)); err |= XX_LEN_ERR; } /* Check for sequence error */ if (exp_seq > buf->seq_num) { sprintf(pline, "%s: Sequence error, expected %llu, actual %llu", what, exp_seq, buf->seq_num); print_line(pline, pline); err |= XX_SEQ_ERR; } /* Do data compare */ if (buf->port_num != sockport) { sprintf(pline, "%s: Socket number mismatch, expected %d, actual %d", what, buf->port_num, sockport); print_line(pline, pline); err |= XX_CMP_ERR; } else if (pk_data_len > 0) { char *pk_data = (char *)&buf->pk_data[0]; char *pk_pattern = (char *)&pattern[buf->pk_data[0]]; #if DEBUG_CERR /* Check for debug forcing a compare error */ if (force_cerr_flag && (stat_rpk == force_cerr_flag)) { pk_data[33] ^= 1; force_cerr_flag = 0; } #endif if (memcmp((void *)pk_data, (void *)pk_pattern, pk_data_len)) { int err_offset; for (err_offset=0; err_offsetpk_data[0]], pk_data_len); exp_buf.pk_len = pkt_len; exp_buf.port_num = sockport; exp_buf.seq_num = exp_seq; pk_dump("Actual:", (uint32_t *)buf, pkt_len); pk_dump("Expected:", (uint32_t *)&exp_buf, pkt_len); } return err; } /* Print_banner */ void print_banner (char *test) { int i; if (stats_interval == 1000000000) return; /* Wait for start of next second */ cur_time = time(0); while (cur_time == time(0)); /* Get times and set up time for periodic display */ cur_time = time(0); stat_time = cur_time + 1; end_time = cur_time + test_duration; if (end_time < cur_time) end_time = 0xFFFFFFFF; gettimeofday(&begin_stats_time, NULL); last_stats_time = begin_stats_time; cur_time_str = ctime(&cur_time); cur_time_str[24] = 0; strcpy(pline, "------------------------------------------------------------"); print_line(pline, pline); print_line(test, test); print_line(pline, pline); if (is_target) { sprintf(pline, "Receiving on port %d", sockport); print_line(pline, pline); } else { char req_transmits_str[32]; uint8_t *ipaddr = (uint8_t *)&sockaddr.sin_addr.s_addr; sprintf(pline, "Sending from port %d to %d.%d.%d.%d", sockport, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); print_line(pline, pline); if (rand_flag) sprintf(pline, "Packet size random from %d to %d bytes (excl hdrs (14+20+8) and CRC bytes)", PKT_MIN, pkt_max); else sprintf(pline, "Packet size %d bytes (excl hdrs(14+20+8) and CRC bytes)", pkt_len); print_line(pline, pline); if (req_transmits == 0x7FFFFFFFFFFFFFFF) strcpy(req_transmits_str, "gobs"); else sprintf(req_transmits_str, "%llu", req_transmits); if (maxxmt) sprintf(pline, "Packets to send %s, pipeline %llu packets (%d mps)", req_transmits_str, pipeline, maxxmt); else sprintf(pline, "Packets to send %s, pipeline %llu packets", req_transmits_str, pipeline); print_line(pline, pline); } if (sobuflen_send < 0) i = sprintf(pline, "sobuflen_send default, "); else i = sprintf(pline, "sobuflen_send %d, ", sobuflen_send); if (sobuflen_recv < 0) sprintf(&pline[i], "sobuflen_recv default"); else sprintf(&pline[i], "sobuflen_recv %d", sobuflen_recv); print_line(pline, pline); if (echo_num && echo_size) { sprintf(pline, "Echo %d bytes of the next %d packets", echo_size, echo_num); print_line(pline, pline); } else if (echo_num) { sprintf(pline, "Echo packet context of the next %d packets", echo_num); print_line(pline, pline); } sprintf(pline, "Dumped packet data is %s", (swap_flag) ? "left-to-right" : "right-to-left"); print_line(pline, pline); sprintf(pline, "Data compare %s", (compare_flag) ? "enabled" : "disabled"); print_line(pline, pline); if (discard_flag) { sprintf(pline, "Receives discarded, not looped back"); print_line(pline, pline); } if (is_initiator) { sprintf(pline, "Bursting %d%s packets", burst_num, rand_burst_flag ? "(random)" : ""); print_line(pline, pline); } if (test_duration != 0x7FFFFFFF) { sprintf(pline, "Test duration %d seconds", test_duration); print_line(pline, pline); } sprintf(pline, "Writing results to %s", log_name); print_line(pline, pline); } /* Calculate stats for printing */ void calc_stats () { /* Get current time and calculate elapsed usec */ cur_time_str = ctime(&cur_time); cur_time_str[24] = 0; gettimeofday(&cur_stats_time, NULL); interval_usec = get_delta(&cur_stats_time, &last_stats_time); total_usec = get_delta(&cur_stats_time, &begin_stats_time); /* Calculate outstanding and lost */ stat_out = xmt_seq - rcv_seq; stat_lost = stat_xpk - stat_rpk - stat_out; /* Calculate interval stats */ interval_stat_xpk = stat_xpk - last_stat_xpk; interval_stat_xby = stat_xby - last_stat_xby; interval_stat_rpk = stat_rpk - last_stat_rpk; interval_stat_rby = stat_rby - last_stat_rby; interval_stat_xfull = stat_xfull - last_stat_xfull; interval_stat_lost = (stat_lost > last_stat_lost) ? stat_lost - last_stat_lost : 0; interval_stat_cmp_err = stat_cmp_err - last_stat_cmp_err; interval_stat_seq_err = stat_seq_err - last_stat_seq_err; interval_stat_len_err = stat_len_err - last_stat_len_err; /* Calculate per second values */ bwx = (interval_stat_xby + interval_stat_xpk * HDRPLUSCRC) * 8.0 / interval_usec; bwr = (interval_stat_rby + interval_stat_rpk * HDRPLUSCRC) * 8.0 / interval_usec; bwpx = interval_stat_xpk * 1000000.0 / interval_usec; bwpr = interval_stat_rpk * 1000000.0 / interval_usec; if (((nprint + 1) % HDR_INTERVAL) == 0) { bwxt = (stat_xby + stat_xpk * HDRPLUSCRC) * 8.0 / total_usec; bwrt = (stat_rby + stat_rpk * HDRPLUSCRC) * 8.0 / total_usec; bwpxt = stat_xpk * 1000000.0 / total_usec; bwprt = stat_rpk * 1000000.0 / total_usec; } /* Update last interval values */ last_stat_xpk = stat_xpk; last_stat_xby = stat_xby; last_stat_rpk = stat_rpk; last_stat_rby = stat_rby; last_stat_xfull = stat_xfull; last_stat_lost = stat_lost; last_stat_cmp_err = stat_cmp_err; last_stat_seq_err = stat_seq_err; last_stat_len_err = stat_len_err; last_stats_time = cur_stats_time; } /* Calculate final stats for printing */ void calc_final_stats () { /* Get current time and calculate elapsed usec */ cur_time_str = ctime(&cur_time); cur_time_str[24] = 0; gettimeofday(&cur_stats_time, NULL); total_usec = get_delta(&cur_stats_time, &begin_stats_time); /* Calculate outstanding and lost */ stat_out = xmt_seq - rcv_seq; stat_lost = stat_xpk - stat_rpk - stat_out; /* Calculate per second values */ bwxt = (stat_xby + stat_xpk * HDRPLUSCRC) * 8.0 / total_usec; bwrt = (stat_rby + stat_rpk * HDRPLUSCRC) * 8.0 / total_usec; bwpxt = stat_xpk * 1000000.0 / total_usec; bwprt = stat_rpk * 1000000.0 / total_usec; } /* Check for ok to transmit, according to pk/sec max (1 if ok) */ int oktoxmt () { double xmtpersec; /* Get current time and calculate elapsed usec */ gettimeofday(&cur_stats_time, NULL); total_usec = get_delta(&cur_stats_time, &begin_stats_time); xmtpersec = stat_xpk / (total_usec / 1000000.0); return xmtpersec < maxxmt; } /* Print stats for the Initiator Test and Initiator/Target Test */ void print_initiator_header (uint64_t total_usec) { if (stats_interval == 1000000000) return; if (total_usec) { total_sec = total_usec / 1000000; total_usec = total_usec - total_sec * 1000000; sprintf(pline, "Elapsed time (seconds): %lld.%06lld", total_sec, total_usec); print_vline(""); print_vline(pline); } if (compare_flag) sprintf(pline, " xpk xby rpk rby xfull out lost cerr serr lerr " "xbs rbs mbs xps rps pps"); else sprintf(pline, " xpk xby rpk rby xfull out lost " "xbs rbs mbs xps rps pps"); print_vline(""); print_vline(pline); } void print_initiator_stats () { char line[240]; if (stats_interval == 1000000000) return; if ((stat_xpk == last_stat_xpk) && (stat_rpk == last_stat_rpk)) return; calc_stats(); if (nprint == 0) print_initiator_header(0); cvt_value(interval_stat_xpk, &line[0]); cvt_value(interval_stat_xby, &line[8]); cvt_value(interval_stat_rpk, &line[16]); cvt_value(interval_stat_rby, &line[24]); cvt_value(interval_stat_xfull, &line[32]); cvt_value(stat_out, &line[40]); cvt_value(interval_stat_lost, &line[48]); if (compare_flag) { cvt_value(interval_stat_cmp_err, &line[56]); cvt_value(interval_stat_seq_err, &line[64]); cvt_value(interval_stat_len_err, &line[72]); } sprintf(pline, "%s%6.0f%6.0f%6.0f%8.0f%8.0f%8.0f", line, bwx, bwr, bwx+bwr, bwpx, bwpr, bwpx+bwpr); if (verbose_flag) print_line(pline, pline); else { sprintf(line, "xxxxxxxx BytesSnt, xxxxxxxx BytesRcv, %8.0f Total MBits/Sec", bwx+bwr); cvt_value(stat_xby, &line[0]); line[8] = ' '; cvt_value(stat_rby, &line[19]); line[27] = ' '; print_line(line, pline); } if ((((nprint + 1) % HDR_INTERVAL) == 0) && nprint) { print_initiator_header(get_delta(&cur_stats_time, &begin_stats_time)); if (nprint) { cvt_value(stat_xpk, &line[0]); cvt_value(stat_xby, &line[8]); cvt_value(stat_rpk, &line[16]); cvt_value(stat_rby, &line[24]); cvt_value(stat_xfull, &line[32]); cvt_value(stat_out, &line[40]); cvt_value(stat_lost, &line[48]); if (compare_flag) { cvt_value(stat_cmp_err, &line[56]); cvt_value(stat_seq_err, &line[64]); cvt_value(stat_len_err, &line[72]); } sprintf(pline, "%s%6.0f%6.0f%6.0f%8.0f%8.0f%8.0f", line, bwxt, bwrt, bwxt+bwrt, bwpxt, bwprt, bwpxt+bwprt); print_vline(pline); print_initiator_header(0); } } nprint++; } /* Print last stats for the Initiator Test */ void print_final_initiator_stats () { char line[240]; if (stats_interval == 1000000000) return; calc_final_stats(); print_initiator_header(0); cvt_value(stat_xpk, &line[0]); cvt_value(stat_xby, &line[8]); cvt_value(stat_rpk, &line[16]); cvt_value(stat_rby, &line[24]); cvt_value(stat_xfull, &line[32]); cvt_value(stat_out, &line[40]); cvt_value(stat_lost, &line[48]); if (compare_flag) { cvt_value(stat_cmp_err, &line[56]); cvt_value(stat_seq_err, &line[64]); cvt_value(stat_len_err, &line[72]); } sprintf(pline, "%s%6.0f%6.0f%6.0f%8.0f%8.0f%8.0f", line, bwxt, bwrt, bwxt+bwrt, bwpxt, bwprt, bwpxt+bwprt); print_vline(pline); print_vline(""); total_sec = total_usec / 1000000; total_usec = total_usec - total_sec * 1000000; sprintf(pline, "Elapsed time (seconds): %lld.%06lld", total_sec, total_usec); print_vline(pline); if ((pipeline == 1) && is_initiator && (bwpxt || bwprt)) { sprintf(pline, "Average latency (one direction) %8.2f usec", 1000000.0 / (bwpxt+bwprt)); print_vline(pline); } } /* Print stats for the Target Test */ void print_target_header (uint64_t total_usec) { if (stats_interval == 1000000000) return; if (total_usec) { total_sec = total_usec / 1000000; total_usec = total_usec - total_sec * 1000000; sprintf(pline, "Elapsed time (seconds): %lld.%06lld", total_sec, total_usec); print_vline(""); print_vline(pline); } if (compare_flag) sprintf(pline, " xpk xby rpk rby cerr lerr xbs rbs mbs xps rps pps"); else sprintf(pline, " xpk xby rpk rby xbs rbs mbs xps rps pps"); print_vline(""); print_vline(pline); } void print_target_stats () { char line[240]; if (stats_interval == 1000000000) return; if ((stat_xpk == last_stat_xpk) && (stat_rpk == last_stat_rpk)) return; calc_stats(); if (nprint == 0) print_target_header(0); cvt_value(interval_stat_xpk, &line[0]); cvt_value(interval_stat_xby, &line[8]); cvt_value(interval_stat_rpk, &line[16]); cvt_value(interval_stat_rby, &line[24]); if (compare_flag) { cvt_value(interval_stat_cmp_err, &line[32]); cvt_value(interval_stat_len_err, &line[40]); } sprintf(pline, "%s%6.0f%6.0f%6.0f%8.0f%8.0f%8.0f", line, bwx, bwr, bwx+bwr, bwpx, bwpr, bwpx+bwpr); if (verbose_flag) print_line(pline, pline); else { sprintf(line, "xxxxxxxx BytesSnt, xxxxxxxx BytesRcv, %8.0f Total MBits/Sec", bwx+bwr); cvt_value(stat_xby, &line[0]); line[8] = ' '; cvt_value(stat_rby, &line[19]); line[27] = ' '; print_line(line, pline); } if ((((nprint + 1) % HDR_INTERVAL) == 0) && nprint) { print_target_header(get_delta(&cur_stats_time, &begin_stats_time)); if (nprint) { cvt_value(stat_xpk, &line[0]); cvt_value(stat_xby, &line[8]); cvt_value(stat_rpk, &line[16]); cvt_value(stat_rby, &line[24]); if (compare_flag) { cvt_value(stat_cmp_err, &line[32]); cvt_value(stat_len_err, &line[40]); } sprintf(pline, "%s%6.0f%6.0f%6.0f%8.0f%8.0f%8.0f", line, bwxt, bwrt, bwxt+bwrt, bwpxt, bwprt, bwpxt+bwprt); print_vline(pline); print_target_header(0); } } nprint++; } /* Print last stats for the Target Test */ void print_final_target_stats () { char line[240]; if (stats_interval == 1000000000) return; calc_final_stats(); print_target_header(0); cvt_value(stat_xpk, &line[0]); cvt_value(stat_xby, &line[8]); cvt_value(stat_rpk, &line[16]); cvt_value(stat_rby, &line[24]); if (compare_flag) { cvt_value(stat_cmp_err, &line[32]); cvt_value(stat_len_err, &line[40]); } sprintf(pline, "%s%6.0f%6.0f%6.0f%8.0f%8.0f%8.0f", line, bwxt, bwrt, bwxt+bwrt, bwpxt, bwprt, bwpxt+bwprt); print_vline(pline); print_vline(""); total_sec = total_usec / 1000000; total_usec = total_usec - total_sec * 1000000; sprintf(pline, "Elapsed time (seconds): %lld.%06lld", total_sec, total_usec); print_vline(pline); } /* do_target_test: * * while (1) * - Receive on target socket * - Data compare if requested * - Loop back if requested * - Print stats periodically */ void do_target_test () { int rx_len, tx_len, addr_len, err; /* Set up target socket */ if (socket_setup_target() == SS$_FAIL) { sprintf(pline, "? Failed to set up target socket"); print_line(pline, pline); exit(SS$_FAIL); } /* Display test parameters */ print_banner("Target Test"); /* Loop forever, receiving */ while (1) { addr_len = sizeof(sockaddr); rx_len = recvfrom(sockfd, (void *)&xx_buf, BUF_SIZ, 0, (struct sockaddr *)&sockrecv, (socklen_t *)&addr_len); /* If data received */ if (rx_len > 0) { if (echo_num) pk_echo("Rcv from", &xx_buf, &sockrecv, rx_len); if (compare_flag && (err = pk_comp("Rcv", &xx_buf, &sockrecv, rx_len, 0))) { if (err & XX_CMP_ERR) stat_cmp_err++; if (err & XX_LEN_ERR) stat_len_err++; } stat_rpk++; stat_rby += rx_len; req_transmits--; xmt_seq = rcv_seq = 0; /* Target test doesn't check sequence numbers */ /* Loop back the packet if enabled */ if (discard_flag == FALSE) { tx_len = sendto(sockfd, (void *)&xx_buf, rx_len, 0, (struct sockaddr *)&sockrecv, sizeof(struct sockaddr)); /* If successful send */ if (tx_len > 0) { if (echo_num) pk_echo("Xmt", &xx_buf, &sockrecv, tx_len); stat_xpk++; stat_xby += tx_len; if (tx_len != rx_len) { sprintf(pline, "Tried to send %d bytes, but %d got sent", rx_len, tx_len); print_line(pline, pline); } /* Socket full or other error */ } else if (check_send_error(tx_len) != SS$_NORMAL) break; } /* Error or other side closed the connection (rx_len was zero) */ } else if (check_recv_error(rx_len) != SS$_NORMAL) break; /* Print periodic stats */ cur_time = time(0); if ((req_transmits == 0) || (end_time <= cur_time)) goto done; if ((stat_time <= cur_time) || override_interval_flag) { override_interval_flag = 0; print_target_stats(); stat_time = cur_time + stats_interval; } } done: /* Do final stats */ print_target_stats(); print_final_target_stats(); close(socktarget); } /* do_initiator_test: * * while (1) * - Send packet on initiator socket to target address * - While receives available: * - Receive packet on initiator socket * - Data compare if requested * - Print stats periodically */ void do_initiator_test () { int rx_len, tx_len, addr_len, err, burst; /* Set up initiator socket */ if (socket_setup_initiator() == SS$_FAIL) { sprintf(pline, "? Failed to set up initiator socket"); print_line(pline, pline); exit(SS$_FAIL); } /* Display test parameters */ print_banner("Initiator Test"); /* Loop till no more packets to send */ while (req_transmits) { if ((maxxmt == 0) || oktoxmt()) { GEN_BURST; for (burst=0; burst 0) { if (echo_num) pk_echo("Xmt", &xx_buf, &sockaddr, tx_len); stat_xpk++; stat_xby += tx_len; xmt_seq++; req_transmits--; if (tx_len != pkt_len) { sprintf(pline, "? Tried to send %d bytes, but %d got sent", pkt_len, tx_len); print_line(pline, pline); } /* Socket full or other error */ } else if (check_send_error(tx_len) != SS$_NORMAL) goto done; } } /* Receive until outstanding less than pipeline */ if (discard_flag == FALSE) { while (1) { addr_len = sizeof(sockrecv); rx_len = recvfrom(sockfd, (void *)&xx_buf, BUF_SIZ, MSG_DONTWAIT, (struct sockaddr *)&sockrecv, (socklen_t *)&addr_len); /* If data received */ if (rx_len > 0) { if (echo_num) pk_echo("Rcv", &xx_buf, &sockrecv, rx_len); /* Check for compare needed */ err = 0; if (compare_flag) { err = pk_comp("Rcv", &xx_buf, &sockrecv, rx_len, rcv_seq + 1); if (err) { if (err & XX_CMP_ERR) stat_cmp_err++; if (err & XX_SEQ_ERR) stat_seq_err++; if (err & XX_LEN_ERR) stat_len_err++; } } stat_rpk++; stat_rby += rx_len; rcv_seq = xx_buf.seq_num; /* Error or other side closed the connection (rx_len was zero) */ } else if (check_recv_error(rx_len) != SS$_NORMAL) { goto done; /* Nothing received, but check pipeline if need to keep checking */ } else { /* If pipeline unlimited, just exit this receive loop and do next tranmsit */ if (pipeline == 0) break; /* If pipeline not full, exit to keep transmitting */ if (xmt_seq - rcv_seq < pipeline) break; /* * Otherwise, keep looking for receives until the end of the second (or when * outstanding drops below pipeline, checked at beginning of while loop) */ tmp_time = time(0); if (cur_time != tmp_time) { cur_time = tmp_time; break; } } } } /* Print periodic stats */ cur_time = time(0); if ((req_transmits == 0) || (end_time <= cur_time)) goto done; if ((stat_time <= cur_time) || override_interval_flag) { override_interval_flag = 0; print_initiator_stats(); stat_time = cur_time + stats_interval; } } done: print_final_initiator_stats(); for (int i=0; i<10; i++) { if (shutdown(sockfd, SHUT_RDWR) != 0) { sprintf(pline, "? Failed to shutdown socket %d (%s)", errno, strerror(errno)); print_vline(pline); } else break; } close(sockfd); } /* print_help */ void print_help () { printf( "Arguments are:\n"); printf( " -p port number Port number to use (default 1234)\n" " -t d1.d2.d3.d4 IP address of the target system (running the target(server))\n" " -l pktlen Packet length to transmit (default 1000)(limit %d bytes)\n" " (doesn't include IP, UDP headers, or CRC)\n" " -r Random packet size from %d bytes to pktlen\n" " -n numpkts Number of packets to transmit (unsigned 32-bits)\n" " (can be followed by k,m,g,t)(default 0)\n" " (0 is 0x7FFFFFFFFFFFFFFF, also target default)\n" " -o numpkts Pipeline, maximum outstanding packets (default 0, no limit)\n" " -m xmtpersec Maximum transmit packets per second (0 if no limit)\n" " (only where target IP address is specified)\n" " -c Do data compare on receive\n" " -d Discard packets (don't loop back)\n" " -e echosize Echo packets, displaying specified amount\n" " (0 displays packet context only)\n" " -s Display packet data left-to-right\n" " -f numpkts Number of packets to echo (default all)\n" " -i seconds Interval between stats display (default 1 if verbose, 10 if not)\n" " (override by doing Ctrl-T)\n" " -w seconds When to stop, after w seconds (default 0, never)\n" " -b numpkts Number of packets in a burst (default 1)\n" " -j sobuflen_send Socket buffer send size (override default)\n" " -k sobuflen_recv Socket buffer receive size (override default)\n" " -v Verbose mode (default if any options other than -p, -t specified)\n" " -u filespec Log to filespec (default SYS$LOGIN:XXUDP_nodename.LOG)\n" " -h Display help\n\n", PKT_MAX, PKT_MIN); printf( "To run, define xxudp as a foreign command (xxudp :== $disk:[dir]xxudp.exe)\n\n"); printf( "Example, initiator, target using default port number 1234:\n" " On the target system: xxudp\n" " On the initiator system: xxudp -t 10.10.41.122\n\n"); printf( "Example, initiator, target using default port number 1234, verbose mode:\n" " On the target system: xxudp -v\n" " On the initiator system: xxudp -v -t 10.10.41.122\n" " or with non-standard parameters, for example:\n" " xxudp -i 1 -t 10.10.41.122\n\n"); printf( "Example, initiator, target using port number 12345:\n" " On the target system: xxudp -p 12345\n" " On the initiator system: xxudp -p 12345 -t 10.10.41.122\n\n"); printf( "Example, initiator, target using default port number 1234, but target running\n" "as a detached process so you can start it up and run the initiator over and over:\n" " On the target system: run/detached xxudp\n" " On the initiator system: xxudp -t 10.10.41.122\n\n"); printf( "Example, default settings, but running for just 1gbyte or 10 seconds max\n" " On the target system: xxudp\n" " On the initiator system: xxudp -t 10.10.41.122 -n 1000000000 -w 10\n\n"); printf( "Control-T for immediate stats\n\n"); printf( "To increase socket buffer size:\n" " HPE TCP/IP:\n" " $ TCPIP sysconfig -r socket sb_max=10000000\n" " $ TCPIP sysconfig -Q socket\n\n" " VSI TCP/IP:\n" " $ ip set/kernel sb_max 10000000\n" " $ ip set/kernel tcp_sendspace 4000000\n" " $ ip set/kernel tcp_recvspace 4000000\n" " $ ip set/kernel tcp_maxburst 32\n"); exit(SS$_NORMAL); } /* main: * * - Get arguments * - Open the socket * - Initiate initiator or target test */ int main (int argc, char *argv[]) { int i; char c; extern char *optarg; extern int optind, optopt; int getopt (int argc, char *const argv[], const char *optstring); uint64_t j; #define OTHER_ARGS "j:k:l:p:t:x:" /* Initialize data pattern buffer for data compares */ for (i=0; i PKT_MAX) pkt_len = PKT_MAX; if (pkt_len < PKT_MIN) pkt_len = PKT_MIN; break; case 'p': sockport = atoi(optarg); break; case 't': { sscanf(optarg, "%s", &dest_ip); is_initiator = TRUE; is_target = FALSE; break; } case 'x': { uint32_t *p = (uint32_t *)pattern; int dpattern; char pat[32]; verbose_flag = TRUE; strncpy(pat, optarg, sizeof(pat) - 1); pat[sizeof(pat) - 1] = 0; if ((pat[0] == '0') && ((pat[1] == 'x') || (pat[1] == 'X'))) dpattern = strtol(&pat[2], 0, 16); else dpattern = strtol(pat, 0, 10); /* Initialize data pattern buffer for data compares */ for (i=0; i