From 3b0ff97a8f37ed2ffa4872737fdc1854f1d19f2a Mon Sep 17 00:00:00 2001
From: Rob Landley <rob@landley.net>
Date: Mon, 11 Jun 2018 12:52:10 -0500
Subject: Fix ping -f, add summary section, yank TOYFLAG_ROOTONLY.

Traditional "ping flood" would be "ping -fi0 -c0" but we require root for -i<.2
---
 toys/pending/ping.c | 60 ++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 41 insertions(+), 19 deletions(-)

(limited to 'toys/pending')

diff --git a/toys/pending/ping.c b/toys/pending/ping.c
index c29fb9df..22c571b3 100644
--- a/toys/pending/ping.c
+++ b/toys/pending/ping.c
@@ -9,8 +9,9 @@
  * (Android does this by default in its init script.)
  *
  * Yes, I wimped out and capped -s at sizeof(toybuf), waiting for a complaint...
- 
-USE_PING(NEWTOY(ping, "<1>1t#<0>255=64c#<0=3s#<0>4088=56I:i:W#<0=10w#<0qf46[-46]", TOYFLAG_ROOTONLY|TOYFLAG_USR|TOYFLAG_BIN))
+
+// -s > 4088 = sizeof(toybuf)-sizeof(struct icmphdr), then kernel adds 20 bytes
+USE_PING(NEWTOY(ping, "<1>1t#<0>255=64c#<0=3s#<0>4088=56I:i:W#<0=10w#<0qf46[-46]", TOYFLAG_USR|TOYFLAG_BIN))
  
 config PING
   bool "ping"
@@ -27,7 +28,7 @@ config PING
     Options:
     -4, -6      Force IPv4 or IPv6
     -c CNT      Send CNT many packets (default 3, 0 = infinite)
-    -f          Flood (. on send, backspace on receive, to show packet drops)
+    -f          Flood (print . and \b to show drops, default -c 15 -i 0.2)
     -i TIME     Interval between packets (default 1, need root for < .2)
     -I IFACE/IP Source interface or address
     -q          Quiet (stops after one returns true if host is alive)
@@ -52,8 +53,10 @@ GLOBALS(
   long c;
   long t;
 
+  struct sockaddr *sa;
   int sock;
   long i_ms;
+  unsigned long sent, recv, fugit, min, max;
 )
 
 static void xsendto(int sockfd, void *buf, size_t len, struct sockaddr *dest)
@@ -65,6 +68,18 @@ static void xsendto(int sockfd, void *buf, size_t len, struct sockaddr *dest)
   if (rc != len) perror_exit("sendto");
 }
 
+static void summary(int sig)
+{
+  if (!(toys.optflags&FLAG_q) && TT.sent && TT.sa) {
+    printf("\n--- %s ping statistics ---\n", ntop(TT.sa));
+    printf("%lu packets transmitted, %lu received, %ld%% packet loss\n",
+      TT.sent, TT.recv, ((TT.sent-TT.recv)*100)/TT.sent);
+    printf("round-trip min/avg/max = %lu/%lu/%lu ms\n",
+      TT.min, TT.max, TT.fugit/TT.recv);
+  }
+  TT.sa = 0;
+}
+
 // assumes aligned and can read even number of bytes
 static unsigned short pingchksum(unsigned short *data, int len)
 {
@@ -91,9 +106,9 @@ void ping_main(void)
   } src_addr, src_addr2;
   struct sockaddr *sa = (void *)&src_addr, *sa2 = (void *)&src_addr2;
   struct pollfd pfd;
-  int family = 0, sent = 0, len;
+  int family = 0, len;
   long long tnext, tW, tnow, tw;
-  unsigned short seq = 0;
+  unsigned short seq = 0, pkttime;
   struct icmphdr *ih = (void *)toybuf;
 
   // Interval
@@ -103,14 +118,17 @@ void ping_main(void)
     TT.i_ms = xparsetime(TT.i, 1000, &frac) * 1000;
     TT.i_ms += frac;
     if (TT.i_ms<200 && getuid()) error_exit("need root for -i <200");
-  } else TT.i_ms = 1000;
+  } else TT.i_ms = (toys.optflags&FLAG_f) ? 200 : 1000;
   if (!(toys.optflags&FLAG_s)) TT.s = 56; // 64-PHDR_LEN
+  if ((toys.optflags&(FLAG_f|FLAG_c)) == FLAG_f) TT.c = 15;
 
   // ipv4 or ipv6? (0 = autodetect if -I or arg have only one address type.)
   if (toys.optflags&FLAG_6) family = AF_INET6;
   else if (toys.optflags&FLAG_4) family = AF_INET;
   else family = 0;
 
+  sigatexit(summary);
+
   // If -I src_addr look it up. Allow numeric address of correct type.
   memset(&src_addr, 0, sizeof(src_addr));
   if (TT.I) {
@@ -146,6 +164,7 @@ void ping_main(void)
 
   if (!ai)
     error_exit("no v%d addr for -I %s", 4+2*(family==AF_INET6), TT.I);
+  TT.sa = ai->ai_addr;
 
   // Open DGRAM socket
   sa->sa_family = ai->ai_family;
@@ -170,7 +189,7 @@ void ping_main(void)
   }
 
   if (!(toys.optflags&FLAG_q)) {
-    printf("Ping %s (%s)", *toys.optargs, ntop(ai->ai_addr));
+    printf("Ping %s (%s)", *toys.optargs, ntop(TT.sa));
     if (TT.I) {
       *toybuf = 0;
       printf(" from %s (%s)", TT.I, ntop(sa));
@@ -191,7 +210,7 @@ void ping_main(void)
     // Exit due to timeout? (TODO: timeout is after last packet, waiting if
     // any packets ever dropped. Not timeout since packet was dropped.)
     tnow = millitime();
-    if (tW) if (0>=(waitms = tW-tnow) || !sent) break;
+    if (tW) if (0>=(waitms = tW-tnow) || !(TT.sent-TT.recv)) break;
     if (tw) {
       if (tnow>tw) break;
       else if (waitms>tw-tnow) waitms = tw-tnow;
@@ -207,9 +226,9 @@ void ping_main(void)
 
       ih->checksum = 0;
       ih->checksum = pingchksum((void *)toybuf, TT.s+sizeof(*ih));
-      xsendto(TT.sock, toybuf, TT.s+sizeof(*ih), ai->ai_addr);
-      sent++;
-      if (toys.optflags&FLAG_f) printf(".");
+      xsendto(TT.sock, toybuf, TT.s+sizeof(*ih), TT.sa);
+      TT.sent++;
+      if ((toys.optflags&(FLAG_f|FLAG_q)) == FLAG_f) xputc('.');
 
       // last packet?
       if (TT.c) if (!--TT.c) {
@@ -231,24 +250,27 @@ void ping_main(void)
 
     len = sizeof(src_addr2);
     len = recvfrom(TT.sock, toybuf, sizeof(toybuf), 0, sa2, (void *)&len);
-    sent--;
+    TT.recv++;
+    TT.fugit += (pkttime = millitime()-*(unsigned *)(ih+1));
 
     // reply id == 0 for ipv4, 129 for ipv6
 
     if (!(toys.optflags&FLAG_q)) {
-      printf("%d bytes from %s: icmp_seq=%d ttl=%d", len, ntop(sa2),
-             ih->un.echo.sequence, 0);
-      if (len >= sizeof(*ih)+4) {
-        unsigned lunchtime = millitime()-*(unsigned *)(ih+1);
-
-        printf(" time=%u.%03u", lunchtime/1000, lunchtime%1000);
+      if (toys.optflags&FLAG_f) xputc('\b');
+      else {
+        printf("%d bytes from %s: icmp_seq=%d ttl=%d", len, ntop(sa2),
+               ih->un.echo.sequence, 0);
+        if (len >= sizeof(*ih)+4)
+          printf(" time=%u ms", pkttime);
+        xputc('\n');
       }
-      xputc('\n');
     }
 
     toys.exitval = 0;
   }
 
+  summary(0);
+
   if (CFG_TOYBOX_FREE) {
     freeaddrinfo(ai2);
     if (ifa2) freeifaddrs(ifa2);
-- 
cgit v1.2.3