ping: implement -A "adaptive ping"
authorDenys Vlasenko <vda.linux@googlemail.com>
Tue, 13 Feb 2018 22:21:33 +0000 (23:21 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Tue, 13 Feb 2018 22:21:33 +0000 (23:21 +0100)
function                                             old     new   delta
common_ping_main                                    1757    1862    +105
packed_usage                                       32367   32427     +60
sendping_tail                                        236     209     -27
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/1 up/down: 165/-27)           Total: 138 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
networking/ping.c

index d1d59d5456c6e71da9ef9f58a6c850d5ad4150be..bf750d032f08c5b518b3e13c4fbc0acc2ecea008 100644 (file)
@@ -74,6 +74,7 @@
 //usage:       )
 //usage:     "\n       -c CNT          Send only CNT pings"
 //usage:     "\n       -s SIZE         Send SIZE data bytes in packets (default 56)"
+//usage:     "\n       -A              Ping as soon as reply is recevied"
 //usage:     "\n       -t TTL          Set TTL"
 //usage:     "\n       -I IFACE/IP     Source interface or IP address"
 //usage:     "\n       -W SEC          Seconds to wait for the first response (default 10)"
@@ -90,6 +91,7 @@
 //usage:       "Send ICMP ECHO_REQUEST packets to network hosts\n"
 //usage:     "\n       -c CNT          Send only CNT pings"
 //usage:     "\n       -s SIZE         Send SIZE data bytes in packets (default 56)"
+//usage:     "\n       -A              Ping as soon as reply is recevied"
 //usage:     "\n       -I IFACE/IP     Source interface or IP address"
 //usage:     "\n       -q              Quiet, only display output at start"
 //usage:     "\n                       and when finished"
@@ -348,20 +350,21 @@ static int common_ping_main(sa_family_t af, char **argv)
 /* Full(er) version */
 
 /* -c NUM, -t NUM, -w NUM, -W NUM */
-#define OPT_STRING "qvc:+s:t:+w:+W:+I:np:4"IF_PING6("6")
+#define OPT_STRING "qvAc:+s:t:+w:+W:+I:np:4"IF_PING6("6")
 enum {
        OPT_QUIET = 1 << 0,
        OPT_VERBOSE = 1 << 1,
-       OPT_c = 1 << 2,
-       OPT_s = 1 << 3,
-       OPT_t = 1 << 4,
-       OPT_w = 1 << 5,
-       OPT_W = 1 << 6,
-       OPT_I = 1 << 7,
-       /*OPT_n = 1 << 8, - ignored */
-       OPT_p = 1 << 9,
-       OPT_IPV4 = 1 << 10,
-       OPT_IPV6 = (1 << 11) * ENABLE_PING6,
+       OPT_A = 1 << 2,
+       OPT_c = 1 << 3,
+       OPT_s = 1 << 4,
+       OPT_t = 1 << 5,
+       OPT_w = 1 << 6,
+       OPT_W = 1 << 7,
+       OPT_I = 1 << 8,
+       /*OPT_n = 1 << 9, - ignored */
+       OPT_p = 1 << 10,
+       OPT_IPV4 = 1 << 11,
+       OPT_IPV6 = (1 << 12) * ENABLE_PING6,
 };
 
 
@@ -377,9 +380,8 @@ struct globals {
        uint8_t pattern;
        unsigned tmin, tmax; /* in us */
        unsigned long long tsum; /* in us, sum of all times */
-       unsigned deadline;
+       unsigned deadline_ms;
        unsigned timeout;
-       unsigned total_secs;
        unsigned sizeof_rcv_packet;
        char *rcv_packet; /* [datalen + MAXIPLEN + MAXICMPLEN] */
        void *snd_packet; /* [datalen + ipv4/ipv6_const] */
@@ -405,9 +407,7 @@ struct globals {
 #define tmin         (G.tmin        )
 #define tmax         (G.tmax        )
 #define tsum         (G.tsum        )
-#define deadline     (G.deadline    )
 #define timeout      (G.timeout     )
-#define total_secs   (G.total_secs  )
 #define hostname     (G.hostname    )
 #define dotted       (G.dotted      )
 #define pingaddr     (G.pingaddr    )
@@ -455,7 +455,7 @@ static void print_stats_and_exit(int junk UNUSED_PARAM)
                        tmax / 1000, tmax % 1000);
        }
        /* if condition is true, exit with 1 -- 'failure' */
-       exit(nrecv == 0 || (deadline && nrecv < pingcount));
+       exit(nrecv == 0 || (G.deadline_ms && nrecv < pingcount));
 }
 
 static void sendping_tail(void (*sp)(int), int size_pkt)
@@ -467,22 +467,23 @@ static void sendping_tail(void (*sp)(int), int size_pkt)
 
        size_pkt += datalen;
 
+       if (G.deadline_ms) {
+               unsigned n = ((unsigned)monotonic_ms()) - G.deadline_ms;
+               if ((int)n >= 0)
+                       print_stats_and_exit(0);
+       }
+
        /* sizeof(pingaddr) can be larger than real sa size, but I think
         * it doesn't matter */
        sz = xsendto(pingsock, G.snd_packet, size_pkt, &pingaddr.sa, sizeof(pingaddr));
        if (sz != size_pkt)
                bb_error_msg_and_die(bb_msg_write_error);
 
-       if (pingcount == 0 || deadline || G.ntransmitted < pingcount) {
+       if (pingcount == 0 || G.ntransmitted < pingcount) {
                /* Didn't send all pings yet - schedule next in 1s */
                signal(SIGALRM, sp);
-               if (deadline) {
-                       total_secs += PINGINTERVAL;
-                       if (total_secs >= deadline)
-                               signal(SIGALRM, print_stats_and_exit);
-               }
                alarm(PINGINTERVAL);
-       } else { /* -c NN, and all NN are sent (and no deadline) */
+       } else { /* -c NN, and all NN are sent */
                /* Wait for the last ping to come back.
                 * -W timeout: wait for a response in seconds.
                 * Affects only timeout in absence of any responses,
@@ -632,7 +633,7 @@ static void unpack_tail(int sz, uint32_t *tp,
        puts(dupmsg);
        fflush_all();
 }
-static void unpack4(char *buf, int sz, struct sockaddr_in *from)
+static int unpack4(char *buf, int sz, struct sockaddr_in *from)
 {
        struct icmp *icmppkt;
        struct iphdr *iphdr;
@@ -640,7 +641,7 @@ static void unpack4(char *buf, int sz, struct sockaddr_in *from)
 
        /* discard if too short */
        if (sz < (datalen + ICMP_MINLEN))
-               return;
+               return 0;
 
        /* check IP header */
        iphdr = (struct iphdr *) buf;
@@ -648,7 +649,7 @@ static void unpack4(char *buf, int sz, struct sockaddr_in *from)
        sz -= hlen;
        icmppkt = (struct icmp *) (buf + hlen);
        if (icmppkt->icmp_id != myid)
-               return;                         /* not our ping */
+               return 0;                               /* not our ping */
 
        if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
                uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
@@ -659,25 +660,28 @@ static void unpack4(char *buf, int sz, struct sockaddr_in *from)
                unpack_tail(sz, tp,
                        inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
                        recv_seq, iphdr->ttl);
-       } else if (icmppkt->icmp_type != ICMP_ECHO) {
+               return 1;
+       }
+       if (icmppkt->icmp_type != ICMP_ECHO) {
                bb_error_msg("warning: got ICMP %d (%s)",
                                icmppkt->icmp_type,
                                icmp_type_name(icmppkt->icmp_type));
        }
+       return 0;
 }
 #if ENABLE_PING6
-static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
+static int unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
 {
        struct icmp6_hdr *icmppkt;
        char buf[INET6_ADDRSTRLEN];
 
        /* discard if too short */
        if (sz < (datalen + sizeof(struct icmp6_hdr)))
-               return;
+               return 0;
 
        icmppkt = (struct icmp6_hdr *) packet;
        if (icmppkt->icmp6_id != myid)
-               return;                         /* not our ping */
+               return 0;                               /* not our ping */
 
        if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
                uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
@@ -689,11 +693,14 @@ static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimi
                        inet_ntop(AF_INET6, &from->sin6_addr,
                                        buf, sizeof(buf)),
                        recv_seq, hoplimit);
-       } else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
+               return 1;
+       }
+       if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
                bb_error_msg("warning: got ICMP %d (%s)",
                                icmppkt->icmp6_type,
                                icmp6_type_name(icmppkt->icmp6_type));
        }
+       return 0;
 }
 #endif
 
@@ -726,6 +733,7 @@ static void ping4(len_and_sockaddr *lsa)
        signal(SIGINT, print_stats_and_exit);
 
        /* start the ping's going ... */
+ send_ping:
        sendping4(0);
 
        /* listen for replies */
@@ -741,9 +749,12 @@ static void ping4(len_and_sockaddr *lsa)
                                bb_perror_msg("recvfrom");
                        continue;
                }
-               unpack4(G.rcv_packet, c, &from);
+               c = unpack4(G.rcv_packet, c, &from);
                if (pingcount && G.nreceived >= pingcount)
                        break;
+               if (c && (option_mask32 & OPT_A)) {
+                       goto send_ping;
+               }
        }
 }
 #if ENABLE_PING6
@@ -794,10 +805,6 @@ static void ping6(len_and_sockaddr *lsa)
 
        signal(SIGINT, print_stats_and_exit);
 
-       /* start the ping's going ... */
-       sendping6(0);
-
-       /* listen for replies */
        msg.msg_name = &from;
        msg.msg_namelen = sizeof(from);
        msg.msg_iov = &iov;
@@ -805,12 +812,18 @@ static void ping6(len_and_sockaddr *lsa)
        msg.msg_control = control_buf;
        iov.iov_base = G.rcv_packet;
        iov.iov_len = G.sizeof_rcv_packet;
+
+       /* start the ping's going ... */
+ send_ping:
+       sendping6(0);
+
+       /* listen for replies */
        while (1) {
                int c;
                struct cmsghdr *mp;
                int hoplimit = -1;
-               msg.msg_controllen = sizeof(control_buf);
 
+               msg.msg_controllen = sizeof(control_buf);
                c = recvmsg(pingsock, &msg, 0);
                if (c < 0) {
                        if (errno != EINTR)
@@ -827,9 +840,12 @@ static void ping6(len_and_sockaddr *lsa)
                                move_from_unaligned_int(hoplimit, CMSG_DATA(mp));
                        }
                }
-               unpack6(G.rcv_packet, c, &from, hoplimit);
+               c = unpack6(G.rcv_packet, c, &from, hoplimit);
                if (pingcount && G.nreceived >= pingcount)
                        break;
+               if (c && (option_mask32 & OPT_A)) {
+                       goto send_ping;
+               }
        }
 }
 #endif
@@ -875,7 +891,7 @@ static int common_ping_main(int opt, char **argv)
                        OPT_STRING
                        /* exactly one arg; -v and -q don't mix */
                        "\0" "=1:q--v:v--q",
-                       &pingcount, &str_s, &opt_ttl, &deadline, &timeout, &str_I, &str_p
+                       &pingcount, &str_s, &opt_ttl, &G.deadline_ms, &timeout, &str_I, &str_p
        );
        if (opt & OPT_s)
                datalen = xatou16(str_s); // -s
@@ -889,6 +905,10 @@ static int common_ping_main(int opt, char **argv)
        }
        if (opt & OPT_p)
                G.pattern = xstrtou_range(str_p, 16, 0, 255);
+       if (G.deadline_ms) {
+               unsigned d = G.deadline_ms < INT_MAX/1000 ? G.deadline_ms : INT_MAX/1000;
+               G.deadline_ms = 1 | ((d * 1000) + monotonic_ms());
+       }
 
        myid = (uint16_t) getpid();
        hostname = argv[optind];
@@ -911,7 +931,7 @@ static int common_ping_main(int opt, char **argv)
 
        dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
        ping(lsa);
-       print_stats_and_exit(EXIT_SUCCESS);
+       print_stats_and_exit(0);
        /*return EXIT_SUCCESS;*/
 }
 #endif /* FEATURE_FANCY_PING */