"Fossies" - the Fresh Open Source Software Archive

Member "bwping-1.17/src/bwping.c" (26 Apr 2020, 21600 Bytes) of package /linux/privat/bwping-1.17.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "bwping.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.16_vs_1.17.

    1 #include "../include/features.h"
    2 
    3 #ifdef HAVE_CONFIG_H
    4 #include <config.h>
    5 #endif
    6 
    7 #include <sys/time.h>
    8 #include <sys/types.h>
    9 #include <sys/select.h>
   10 #include <sys/socket.h>
   11 
   12 #include <stdbool.h>
   13 #include <stdint.h>
   14 #include <stddef.h>
   15 #include <inttypes.h>
   16 #include <stdio.h>
   17 #include <unistd.h>
   18 #include <stdlib.h>
   19 #include <sysexits.h>
   20 #include <time.h>
   21 #include <errno.h>
   22 #include <string.h>
   23 #include <libgen.h>
   24 
   25 #include <netinet/in.h>
   26 #include <netinet/in_systm.h>
   27 #include <netinet/ip.h>
   28 #include <netinet/ip6.h>
   29 #include <netinet/ip_icmp.h>
   30 #ifdef HAVE_NETINET_ICMP6_H
   31 #include <netinet/icmp6.h>
   32 #endif
   33 
   34 #ifdef __CYGWIN__
   35 #include "../include/cygwin.h"
   36 #endif
   37 
   38 #include <netdb.h>
   39 
   40 static const size_t   MAX_IPV4_HDR_SIZE       = 60;
   41 static const uint32_t CALIBRATION_CYCLES      = 100,
   42                       PKT_BURST_PRECISION     = 1000,
   43                       BUF_SIZE_RESERVE_FACTOR = 10;
   44 
   45 static char *prog_name;
   46 
   47 static void get_time(struct timespec *ts)
   48 {
   49 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME)
   50 #if defined(CLOCK_HIGHRES)
   51     const clockid_t id = CLOCK_HIGHRES;
   52 #elif defined(CLOCK_MONOTONIC_RAW)
   53     const clockid_t id = CLOCK_MONOTONIC_RAW;
   54 #elif defined(CLOCK_MONOTONIC)
   55     const clockid_t id = CLOCK_MONOTONIC;
   56 #else
   57     const clockid_t id = CLOCK_REALTIME;
   58 #endif
   59 
   60     if (clock_gettime(id, ts) < 0) {
   61         fprintf(stderr, "%s: clock_gettime() failed: %s\n", prog_name, strerror(errno));
   62 
   63         ts->tv_sec  = 0;
   64         ts->tv_nsec = 0;
   65     }
   66 #else /* HAVE_CLOCK_GETTIME */
   67     struct timeval tv;
   68 
   69     if (gettimeofday(&tv, NULL) < 0) {
   70         fprintf(stderr, "%s: gettimeofday() failed: %s\n", prog_name, strerror(errno));
   71 
   72         ts->tv_sec  = 0;
   73         ts->tv_nsec = 0;
   74     } else {
   75         ts->tv_sec  = tv.tv_sec;
   76         ts->tv_nsec = tv.tv_usec * 1000;
   77     }
   78 #endif /* HAVE_CLOCK_GETTIME */
   79 }
   80 
   81 static int64_t ts_sub(const struct timespec *ts1, const struct timespec *ts2)
   82 {
   83     return ((int64_t)ts1->tv_sec - (int64_t)ts2->tv_sec) * 1000000 + (ts1->tv_nsec - ts2->tv_nsec) / 1000;
   84 }
   85 
   86 static uint16_t cksum(const void *packet, size_t pkt_size)
   87 {
   88     uint16_t buf[IP_MAXPACKET] = {0};
   89 
   90     memcpy(buf, packet, pkt_size);
   91 
   92     uint32_t sum = 0;
   93 
   94     for (size_t i = 0; i < pkt_size / 2 + pkt_size % 2; i++) {
   95         sum += buf[i];
   96     }
   97 
   98     sum  = (sum >> 16) + (sum & 0xFFFF);
   99     sum += (sum >> 16);
  100 
  101     return ~sum;
  102 }
  103 
  104 static int64_t calibrate_timer(void)
  105 {
  106     int64_t sum = 0;
  107 
  108     for (uint32_t i = 0; i < CALIBRATION_CYCLES; i++) {
  109         struct timespec begin, end;
  110 
  111         int n = -1;
  112 
  113         while (n < 0) {
  114             get_time(&begin);
  115 
  116             struct timeval timeout;
  117 
  118             timeout.tv_sec  = 0;
  119             timeout.tv_usec = 10;
  120 
  121             n = select(0, NULL, NULL, NULL, &timeout);
  122         }
  123 
  124         get_time(&end);
  125 
  126         sum += ts_sub(&end, &begin);
  127     }
  128 
  129     return sum / CALIBRATION_CYCLES;
  130 }
  131 
  132 static void send_ping4(int sock, const struct addrinfo *to_ai, size_t pkt_size, uint16_t ident, bool first_in_burst, uint32_t *transmitted_number, uint64_t *transmitted_volume)
  133 {
  134     char packet[IP_MAXPACKET] = {0};
  135 
  136     struct icmp icmp4 = {.icmp_type = ICMP_ECHO, .icmp_code = 0, .icmp_cksum = 0, .icmp_id = ident, .icmp_seq = htons(*transmitted_number)};
  137 
  138     memcpy(packet, &icmp4, sizeof(icmp4));
  139 
  140     if (first_in_burst) {
  141         struct timespec pkt_time;
  142 
  143         get_time(&pkt_time);
  144 
  145         memcpy(&packet[sizeof(icmp4)], &pkt_time, sizeof(pkt_time));
  146     }
  147 
  148     icmp4.icmp_cksum = cksum(packet, pkt_size);
  149 
  150     memcpy(&packet[offsetof(struct icmp, icmp_cksum)], &icmp4.icmp_cksum, sizeof(icmp4.icmp_cksum));
  151 
  152     ssize_t res = sendto(sock, packet, pkt_size, 0, to_ai->ai_addr, to_ai->ai_addrlen);
  153 
  154     if (res < 0) {
  155         fprintf(stderr, "%s: sendto() failed: %s\n", prog_name, strerror(errno));
  156     } else if (res != (ssize_t)pkt_size) {
  157         fprintf(stderr, "%s: partial write: packet size: %zu, sent: %zd\n", prog_name, pkt_size, res);
  158     }
  159 
  160     (*transmitted_number)++;
  161     (*transmitted_volume) += pkt_size;
  162 }
  163 
  164 static void send_ping6(int sock, const struct addrinfo *to_ai, size_t pkt_size, uint16_t ident, bool first_in_burst, uint32_t *transmitted_number, uint64_t *transmitted_volume)
  165 {
  166     char packet[IP_MAXPACKET] = {0};
  167 
  168     struct icmp6_hdr icmp6 = {.icmp6_type = ICMP6_ECHO_REQUEST, .icmp6_code = 0, .icmp6_cksum = 0, .icmp6_id = ident, .icmp6_seq = htons(*transmitted_number)};
  169 
  170     memcpy(packet, &icmp6, sizeof(icmp6));
  171 
  172     if (first_in_burst) {
  173         struct timespec pkt_time;
  174 
  175         get_time(&pkt_time);
  176 
  177         memcpy(&packet[sizeof(icmp6)], &pkt_time, sizeof(pkt_time));
  178     }
  179 
  180     ssize_t res = sendto(sock, packet, pkt_size, 0, to_ai->ai_addr, to_ai->ai_addrlen);
  181 
  182     if (res < 0) {
  183         fprintf(stderr, "%s: sendto() failed: %s\n", prog_name, strerror(errno));
  184     } else if (res != (ssize_t)pkt_size) {
  185         fprintf(stderr, "%s: partial write: packet size: %zu, sent: %zd\n", prog_name, pkt_size, res);
  186     }
  187 
  188     (*transmitted_number)++;
  189     (*transmitted_volume) += pkt_size;
  190 }
  191 
  192 static bool recv_ping4(int sock, uint16_t ident, uint32_t *received_number, uint64_t *received_volume, int64_t *min_rtt, int64_t *max_rtt, int64_t *average_rtt)
  193 {
  194     char packet[IP_MAXPACKET];
  195 
  196     struct iovec  iov = {.iov_base = packet, .iov_len    = sizeof(packet)};
  197     struct msghdr msg = {.msg_iov  = &iov,   .msg_iovlen = 1};
  198 
  199     ssize_t res = recvmsg(sock, &msg, MSG_DONTWAIT);
  200 
  201     struct ip ip4;
  202 
  203     if (res >= (ssize_t)sizeof(ip4)) {
  204         memcpy(&ip4, packet, sizeof(ip4));
  205 
  206         size_t hdr_len = ip4.ip_hl << 2;
  207 
  208         struct icmp icmp4;
  209 
  210         if (res >= (ssize_t)(hdr_len + sizeof(icmp4))) {
  211             memcpy(&icmp4, &packet[hdr_len], sizeof(icmp4));
  212 
  213             if (icmp4.icmp_type == ICMP_ECHOREPLY &&
  214                 icmp4.icmp_id   == ident) {
  215                 (*received_number)++;
  216                 (*received_volume) += res - hdr_len;
  217 
  218                 struct timespec pkt_time;
  219 
  220                 if (res >= (ssize_t)(hdr_len + sizeof(icmp4) + sizeof(pkt_time))) {
  221                     memcpy(&pkt_time, &packet[hdr_len + sizeof(icmp4)], sizeof(pkt_time));
  222 
  223                     if (pkt_time.tv_sec != 0 || pkt_time.tv_nsec != 0) {
  224                         struct timespec now;
  225 
  226                         get_time(&now);
  227 
  228                         int64_t rtt = ts_sub(&now, &pkt_time) / 1000;
  229 
  230                         if (*min_rtt > rtt) {
  231                             *min_rtt = rtt;
  232                         }
  233                         if (*max_rtt < rtt) {
  234                             *max_rtt = rtt;
  235                         }
  236 
  237                         *average_rtt = *received_number ? ((*average_rtt * (*received_number - 1)) + rtt) / *received_number : *average_rtt;
  238                     }
  239                 }
  240             }
  241         }
  242 
  243         return true;
  244     } else {
  245         return false;
  246     }
  247 }
  248 
  249 static bool recv_ping6(int sock, uint16_t ident, uint32_t *received_number, uint64_t *received_volume, int64_t *min_rtt, int64_t *max_rtt, int64_t *average_rtt)
  250 {
  251     char packet[IP_MAXPACKET];
  252 
  253     struct iovec  iov = {.iov_base = packet, .iov_len    = sizeof(packet)};
  254     struct msghdr msg = {.msg_iov  = &iov,   .msg_iovlen = 1};
  255 
  256     ssize_t res = recvmsg(sock, &msg, MSG_DONTWAIT);
  257 
  258     struct icmp6_hdr icmp6;
  259 
  260     if (res >= (ssize_t)sizeof(icmp6)) {
  261         memcpy(&icmp6, packet, sizeof(icmp6));
  262 
  263         if (icmp6.icmp6_type == ICMP6_ECHO_REPLY &&
  264             icmp6.icmp6_id   == ident) {
  265             (*received_number)++;
  266             (*received_volume) += res;
  267 
  268             struct timespec pkt_time;
  269 
  270             if (res >= (ssize_t)(sizeof(icmp6) + sizeof(pkt_time))) {
  271                 memcpy(&pkt_time, &packet[sizeof(icmp6)], sizeof(pkt_time));
  272 
  273                 if (pkt_time.tv_sec != 0 || pkt_time.tv_nsec != 0) {
  274                     struct timespec now;
  275 
  276                     get_time(&now);
  277 
  278                     int64_t rtt = ts_sub(&now, &pkt_time) / 1000;
  279 
  280                     if (*min_rtt > rtt) {
  281                         *min_rtt = rtt;
  282                     }
  283                     if (*max_rtt < rtt) {
  284                         *max_rtt = rtt;
  285                     }
  286 
  287                     *average_rtt = *received_number ? ((*average_rtt * (*received_number - 1)) + rtt) / *received_number : *average_rtt;
  288                 }
  289             }
  290         }
  291 
  292         return true;
  293     } else {
  294         return false;
  295     }
  296 }
  297 
  298 static bool resolve_name(bool ipv4_mode, const char *name, struct addrinfo **addr_info)
  299 {
  300     struct addrinfo hints = {.ai_flags = AI_CANONNAME, .ai_socktype = SOCK_RAW};
  301 
  302     if (ipv4_mode) {
  303         hints.ai_family   = AF_INET;
  304         hints.ai_protocol = IPPROTO_ICMP;
  305     } else {
  306         hints.ai_family   = AF_INET6;
  307         hints.ai_protocol = IPPROTO_ICMPV6;
  308     }
  309 
  310     int res = getaddrinfo(name, NULL, &hints, addr_info);
  311 
  312     if (res != 0) {
  313         fprintf(stderr, "%s: cannot resolve %s: %s\n", prog_name, name, gai_strerror(res));
  314 
  315         return false;
  316     } else {
  317         return true;
  318     }
  319 }
  320 
  321 int main(int argc, char *argv[])
  322 {
  323     prog_name = basename(argv[0]);
  324 
  325     bool ipv4_mode = (strcmp(prog_name, "bwping") == 0);
  326     int  exit_val  = EX_OK;
  327 
  328     unsigned int buf_size          = 0,
  329                  tos_or_traf_class = 0;
  330     size_t       pkt_size          = 0;
  331     int32_t      reporting_period  = 0;
  332     uint32_t     kbps              = 0;
  333     uint64_t     volume            = 0;
  334     char        *bind_addr         = NULL,
  335                 *target            = NULL;
  336 
  337     int ch;
  338 
  339     while ((ch = getopt(argc, argv, "46B:T:b:r:s:u:v:")) != -1) {
  340         char *ep;
  341 
  342         switch (ch) {
  343             case '4':
  344                 ipv4_mode = true;
  345 
  346                 break;
  347             case '6':
  348                 ipv4_mode = false;
  349 
  350                 break;
  351             case 'B':
  352                 bind_addr = optarg;
  353 
  354                 break;
  355             case 'T':
  356                 tos_or_traf_class = strtoul(optarg, &ep, 0);
  357 
  358                 if (*ep || ep == optarg) {
  359                     exit_val = EX_USAGE;
  360                 }
  361 
  362                 break;
  363             case 'b':
  364                 kbps = strtoul(optarg, &ep, 0);
  365 
  366                 if (*ep || ep == optarg) {
  367                     exit_val = EX_USAGE;
  368                 }
  369 
  370                 break;
  371             case 'r':
  372                 reporting_period = strtol(optarg, &ep, 0);
  373 
  374                 if (*ep || ep == optarg || reporting_period < 0) {
  375                     exit_val = EX_USAGE;
  376                 }
  377 
  378                 break;
  379             case 's':
  380                 pkt_size = strtoul(optarg, &ep, 0);
  381 
  382                 if (*ep || ep == optarg) {
  383                     exit_val = EX_USAGE;
  384                 }
  385 
  386                 break;
  387             case 'u':
  388                 buf_size = strtoul(optarg, &ep, 0);
  389 
  390                 if (*ep || ep == optarg) {
  391                     exit_val = EX_USAGE;
  392                 }
  393 
  394                 break;
  395             case 'v':
  396                 volume = strtoull(optarg, &ep, 0);
  397 
  398                 if (*ep || ep == optarg) {
  399                     exit_val = EX_USAGE;
  400                 }
  401 
  402                 break;
  403             default:
  404                 exit_val = EX_USAGE;
  405         }
  406     }
  407 
  408     if (argc - optind == 1) {
  409         target = argv[optind];
  410     }
  411 
  412     if (pkt_size == 0 || kbps == 0 || volume == 0 || target == NULL) {
  413         exit_val = EX_USAGE;
  414     }
  415 
  416     if (exit_val != EX_OK) {
  417         fprintf(stderr, "Usage: %s [-4 | -6] [-u buf_size] [-r reporting_period] [-T tos(v4) | traf_class(v6)] [-B bind_addr] -b kbps -s pkt_size -v volume target\n", prog_name);
  418 
  419         exit(exit_val);
  420     }
  421 
  422     if (ipv4_mode) {
  423         if (pkt_size < sizeof(struct icmp) + sizeof(struct timespec) || pkt_size > IP_MAXPACKET - MAX_IPV4_HDR_SIZE) {
  424             fprintf(stderr, "%s: invalid packet size, should be between %zu and %zu\n", prog_name,
  425                                                                                         sizeof(struct icmp) + sizeof(struct timespec),
  426                                                                                         (size_t)IP_MAXPACKET - MAX_IPV4_HDR_SIZE);
  427             exit(EX_USAGE);
  428         }
  429     } else {
  430         if (pkt_size < sizeof(struct icmp6_hdr) + sizeof(struct timespec) || pkt_size > IP_MAXPACKET) {
  431             fprintf(stderr, "%s: invalid packet size, should be between %zu and %zu\n", prog_name,
  432                                                                                         sizeof(struct icmp6_hdr) + sizeof(struct timespec),
  433                                                                                         (size_t)IP_MAXPACKET);
  434             exit(EX_USAGE);
  435         }
  436     }
  437 
  438     int sock;
  439 
  440     if (ipv4_mode) {
  441         sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
  442 
  443         if (sock < 0) {
  444             fprintf(stderr, "%s: socket(AF_INET, SOCK_RAW, IPPROTO_ICMP) failed: %s\n", prog_name, strerror(errno));
  445 
  446             exit(EX_OSERR);
  447         }
  448     } else {
  449         sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
  450 
  451         if (sock < 0) {
  452             fprintf(stderr, "%s: socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6) failed: %s\n", prog_name, strerror(errno));
  453 
  454             exit(EX_OSERR);
  455         }
  456     }
  457 
  458     if (setuid(getuid()) < 0) {
  459         fprintf(stderr, "%s: setuid(getuid()) failed: %s\n", prog_name, strerror(errno));
  460 
  461         exit_val = EX_OSERR;
  462     } else {
  463         if (bind_addr != NULL) {
  464             struct addrinfo *bind_ai;
  465 
  466             if (resolve_name(ipv4_mode, bind_addr, &bind_ai)) {
  467                 if (bind(sock, bind_ai->ai_addr, bind_ai->ai_addrlen) < 0) {
  468                     fprintf(stderr, "%s: bind() failed: %s\n", prog_name, strerror(errno));
  469 
  470                     exit_val = EX_OSERR;
  471                 }
  472 
  473                 freeaddrinfo(bind_ai);
  474             } else {
  475                 exit_val = EX_SOFTWARE;
  476             }
  477         }
  478 
  479         if (exit_val == EX_OK) {
  480             struct addrinfo *to_ai;
  481 
  482             if (resolve_name(ipv4_mode, target, &to_ai)) {
  483                 uint16_t ident = getpid() & 0xFFFF;
  484 
  485                 char addr_buf[ipv4_mode ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN];
  486 
  487                 if (getnameinfo(to_ai->ai_addr, to_ai->ai_addrlen, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST) != 0) {
  488                     addr_buf[0] = '?';
  489                     addr_buf[1] = 0;
  490                 }
  491 
  492                 printf("Target: %s (%s), transfer speed: %" PRIu32 " kbps, packet size: %zu bytes, traffic volume: %" PRIu64 " bytes\n",
  493                        target, addr_buf, kbps, pkt_size, volume);
  494 
  495                 bool     finish             = false;
  496                 uint32_t transmitted_number = 0,
  497                          received_number    = 0;
  498                 int64_t  min_rtt            = INT64_MAX,
  499                          max_rtt            = 0,
  500                          average_rtt        = 0;
  501                 uint64_t transmitted_volume = 0,
  502                          received_volume    = 0;
  503 
  504                 int64_t interval     = (int64_t)pkt_size * 8000 / kbps,
  505                         min_interval = calibrate_timer();
  506 
  507                 uint32_t pkt_burst;
  508 
  509                 if (interval >= min_interval) {
  510                     pkt_burst = PKT_BURST_PRECISION * 1;
  511                 } else if (interval == 0) {
  512                     pkt_burst = PKT_BURST_PRECISION * min_interval * kbps / 8000 / pkt_size;
  513                     interval  = min_interval;
  514                 } else {
  515                     pkt_burst = PKT_BURST_PRECISION * min_interval / interval;
  516                     interval  = min_interval;
  517                 }
  518 
  519                 if (buf_size == 0) {
  520                     buf_size = pkt_size * (pkt_burst / PKT_BURST_PRECISION + 1) * BUF_SIZE_RESERVE_FACTOR;
  521                 }
  522 
  523                 if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &buf_size, sizeof(buf_size)) < 0) {
  524                     fprintf(stderr, "%s: setsockopt(SO_RCVBUF, %u) failed: %s\n", prog_name, buf_size, strerror(errno));
  525                 }
  526                 if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buf_size, sizeof(buf_size)) < 0) {
  527                     fprintf(stderr, "%s: setsockopt(SO_SNDBUF, %u) failed: %s\n", prog_name, buf_size, strerror(errno));
  528                 }
  529 
  530                 if (ipv4_mode) {
  531                     if (setsockopt(sock, IPPROTO_IP, IP_TOS, &tos_or_traf_class, sizeof(tos_or_traf_class)) < 0) {
  532                         fprintf(stderr, "%s: setsockopt(IP_TOS, %u) failed: %s\n", prog_name, tos_or_traf_class, strerror(errno));
  533                     }
  534                 } else {
  535                     if (setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, &tos_or_traf_class, sizeof(tos_or_traf_class)) < 0) {
  536                         fprintf(stderr, "%s: setsockopt(IPV6_TCLASS, %u) failed: %s\n", prog_name, tos_or_traf_class, strerror(errno));
  537                     }
  538                 }
  539 
  540                 struct timespec begin, end, report;
  541 
  542                 get_time(&begin);
  543                 get_time(&end);
  544                 get_time(&report);
  545 
  546                 uint32_t pkt_burst_error  = 0;
  547                 int64_t  current_interval = interval,
  548                          interval_error   = 0;
  549 
  550                 while (!finish) {
  551                     struct timespec start;
  552 
  553                     get_time(&start);
  554 
  555                     for (uint32_t i = 0; i < pkt_burst / PKT_BURST_PRECISION + pkt_burst_error / PKT_BURST_PRECISION; i++) {
  556                         if ((uint64_t)pkt_size * transmitted_number < volume) {
  557                             if (ipv4_mode) {
  558                                 send_ping4(sock, to_ai, pkt_size, ident, !i, &transmitted_number, &transmitted_volume);
  559                             } else {
  560                                 send_ping6(sock, to_ai, pkt_size, ident, !i, &transmitted_number, &transmitted_volume);
  561                             }
  562                         }
  563                     }
  564 
  565                     pkt_burst_error  = pkt_burst_error % PKT_BURST_PRECISION;
  566                     pkt_burst_error += pkt_burst       % PKT_BURST_PRECISION;
  567 
  568                     int64_t select_timeout = current_interval;
  569 
  570                     while (1) {
  571                         fd_set fds;
  572 
  573                         FD_ZERO(&fds);
  574                         FD_SET(sock, &fds);
  575 
  576                         struct timeval timeout;
  577 
  578                         timeout.tv_sec  = select_timeout / 1000000;
  579                         timeout.tv_usec = select_timeout % 1000000;
  580 
  581                         int n = select(sock + 1, &fds, NULL, NULL, &timeout);
  582 
  583                         if (n > 0) {
  584                             while (ipv4_mode ? recv_ping4(sock, ident, &received_number, &received_volume, &min_rtt, &max_rtt, &average_rtt) :
  585                                                recv_ping6(sock, ident, &received_number, &received_volume, &min_rtt, &max_rtt, &average_rtt)) {
  586                                 if (received_number >= transmitted_number) {
  587                                     break;
  588                                 }
  589                             }
  590                         }
  591 
  592                         struct timespec now;
  593 
  594                         get_time(&now);
  595 
  596                         if (ts_sub(&now, &start) >= current_interval) {
  597                             if ((uint64_t)pkt_size * transmitted_number >= volume) {
  598                                 finish = true;
  599                             } else {
  600                                 interval_error += ts_sub(&now, &start) - current_interval;
  601 
  602                                 if (interval_error >= interval / 2) {
  603                                     current_interval  = interval / 2;
  604                                     interval_error   -= interval / 2;
  605                                 } else {
  606                                     current_interval = interval;
  607                                 }
  608                             }
  609 
  610                             break;
  611                         } else {
  612                             select_timeout = current_interval - ts_sub(&now, &start);
  613                         }
  614                     }
  615 
  616                     get_time(&end);
  617 
  618                     if (reporting_period != 0 && end.tv_sec - report.tv_sec >= reporting_period) {
  619                         printf("Periodic: pkts sent/rcvd: %" PRIu32 "/%" PRIu32 ", volume sent/rcvd: %" PRIu64 "/%" PRIu64 " bytes, time: %ld sec, speed: %" PRIu64 " kbps, rtt min/max/average: %" PRId64 "/%" PRId64 "/%" PRId64 " ms\n",
  620                                transmitted_number, received_number, transmitted_volume, received_volume, (long int)(end.tv_sec - begin.tv_sec),
  621                                end.tv_sec - begin.tv_sec ? ((received_volume / (end.tv_sec - begin.tv_sec)) * 8) / 1000 : (received_volume * 8) / 1000,
  622                                min_rtt == INT64_MAX ? 0 : min_rtt, max_rtt, average_rtt);
  623 
  624                         get_time(&report);
  625                     }
  626                 }
  627 
  628                 printf("Total: pkts sent/rcvd: %" PRIu32 "/%" PRIu32 ", volume sent/rcvd: %" PRIu64 "/%" PRIu64 " bytes, time: %ld sec, speed: %" PRIu64 " kbps, rtt min/max/average: %" PRId64 "/%" PRId64 "/%" PRId64 " ms\n",
  629                        transmitted_number, received_number, transmitted_volume, received_volume, (long int)(end.tv_sec - begin.tv_sec),
  630                        end.tv_sec - begin.tv_sec ? ((received_volume / (end.tv_sec - begin.tv_sec)) * 8) / 1000 : (received_volume * 8) / 1000,
  631                        min_rtt == INT64_MAX ? 0 : min_rtt, max_rtt, average_rtt);
  632 
  633                 freeaddrinfo(to_ai);
  634             } else {
  635                 exit_val = EX_SOFTWARE;
  636             }
  637         }
  638     }
  639 
  640     close(sock);
  641 
  642     exit(exit_val);
  643 }