10f66f451Sopenharmony_ci/* ping.c - check network connectivity
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2014 Rob Landley <rob@landley.net>
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * Not in SUSv4.
60f66f451Sopenharmony_ci *
70f66f451Sopenharmony_ci * Note: ping_group_range should never have existed. To disable it, do:
80f66f451Sopenharmony_ci *   echo 0 $(((1<<31)-1)) > /proc/sys/net/ipv4/ping_group_range
90f66f451Sopenharmony_ci * (Android does this by default in its init script.)
100f66f451Sopenharmony_ci *
110f66f451Sopenharmony_ci * Yes, I wimped out and capped -s at sizeof(toybuf), waiting for a complaint...
120f66f451Sopenharmony_ci
130f66f451Sopenharmony_ci// -s > 4088 = sizeof(toybuf)-sizeof(struct icmphdr), then kernel adds 20 bytes
140f66f451Sopenharmony_ciUSE_PING(NEWTOY(ping, "<1>1m#t#<0>255=64c#<0=3s#<0>4088=56i%W#<0=3w#<0qf46I:[-46]", TOYFLAG_USR|TOYFLAG_BIN))
150f66f451Sopenharmony_ci
160f66f451Sopenharmony_ciconfig PING
170f66f451Sopenharmony_ci  bool "ping"
180f66f451Sopenharmony_ci  default y
190f66f451Sopenharmony_ci  help
200f66f451Sopenharmony_ci    usage: ping [OPTIONS] HOST
210f66f451Sopenharmony_ci
220f66f451Sopenharmony_ci    Check network connectivity by sending packets to a host and reporting
230f66f451Sopenharmony_ci    its response.
240f66f451Sopenharmony_ci
250f66f451Sopenharmony_ci    Send ICMP ECHO_REQUEST packets to ipv4 or ipv6 addresses and prints each
260f66f451Sopenharmony_ci    echo it receives back, with round trip time. Returns true if host alive.
270f66f451Sopenharmony_ci
280f66f451Sopenharmony_ci    Options:
290f66f451Sopenharmony_ci    -4,		Force IPv4
300f66f451Sopenharmony_ci    -c CNT		Send CNT many packets (default 3, 0 = infinite)
310f66f451Sopenharmony_ci    -f		Flood (print . and \b to show drops, default -c 15 -i 0.2)
320f66f451Sopenharmony_ci    -i TIME		Interval between packets (default 1, need root for < 0.2)
330f66f451Sopenharmony_ci    -q		Quiet (stops after one returns true if host is alive)
340f66f451Sopenharmony_ci    -s SIZE		Data SIZE in bytes (default 56)
350f66f451Sopenharmony_ci*/
360f66f451Sopenharmony_ci
370f66f451Sopenharmony_ci#define FOR_ping
380f66f451Sopenharmony_ci#include "toys.h"
390f66f451Sopenharmony_ci
400f66f451Sopenharmony_ci#include <ifaddrs.h>
410f66f451Sopenharmony_ci#include <netinet/ip_icmp.h>
420f66f451Sopenharmony_ci
430f66f451Sopenharmony_ciGLOBALS(
440f66f451Sopenharmony_ci  char *I;
450f66f451Sopenharmony_ci  long w, W, i, s, c, t, m;
460f66f451Sopenharmony_ci
470f66f451Sopenharmony_ci  struct sockaddr *sa;
480f66f451Sopenharmony_ci  int sock;
490f66f451Sopenharmony_ci  unsigned long sent, recv, fugit, min, max;
500f66f451Sopenharmony_ci)
510f66f451Sopenharmony_ci
520f66f451Sopenharmony_ci// Print a summary. Called as a single handler or at exit.
530f66f451Sopenharmony_cistatic void summary(int sig)
540f66f451Sopenharmony_ci{
550f66f451Sopenharmony_ci  unsigned long lostret = 0;
560f66f451Sopenharmony_ci  lostret = TT.sent > TT.recv ? ((TT.sent-TT.recv)*100)/(TT.sent?TT.sent:1) : 0;
570f66f451Sopenharmony_ci  if (!(toys.optflags&FLAG_q) && TT.sent && TT.sa) {
580f66f451Sopenharmony_ci    printf("\n--- %s ping statistics ---\n", ntop(TT.sa));
590f66f451Sopenharmony_ci    printf("%lu packets transmitted, %lu received, %lu%% packet loss\n",
600f66f451Sopenharmony_ci      TT.sent, TT.recv, lostret);
610f66f451Sopenharmony_ci    printf("round-trip min/avg/max = %lu/%lu/%lu ms\n",
620f66f451Sopenharmony_ci      TT.min, TT.max, TT.fugit/(TT.recv?TT.recv:1));
630f66f451Sopenharmony_ci  }
640f66f451Sopenharmony_ci  TT.sa = 0;
650f66f451Sopenharmony_ci}
660f66f451Sopenharmony_ci
670f66f451Sopenharmony_ci// assumes aligned and can read even number of bytes
680f66f451Sopenharmony_cistatic unsigned short pingchksum(unsigned short *data, int len)
690f66f451Sopenharmony_ci{
700f66f451Sopenharmony_ci  u_int32_t sum = 0;
710f66f451Sopenharmony_ci  int nwords = len >> 1;
720f66f451Sopenharmony_ci
730f66f451Sopenharmony_ci  while (nwords-- != 0) sum += *data++;
740f66f451Sopenharmony_ci  if (len & 1) {
750f66f451Sopenharmony_ci    union {
760f66f451Sopenharmony_ci      u_int16_t w;
770f66f451Sopenharmony_ci      u_int8_t c[2];
780f66f451Sopenharmony_ci    } u;
790f66f451Sopenharmony_ci    u.c[0] = *(u_char *) data;
800f66f451Sopenharmony_ci    u.c[1] = 0;
810f66f451Sopenharmony_ci    sum += u.w;
820f66f451Sopenharmony_ci  }
830f66f451Sopenharmony_ci  // end-around-carry
840f66f451Sopenharmony_ci  sum = (sum >> 16) + (sum & 0xffff);
850f66f451Sopenharmony_ci  sum += (sum >> 16);
860f66f451Sopenharmony_ci  return (~sum);
870f66f451Sopenharmony_ci}
880f66f451Sopenharmony_ci
890f66f451Sopenharmony_civoid ping_main(void)
900f66f451Sopenharmony_ci{
910f66f451Sopenharmony_ci  struct addrinfo *ai, *ai2;
920f66f451Sopenharmony_ci  struct ifaddrs *ifa, *ifa2 = 0;
930f66f451Sopenharmony_ci  struct icmphdr *ih = (void *)toybuf;
940f66f451Sopenharmony_ci  union socksaddr srcaddr, srcaddr2;
950f66f451Sopenharmony_ci  struct sockaddr *sa = (void *)&srcaddr;
960f66f451Sopenharmony_ci  int family = 0, len;
970f66f451Sopenharmony_ci  int recvLen = 0;
980f66f451Sopenharmony_ci  long long tnext, tW, tnow, tw;
990f66f451Sopenharmony_ci  unsigned short seq = 0 ,pkttime;
1000f66f451Sopenharmony_ci  long long tmp_tnow;
1010f66f451Sopenharmony_ci
1020f66f451Sopenharmony_ci  // Set nonstatic default values
1030f66f451Sopenharmony_ci  if (!(toys.optflags&FLAG_i)) TT.i = (toys.optflags&FLAG_f) ? 200 : 1000;
1040f66f451Sopenharmony_ci  else if (TT.i<200 && getuid()) error_exit("need root for -i <200");
1050f66f451Sopenharmony_ci  if (!(toys.optflags&FLAG_s)) TT.s = 56; // 64-PHDR_LEN
1060f66f451Sopenharmony_ci  if ((toys.optflags&(FLAG_f|FLAG_c)) == FLAG_f) TT.c = 15;
1070f66f451Sopenharmony_ci
1080f66f451Sopenharmony_ci  // ipv4 or ipv6? (0 = autodetect if -I or arg have only one address type.)
1090f66f451Sopenharmony_ci  if (FLAG(6) || strchr(toys.which->name, '6')) family = AF_INET6;
1100f66f451Sopenharmony_ci  else if (FLAG(4)) family = AF_INET;
1110f66f451Sopenharmony_ci  else family = 0;
1120f66f451Sopenharmony_ci
1130f66f451Sopenharmony_ci  // If -I srcaddr look it up. Allow numeric address of correct type.
1140f66f451Sopenharmony_ci  memset(&srcaddr, 0, sizeof(srcaddr));
1150f66f451Sopenharmony_ci  if (TT.I) {
1160f66f451Sopenharmony_ci    if (!(toys.optflags&FLAG_6) && inet_pton(AF_INET, TT.I,
1170f66f451Sopenharmony_ci      (void *)&srcaddr.in.sin_addr))
1180f66f451Sopenharmony_ci        family = AF_INET;
1190f66f451Sopenharmony_ci    else if (!(toys.optflags&FLAG_4) && inet_pton(AF_INET6, TT.I,
1200f66f451Sopenharmony_ci      (void *)&srcaddr.in6.sin6_addr))
1210f66f451Sopenharmony_ci        family = AF_INET6;
1220f66f451Sopenharmony_ci    else if (getifaddrs(&ifa2)) perror_exit("getifaddrs");
1230f66f451Sopenharmony_ci  }
1240f66f451Sopenharmony_ci
1250f66f451Sopenharmony_ci  // Look up HOST address, filtering for correct type and interface.
1260f66f451Sopenharmony_ci  // If -I but no -46 then find compatible type between -I and HOST
1270f66f451Sopenharmony_ci  ai2 = xgetaddrinfo(*toys.optargs, 0, family, 0, 0, 0);
1280f66f451Sopenharmony_ci  for (ai = ai2; ai; ai = ai->ai_next) {
1290f66f451Sopenharmony_ci
1300f66f451Sopenharmony_ci    // correct type?
1310f66f451Sopenharmony_ci    if (family && family!=ai->ai_family) continue;
1320f66f451Sopenharmony_ci    if (ai->ai_family!=AF_INET && ai->ai_family!=AF_INET6) continue;
1330f66f451Sopenharmony_ci
1340f66f451Sopenharmony_ci    // correct interface?
1350f66f451Sopenharmony_ci    if (!TT.I || !ifa2) break;
1360f66f451Sopenharmony_ci    for (ifa = ifa2; ifa; ifa = ifa->ifa_next) {
1370f66f451Sopenharmony_ci      if (!ifa->ifa_addr || ifa->ifa_addr->sa_family!=ai->ai_family
1380f66f451Sopenharmony_ci          || strcmp(ifa->ifa_name, TT.I)) continue;
1390f66f451Sopenharmony_ci      sa = (void *)ifa->ifa_addr;
1400f66f451Sopenharmony_ci
1410f66f451Sopenharmony_ci      break;
1420f66f451Sopenharmony_ci    }
1430f66f451Sopenharmony_ci    if (ifa) break;
1440f66f451Sopenharmony_ci  }
1450f66f451Sopenharmony_ci
1460f66f451Sopenharmony_ci  if (!ai)
1470f66f451Sopenharmony_ci    error_exit("no v%d addr for -I %s", 4+2*(family==AF_INET6), TT.I);
1480f66f451Sopenharmony_ci  TT.sa = ai->ai_addr;
1490f66f451Sopenharmony_ci
1500f66f451Sopenharmony_ci  // Open DGRAM socket
1510f66f451Sopenharmony_ci  sa->sa_family = ai->ai_family;
1520f66f451Sopenharmony_ci  TT.sock = socket(ai->ai_family, SOCK_RAW,
1530f66f451Sopenharmony_ci    len = (ai->ai_family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6);
1540f66f451Sopenharmony_ci
1550f66f451Sopenharmony_ci  if (TT.sock == -1) {
1560f66f451Sopenharmony_ci    perror_msg("socket SOCK_DGRAM %x", len);
1570f66f451Sopenharmony_ci    if (errno == EACCES) {
1580f66f451Sopenharmony_ci      fprintf(stderr, "Kernel bug workaround (as root):\n");
1590f66f451Sopenharmony_ci      fprintf(stderr, "echo 0 9999999 > /proc/sys/net/ipv4/ping_group_range\n");
1600f66f451Sopenharmony_ci    }
1610f66f451Sopenharmony_ci    xexit();
1620f66f451Sopenharmony_ci  }
1630f66f451Sopenharmony_ci  if (TT.I) xbind(TT.sock, sa, sizeof(srcaddr));
1640f66f451Sopenharmony_ci
1650f66f451Sopenharmony_ci  if (toys.optflags&FLAG_m) {
1660f66f451Sopenharmony_ci      int mark = TT.m;
1670f66f451Sopenharmony_ci
1680f66f451Sopenharmony_ci      xsetsockopt(TT.sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark));
1690f66f451Sopenharmony_ci  }
1700f66f451Sopenharmony_ci
1710f66f451Sopenharmony_ci  if (TT.t) {
1720f66f451Sopenharmony_ci    len = TT.t;
1730f66f451Sopenharmony_ci
1740f66f451Sopenharmony_ci    if (ai->ai_family == AF_INET)
1750f66f451Sopenharmony_ci      xsetsockopt(TT.sock, IPPROTO_IP, IP_TTL, &len, 4);
1760f66f451Sopenharmony_ci    else xsetsockopt(TT.sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &len, 4);
1770f66f451Sopenharmony_ci  }
1780f66f451Sopenharmony_ci
1790f66f451Sopenharmony_ci  if (!(toys.optflags&FLAG_q)) {
1800f66f451Sopenharmony_ci    printf("Ping %s (%s)", *toys.optargs, ntop(TT.sa));
1810f66f451Sopenharmony_ci    if (TT.I) {
1820f66f451Sopenharmony_ci      *toybuf = 0;
1830f66f451Sopenharmony_ci      printf(" from %s (%s)", TT.I, ntop(sa));
1840f66f451Sopenharmony_ci    }
1850f66f451Sopenharmony_ci    // 20 byte TCP header, 8 byte ICMP header, plus data payload
1860f66f451Sopenharmony_ci    printf(": %ld(%ld) bytes.\n", TT.s, TT.s+28);
1870f66f451Sopenharmony_ci  }
1880f66f451Sopenharmony_ci  toys.exitval = 1;
1890f66f451Sopenharmony_ci
1900f66f451Sopenharmony_ci  tW = tw = 0;
1910f66f451Sopenharmony_ci  tnext = millitime();
1920f66f451Sopenharmony_ci  if (TT.w) tw = TT.w*1000+tnext;
1930f66f451Sopenharmony_ci
1940f66f451Sopenharmony_ci  // Send/receive packets
1950f66f451Sopenharmony_ci  for (;;) {
1960f66f451Sopenharmony_ci    int waitms = INT_MAX;
1970f66f451Sopenharmony_ci
1980f66f451Sopenharmony_ci    // Exit due to timeout? (TODO: timeout is after last packet, waiting if
1990f66f451Sopenharmony_ci    // any packets ever dropped. Not timeout since packet was dropped.)
2000f66f451Sopenharmony_ci    tnow = millitime();
2010f66f451Sopenharmony_ci    if (tW) {
2020f66f451Sopenharmony_ci      if (0>=(waitms = tW-tnow) || !(TT.sent-TT.recv)) break;
2030f66f451Sopenharmony_ci      waitms = tW-tnow;
2040f66f451Sopenharmony_ci    }
2050f66f451Sopenharmony_ci    if (tw) {
2060f66f451Sopenharmony_ci      if (tnow>tw) break;
2070f66f451Sopenharmony_ci      else if (waitms>tw-tnow) waitms = tw-tnow;
2080f66f451Sopenharmony_ci    }
2090f66f451Sopenharmony_ci
2100f66f451Sopenharmony_ci    // Time to send the next packet?
2110f66f451Sopenharmony_ci    if (!tW && tnext-tnow <= 0) {
2120f66f451Sopenharmony_ci      tnext += TT.i;
2130f66f451Sopenharmony_ci
2140f66f451Sopenharmony_ci      memset(ih, 0, sizeof(*ih));
2150f66f451Sopenharmony_ci      ih->type = (ai->ai_family == AF_INET) ? 8 : 128;
2160f66f451Sopenharmony_ci      ih->un.echo.id = getpid();
2170f66f451Sopenharmony_ci      ih->un.echo.sequence = ++seq;
2180f66f451Sopenharmony_ci      if (TT.s >= 4) tmp_tnow = tnow;
2190f66f451Sopenharmony_ci
2200f66f451Sopenharmony_ci      ih->checksum = 0;
2210f66f451Sopenharmony_ci      ih->checksum = pingchksum((void *)toybuf, TT.s+sizeof(*ih));
2220f66f451Sopenharmony_ci      xsendto(TT.sock, toybuf, TT.s+sizeof(*ih), TT.sa);
2230f66f451Sopenharmony_ci      TT.sent++;
2240f66f451Sopenharmony_ci      if ((toys.optflags&(FLAG_f|FLAG_q)) == FLAG_f) xputc('.');
2250f66f451Sopenharmony_ci
2260f66f451Sopenharmony_ci      // last packet?
2270f66f451Sopenharmony_ci      if (TT.c) if (!--TT.c) {
2280f66f451Sopenharmony_ci        tW = tnow + TT.W*1000;
2290f66f451Sopenharmony_ci        waitms = 1; // check for immediate return even when W=0
2300f66f451Sopenharmony_ci      }
2310f66f451Sopenharmony_ci    }
2320f66f451Sopenharmony_ci
2330f66f451Sopenharmony_ci    // This is down here so it's against new period if we just sent a packet
2340f66f451Sopenharmony_ci    if (!tW && waitms>tnext-tnow) waitms = tnext-tnow;
2350f66f451Sopenharmony_ci
2360f66f451Sopenharmony_ci    // wait for next packet or timeout
2370f66f451Sopenharmony_ci    if (waitms<0) waitms = 0;
2380f66f451Sopenharmony_ci    len = xrecvwait(TT.sock, toybuf, sizeof(toybuf), &srcaddr2, waitms);
2390f66f451Sopenharmony_ci    recvLen += len;
2400f66f451Sopenharmony_ci    if ((len == 0) && (recvLen != 0)) {
2410f66f451Sopenharmony_ci      TT.recv++;
2420f66f451Sopenharmony_ci
2430f66f451Sopenharmony_ci      // reply id == 0 for ipv4, 129 for ipv6
2440f66f451Sopenharmony_ci      if (!(toys.optflags&FLAG_q)) {
2450f66f451Sopenharmony_ci        if (toys.optflags&FLAG_f) xputc('\b');
2460f66f451Sopenharmony_ci        else {
2470f66f451Sopenharmony_ci          printf("len %d bytes from %s: icmp_seq=%d ttl=%d", recvLen, ntop(&srcaddr2.s),
2480f66f451Sopenharmony_ci                 ih->un.echo.sequence, 0);
2490f66f451Sopenharmony_ci          if (len >= sizeof(*ih)+4)
2500f66f451Sopenharmony_ci            printf(" time=%u ms", pkttime);
2510f66f451Sopenharmony_ci          xputc('\n');
2520f66f451Sopenharmony_ci        }
2530f66f451Sopenharmony_ci      }
2540f66f451Sopenharmony_ci      recvLen = 0;
2550f66f451Sopenharmony_ci      continue;
2560f66f451Sopenharmony_ci    }
2570f66f451Sopenharmony_ci    TT.fugit += (pkttime = millitime()-tmp_tnow);
2580f66f451Sopenharmony_ci    toys.exitval = 0;
2590f66f451Sopenharmony_ci  }
2600f66f451Sopenharmony_ci
2610f66f451Sopenharmony_ci  summary(0);
2620f66f451Sopenharmony_ci
2630f66f451Sopenharmony_ci  if (CFG_TOYBOX_FREE) {
2640f66f451Sopenharmony_ci    freeaddrinfo(ai2);
2650f66f451Sopenharmony_ci    if (ifa2) freeifaddrs(ifa2);
2660f66f451Sopenharmony_ci  }
2670f66f451Sopenharmony_ci}
268