Home | History | Annotate | Download | only in pending
      1 /* ping.c - check network connectivity
      2  *
      3  * Copyright 2014 Rob Landley <rob (at) landley.net>
      4  *
      5  * Not in SUSv4.
      6  *
      7  * Note: ping_group_range should never have existed. To disable it, do:
      8  *   echo 0 $(((1<<31)-1)) > /proc/sys/net/ipv4/ping_group_range
      9  * (Android does this by default in its init script.)
     10  *
     11  * Yes, I wimped out and capped -s at sizeof(toybuf), waiting for a complaint...
     12 
     13 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))
     14 
     15 config PING
     16   bool "ping"
     17   default n
     18   help
     19     usage: ping [OPTIONS] HOST
     20 
     21     Check network connectivity by sending packets to a host and reporting
     22     its response.
     23 
     24     Send ICMP ECHO_REQUEST packets to ipv4 or ipv6 addresses and prints each
     25     echo it receives back, with round trip time. Returns true if host alive.
     26 
     27     Options:
     28     -4, -6      Force IPv4 or IPv6
     29     -c CNT      Send CNT many packets (default 3, 0 = infinite)
     30     -f          Flood (. on send, backspace on receive, to show packet drops)
     31     -i TIME     Interval between packets (default 1, need root for < .2)
     32     -I IFACE/IP Source interface or address
     33     -q          Quiet (stops after one returns true if host is alive)
     34     -s SIZE     Data SIZE in bytes (default 56)
     35     -t TTL      Set Time To Live (number of hops)
     36     -W SEC      Seconds to wait for response after -c (default 10)
     37     -w SEC      Exit after this many seconds
     38 */
     39 
     40 #define FOR_ping
     41 #include "toys.h"
     42 
     43 #include <ifaddrs.h>
     44 #include <netinet/ip_icmp.h>
     45 
     46 GLOBALS(
     47   long w;
     48   long W;
     49   char *i;
     50   char *I;
     51   long s;
     52   long c;
     53   long t;
     54 
     55   int sock;
     56   long i_ms;
     57 )
     58 
     59 static void xsendto(int sockfd, void *buf, size_t len, struct sockaddr *dest)
     60 {
     61   int rc = sendto(TT.sock, buf, len, 0, dest,
     62     dest->sa_family == AF_INET ? sizeof(struct sockaddr_in) :
     63       sizeof(struct sockaddr_in6));
     64 
     65   if (rc != len) perror_exit("sendto");
     66 }
     67 
     68 // assumes aligned and can read even number of bytes
     69 static unsigned short pingchksum(unsigned short *data, int len)
     70 {
     71   unsigned short u = 0, d;
     72 
     73   // circular carry is endian independent: bits from high byte go to low byte
     74   while (len>0) {
     75     d = *data++;
     76     if (len == 1) d &= 255<<IS_BIG_ENDIAN;
     77     if (d >= (u += d)) u++;
     78     len -= 2;
     79   }
     80 
     81   return u;
     82 }
     83 
     84 void ping_main(void)
     85 {
     86   struct addrinfo *ai, *ai2;
     87   struct ifaddrs *ifa, *ifa2 = 0;
     88   union {
     89     struct sockaddr_in in;
     90     struct sockaddr_in6 in6;
     91   } src_addr, src_addr2;
     92   struct sockaddr *sa = (void *)&src_addr, *sa2 = (void *)&src_addr2;
     93   struct pollfd pfd;
     94   int family = 0, sent = 0, len;
     95   long long tnext, tW, tnow, tw;
     96   unsigned short seq = 0;
     97   struct icmphdr *ih = (void *)toybuf;
     98 
     99   // Interval
    100   if (TT.i) {
    101     long frac;
    102 
    103     TT.i_ms = xparsetime(TT.i, 1000, &frac) * 1000;
    104     TT.i_ms += frac;
    105     if (TT.i_ms<200 && getuid()) error_exit("need root for -i <200");
    106   } else TT.i_ms = 1000;
    107   if (!(toys.optflags&FLAG_s)) TT.s = 56; // 64-PHDR_LEN
    108 
    109   // ipv4 or ipv6? (0 = autodetect if -I or arg have only one address type.)
    110   if (toys.optflags&FLAG_6) family = AF_INET6;
    111   else if (toys.optflags&FLAG_4) family = AF_INET;
    112   else family = 0;
    113 
    114   // If -I src_addr look it up. Allow numeric address of correct type.
    115   memset(&src_addr, 0, sizeof(src_addr));
    116   if (TT.I) {
    117     if (!(toys.optflags&FLAG_6) && inet_pton(AF_INET, TT.I,
    118       (void *)&src_addr.in.sin_addr))
    119         family = AF_INET;
    120     else if (!(toys.optflags&FLAG_4) && inet_pton(AF_INET6, TT.I,
    121       (void *)&src_addr.in6.sin6_addr))
    122         family = AF_INET6;
    123     else if (getifaddrs(&ifa2)) perror_exit("getifaddrs");
    124   }
    125 
    126   // Look up HOST address, filtering for correct type and interface.
    127   // If -I but no -46 then find compatible type between -I and HOST
    128   ai2 = xgetaddrinfo(*toys.optargs, 0, family, 0, 0, 0);
    129   for (ai = ai2; ai; ai = ai->ai_next) {
    130 
    131     // correct type?
    132     if (family && family!=ai->ai_family) continue;
    133     if (ai->ai_family!=AF_INET && ai->ai_family!=AF_INET6) continue;
    134 
    135     // correct interface?
    136     if (!TT.I || !ifa2) break;
    137     for (ifa = ifa2; ifa; ifa = ifa->ifa_next) {
    138       if (!ifa->ifa_addr || ifa->ifa_addr->sa_family!=ai->ai_family
    139           || strcmp(ifa->ifa_name, TT.I)) continue;
    140       sa = (void *)ifa->ifa_addr;
    141 
    142       break;
    143     }
    144     if (ifa) break;
    145   }
    146 
    147   if (!ai)
    148     error_exit("no v%d addr for -I %s", 4+2*(family==AF_INET6), TT.I);
    149 
    150   // Open DGRAM socket
    151   sa->sa_family = ai->ai_family;
    152   TT.sock = xsocket(ai->ai_family, SOCK_DGRAM,
    153     (ai->ai_family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6);
    154   if (TT.I && bind(TT.sock, sa, sizeof(src_addr))) perror_exit("bind");
    155 
    156   if (TT.t) {
    157     len = TT.t;
    158 
    159     if (ai->ai_family == AF_INET)
    160       setsockopt(TT.sock, IPPROTO_IP, IP_TTL, &len, 4);
    161     else setsockopt(TT.sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &len, 4);
    162   }
    163 
    164   if (!(toys.optflags&FLAG_q)) {
    165     printf("Ping %s (%s)", *toys.optargs, ntop(ai->ai_addr));
    166     if (TT.I) {
    167       *toybuf = 0;
    168       printf(" from %s (%s)", TT.I, ntop(sa));
    169     }
    170     // 20 byte TCP header, 8 byte ICMP header, plus data payload
    171     printf(": %ld(%ld) bytes.\n", TT.s, TT.s+28);
    172   }
    173   toys.exitval = 1;
    174 
    175   tW = tw = 0;
    176   tnext = millitime();
    177   if (TT.w) tw = TT.w*1000+tnext;
    178 
    179   // Send/receive packets
    180   for (;;) {
    181     int waitms = INT_MAX;
    182 
    183     // Exit due to timeout? (TODO: timeout is after last packet, waiting if
    184     // any packets ever dropped. Not timeout since packet was dropped.)
    185     tnow = millitime();
    186     if (tW) if (0>=(waitms = tW-tnow) || !sent) break;
    187     if (tw) {
    188       if (tnow>tw) break;
    189       else if (waitms>tw-tnow) waitms = tw-tnow;
    190     // Time to send the next packet?
    191     } else if (tnext-tnow <= 0) {
    192       tnext += TT.i_ms;
    193 
    194       memset(ih, 0, sizeof(*ih));
    195       ih->type = (ai->ai_family == AF_INET) ? 8 : 128;
    196       ih->un.echo.id = getpid();
    197       ih->un.echo.sequence = ++seq;
    198       if (TT.s >= 4) *(unsigned *)(ih+1) = tnow;
    199 
    200       ih->checksum = 0;
    201       ih->checksum = pingchksum((void *)toybuf, TT.s+sizeof(*ih));
    202       xsendto(TT.sock, toybuf, TT.s+sizeof(*ih), ai->ai_addr);
    203       sent++;
    204       if (toys.optflags&FLAG_f) printf(".");
    205 
    206       // last packet?
    207       if (TT.c) if (!--TT.c) {
    208         if (!TT.W) break;
    209         tW = tnow + TT.W*1000;
    210       }
    211     }
    212 
    213     // This is down here so it's against new period if we just sent a packet
    214     if (!tw && waitms>tnext-tnow) waitms = tnext-tnow;
    215 
    216     // wait for next packet or timeout
    217 
    218     if (waitms<0) waitms = 0;
    219     pfd.fd = TT.sock;
    220     pfd.events = POLLIN;
    221     if (0>(len = poll(&pfd, 1, waitms))) break;
    222     if (!len) continue;
    223 
    224     len = sizeof(src_addr2);
    225     len = recvfrom(TT.sock, toybuf, sizeof(toybuf), 0, sa2, (void *)&len);
    226     sent--;
    227 
    228     // reply id == 0 for ipv4, 129 for ipv6
    229 
    230     if (!(toys.optflags&FLAG_q)) {
    231       printf("%d bytes from %s: icmp_seq=%d ttl=%d", len, ntop(sa2),
    232              ih->un.echo.sequence, 0);
    233       if (len >= sizeof(*ih)+4) {
    234         unsigned lunchtime = millitime()-*(unsigned *)(ih+1);
    235 
    236         printf(" time=%u.%03u", lunchtime/1000, lunchtime%1000);
    237       }
    238       xputc('\n');
    239     }
    240 
    241     toys.exitval = 0;
    242   }
    243 
    244   if (CFG_TOYBOX_FREE) {
    245     freeaddrinfo(ai2);
    246     if (ifa2) freeifaddrs(ifa2);
    247   }
    248 }
    249