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 999999999 > /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 > 4064 = sizeof(toybuf)-sizeof(struct icmphdr)-CMSG_SPACE(sizeof(uint8_t)), then kernel adds 20 bytes
140f66f451Sopenharmony_ciUSE_PING(NEWTOY(ping, "<1>1m#t#<0>255=64c#<0=3s#<0>4064=56i%W#<0=3w#<0qf46I:[-46]", TOYFLAG_USR|TOYFLAG_BIN))
150f66f451Sopenharmony_ciUSE_PING(OLDTOY(ping6, ping, TOYFLAG_USR|TOYFLAG_BIN))
160f66f451Sopenharmony_ci
170f66f451Sopenharmony_ciconfig PING
180f66f451Sopenharmony_ci  bool "ping"
190f66f451Sopenharmony_ci  default y
200f66f451Sopenharmony_ci  help
210f66f451Sopenharmony_ci    usage: ping [OPTIONS] HOST
220f66f451Sopenharmony_ci
230f66f451Sopenharmony_ci    Check network connectivity by sending packets to a host and reporting
240f66f451Sopenharmony_ci    its response.
250f66f451Sopenharmony_ci
260f66f451Sopenharmony_ci    Send ICMP ECHO_REQUEST packets to ipv4 or ipv6 addresses and prints each
270f66f451Sopenharmony_ci    echo it receives back, with round trip time. Returns true if host alive.
280f66f451Sopenharmony_ci
290f66f451Sopenharmony_ci    Options:
300f66f451Sopenharmony_ci    -4, -6		Force IPv4 or IPv6
310f66f451Sopenharmony_ci    -c CNT		Send CNT many packets (default 3, 0 = infinite)
320f66f451Sopenharmony_ci    -f		Flood (print . and \b to show drops, default -c 15 -i 0.2)
330f66f451Sopenharmony_ci    -i TIME		Interval between packets (default 1, need root for < 0.2)
340f66f451Sopenharmony_ci    -I IFACE/IP	Source interface or address
350f66f451Sopenharmony_ci    -m MARK		Tag outgoing packets using SO_MARK
360f66f451Sopenharmony_ci    -q		Quiet (stops after one returns true if host is alive)
370f66f451Sopenharmony_ci    -s SIZE		Data SIZE in bytes (default 56)
380f66f451Sopenharmony_ci    -t TTL		Set Time To Live (number of hops)
390f66f451Sopenharmony_ci    -W SEC		Seconds to wait for response after last -c packet (default 3)
400f66f451Sopenharmony_ci    -w SEC		Exit after this many seconds
410f66f451Sopenharmony_ci*/
420f66f451Sopenharmony_ci
430f66f451Sopenharmony_ci#define FOR_ping
440f66f451Sopenharmony_ci#include "toys.h"
450f66f451Sopenharmony_ci
460f66f451Sopenharmony_ci#include <ifaddrs.h>
470f66f451Sopenharmony_ci#include <netinet/ip_icmp.h>
480f66f451Sopenharmony_ci
490f66f451Sopenharmony_ciGLOBALS(
500f66f451Sopenharmony_ci  char *I;
510f66f451Sopenharmony_ci  long w, W, i, s, c, t, m;
520f66f451Sopenharmony_ci
530f66f451Sopenharmony_ci  struct sockaddr *sa;
540f66f451Sopenharmony_ci  int sock;
550f66f451Sopenharmony_ci  unsigned long sent, recv, fugit, min, max;
560f66f451Sopenharmony_ci)
570f66f451Sopenharmony_ci
580f66f451Sopenharmony_ci// Print a summary. Called as a single handler or at exit.
590f66f451Sopenharmony_cistatic void summary(int sig)
600f66f451Sopenharmony_ci{
610f66f451Sopenharmony_ci  if (!FLAG(q) && TT.sent && TT.sa) {
620f66f451Sopenharmony_ci    printf("\n--- %s ping statistics ---\n", ntop(TT.sa));
630f66f451Sopenharmony_ci    printf("%lu packets transmitted, %lu received, %ld%% packet loss\n",
640f66f451Sopenharmony_ci      TT.sent, TT.recv, ((TT.sent-TT.recv)*100)/(TT.sent?TT.sent:1));
650f66f451Sopenharmony_ci    if (TT.recv)
660f66f451Sopenharmony_ci      printf("round-trip min/avg/max = %lu/%lu/%lu ms\n",
670f66f451Sopenharmony_ci        TT.min, TT.fugit/TT.recv, TT.max);
680f66f451Sopenharmony_ci  }
690f66f451Sopenharmony_ci  TT.sa = 0;
700f66f451Sopenharmony_ci}
710f66f451Sopenharmony_ci
720f66f451Sopenharmony_ci// assumes aligned and can read even number of bytes
730f66f451Sopenharmony_cistatic unsigned short pingchksum(unsigned short *data, int len)
740f66f451Sopenharmony_ci{
750f66f451Sopenharmony_ci  unsigned short u = 0, d;
760f66f451Sopenharmony_ci
770f66f451Sopenharmony_ci  // circular carry is endian independent: bits from high byte go to low byte
780f66f451Sopenharmony_ci  while (len>0) {
790f66f451Sopenharmony_ci    d = *data++;
800f66f451Sopenharmony_ci    if (len == 1) d &= 255<<IS_BIG_ENDIAN;
810f66f451Sopenharmony_ci    if (d >= (u += d)) u++;
820f66f451Sopenharmony_ci    len -= 2;
830f66f451Sopenharmony_ci  }
840f66f451Sopenharmony_ci
850f66f451Sopenharmony_ci  return u;
860f66f451Sopenharmony_ci}
870f66f451Sopenharmony_ci
880f66f451Sopenharmony_cistatic int xrecvmsgwait(int fd, struct msghdr *msg, int flag,
890f66f451Sopenharmony_ci  union socksaddr *sa, int timeout)
900f66f451Sopenharmony_ci{
910f66f451Sopenharmony_ci  socklen_t sl = sizeof(*sa);
920f66f451Sopenharmony_ci  int len;
930f66f451Sopenharmony_ci
940f66f451Sopenharmony_ci  if (timeout >= 0) {
950f66f451Sopenharmony_ci    struct pollfd pfd;
960f66f451Sopenharmony_ci
970f66f451Sopenharmony_ci    pfd.fd = fd;
980f66f451Sopenharmony_ci    pfd.events = POLLIN;
990f66f451Sopenharmony_ci    if (!xpoll(&pfd, 1, timeout)) return 0;
1000f66f451Sopenharmony_ci  }
1010f66f451Sopenharmony_ci
1020f66f451Sopenharmony_ci  msg->msg_name = (void *)sa;
1030f66f451Sopenharmony_ci  msg->msg_namelen = sl;
1040f66f451Sopenharmony_ci  len = recvmsg(fd, msg, flag);
1050f66f451Sopenharmony_ci  if (len<0) perror_exit("recvmsg");
1060f66f451Sopenharmony_ci
1070f66f451Sopenharmony_ci  return len;
1080f66f451Sopenharmony_ci}
1090f66f451Sopenharmony_ci
1100f66f451Sopenharmony_civoid ping_main(void)
1110f66f451Sopenharmony_ci{
1120f66f451Sopenharmony_ci  struct addrinfo *ai, *ai2;
1130f66f451Sopenharmony_ci  struct ifaddrs *ifa, *ifa2 = 0;
1140f66f451Sopenharmony_ci  struct icmphdr *ih = (void *)toybuf;
1150f66f451Sopenharmony_ci  struct msghdr msg;
1160f66f451Sopenharmony_ci  struct cmsghdr *cmsg;
1170f66f451Sopenharmony_ci  struct iovec iov;
1180f66f451Sopenharmony_ci  union socksaddr srcaddr, srcaddr2;
1190f66f451Sopenharmony_ci  struct sockaddr *sa = (void *)&srcaddr;
1200f66f451Sopenharmony_ci  int family = 0, ttl = 0, len;
1210f66f451Sopenharmony_ci  long long tnext, tW, tnow, tw;
1220f66f451Sopenharmony_ci  unsigned short seq = 0, pkttime;
1230f66f451Sopenharmony_ci
1240f66f451Sopenharmony_ci  // Set nonstatic default values
1250f66f451Sopenharmony_ci  if (!FLAG(i)) TT.i = FLAG(f) ? 200 : 1000;
1260f66f451Sopenharmony_ci  else if (TT.i<200 && geteuid()) error_exit("need root for -i <200");
1270f66f451Sopenharmony_ci  if (!FLAG(s)) TT.s = 56; // 64-PHDR_LEN
1280f66f451Sopenharmony_ci  if (FLAG(f) && !FLAG(c)) TT.c = 15;
1290f66f451Sopenharmony_ci
1300f66f451Sopenharmony_ci  // ipv4 or ipv6? (0 = autodetect if -I or arg have only one address type.)
1310f66f451Sopenharmony_ci  if (FLAG(6) || strchr(toys.which->name, '6')) family = AF_INET6;
1320f66f451Sopenharmony_ci  else if (FLAG(4)) family = AF_INET;
1330f66f451Sopenharmony_ci  else family = 0;
1340f66f451Sopenharmony_ci
1350f66f451Sopenharmony_ci  // If -I srcaddr look it up. Allow numeric address of correct type.
1360f66f451Sopenharmony_ci  memset(&srcaddr, 0, sizeof(srcaddr));
1370f66f451Sopenharmony_ci  if (TT.I) {
1380f66f451Sopenharmony_ci    if (!FLAG(6) && inet_pton(AF_INET, TT.I, (void *)&srcaddr.in.sin_addr))
1390f66f451Sopenharmony_ci      family = AF_INET;
1400f66f451Sopenharmony_ci    else if (!FLAG(4) && inet_pton(AF_INET6, TT.I, (void *)&srcaddr.in6.sin6_addr))
1410f66f451Sopenharmony_ci      family = AF_INET6;
1420f66f451Sopenharmony_ci    else if (getifaddrs(&ifa2)) perror_exit("getifaddrs");
1430f66f451Sopenharmony_ci  }
1440f66f451Sopenharmony_ci
1450f66f451Sopenharmony_ci  // Look up HOST address, filtering for correct type and interface.
1460f66f451Sopenharmony_ci  // If -I but no -46 then find compatible type between -I and HOST
1470f66f451Sopenharmony_ci  ai2 = xgetaddrinfo(*toys.optargs, 0, family, 0, 0, 0);
1480f66f451Sopenharmony_ci  for (ai = ai2; ai; ai = ai->ai_next) {
1490f66f451Sopenharmony_ci
1500f66f451Sopenharmony_ci    // correct type?
1510f66f451Sopenharmony_ci    if (family && family!=ai->ai_family) continue;
1520f66f451Sopenharmony_ci    if (ai->ai_family!=AF_INET && ai->ai_family!=AF_INET6) continue;
1530f66f451Sopenharmony_ci
1540f66f451Sopenharmony_ci    // correct interface?
1550f66f451Sopenharmony_ci    if (!TT.I || !ifa2) break;
1560f66f451Sopenharmony_ci    for (ifa = ifa2; ifa; ifa = ifa->ifa_next) {
1570f66f451Sopenharmony_ci      if (!ifa->ifa_addr || ifa->ifa_addr->sa_family!=ai->ai_family
1580f66f451Sopenharmony_ci          || strcmp(ifa->ifa_name, TT.I)) continue;
1590f66f451Sopenharmony_ci      sa = (void *)ifa->ifa_addr;
1600f66f451Sopenharmony_ci
1610f66f451Sopenharmony_ci      break;
1620f66f451Sopenharmony_ci    }
1630f66f451Sopenharmony_ci    if (ifa) break;
1640f66f451Sopenharmony_ci  }
1650f66f451Sopenharmony_ci
1660f66f451Sopenharmony_ci  if (!ai)
1670f66f451Sopenharmony_ci    error_exit("no v%d addr for -I %s", 4+2*(family==AF_INET6), TT.I);
1680f66f451Sopenharmony_ci  TT.sa = ai->ai_addr;
1690f66f451Sopenharmony_ci
1700f66f451Sopenharmony_ci  // Open DGRAM socket
1710f66f451Sopenharmony_ci  sa->sa_family = ai->ai_family;
1720f66f451Sopenharmony_ci  TT.sock = socket(ai->ai_family, SOCK_DGRAM,
1730f66f451Sopenharmony_ci    len = (ai->ai_family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6);
1740f66f451Sopenharmony_ci  if (TT.sock == -1) {
1750f66f451Sopenharmony_ci    perror_msg("socket SOCK_DGRAM %x", len);
1760f66f451Sopenharmony_ci    if (errno == EACCES) {
1770f66f451Sopenharmony_ci      fprintf(stderr, "Kernel bug workaround:\n"
1780f66f451Sopenharmony_ci        "echo 0 99999999 | sudo tee /proc/sys/net/ipv4/ping_group_range\n");
1790f66f451Sopenharmony_ci    }
1800f66f451Sopenharmony_ci    xexit();
1810f66f451Sopenharmony_ci  }
1820f66f451Sopenharmony_ci  if (TT.I) xbind(TT.sock, sa, sizeof(srcaddr));
1830f66f451Sopenharmony_ci
1840f66f451Sopenharmony_ci  len = 1;
1850f66f451Sopenharmony_ci  xsetsockopt(TT.sock, SOL_IP, IP_RECVTTL, &len, sizeof(len));
1860f66f451Sopenharmony_ci
1870f66f451Sopenharmony_ci  if (FLAG(m)) {
1880f66f451Sopenharmony_ci    len = TT.m;
1890f66f451Sopenharmony_ci    xsetsockopt(TT.sock, SOL_SOCKET, SO_MARK, &len, sizeof(len));
1900f66f451Sopenharmony_ci  }
1910f66f451Sopenharmony_ci
1920f66f451Sopenharmony_ci  if (TT.t) {
1930f66f451Sopenharmony_ci    len = TT.t;
1940f66f451Sopenharmony_ci    if (ai->ai_family == AF_INET)
1950f66f451Sopenharmony_ci      xsetsockopt(TT.sock, IPPROTO_IP, IP_TTL, &len, 4);
1960f66f451Sopenharmony_ci    else xsetsockopt(TT.sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &len, sizeof(len));
1970f66f451Sopenharmony_ci  }
1980f66f451Sopenharmony_ci
1990f66f451Sopenharmony_ci  if (!FLAG(q)) {
2000f66f451Sopenharmony_ci    printf("Ping %s (%s)", *toys.optargs, ntop(TT.sa));
2010f66f451Sopenharmony_ci    if (TT.I) {
2020f66f451Sopenharmony_ci      *toybuf = 0;
2030f66f451Sopenharmony_ci      printf(" from %s (%s)", TT.I, ntop(sa));
2040f66f451Sopenharmony_ci    }
2050f66f451Sopenharmony_ci    // 20 byte TCP header, 8 byte ICMP header, plus data payload
2060f66f451Sopenharmony_ci    printf(": %ld(%ld) bytes.\n", TT.s, TT.s+28);
2070f66f451Sopenharmony_ci  }
2080f66f451Sopenharmony_ci
2090f66f451Sopenharmony_ci  TT.min = ULONG_MAX;
2100f66f451Sopenharmony_ci  toys.exitval = 1;
2110f66f451Sopenharmony_ci
2120f66f451Sopenharmony_ci  tW = tw = 0;
2130f66f451Sopenharmony_ci  tnext = millitime();
2140f66f451Sopenharmony_ci  if (TT.w) tw = TT.w*1000+tnext;
2150f66f451Sopenharmony_ci
2160f66f451Sopenharmony_ci  memset(&msg, 0, sizeof(msg));
2170f66f451Sopenharmony_ci  // left enought space to store ttl value
2180f66f451Sopenharmony_ci  len = CMSG_SPACE(sizeof(uint8_t));
2190f66f451Sopenharmony_ci  iov.iov_base = (void *)toybuf;
2200f66f451Sopenharmony_ci  iov.iov_len = sizeof(toybuf) - len;
2210f66f451Sopenharmony_ci  msg.msg_iov = &iov;
2220f66f451Sopenharmony_ci  msg.msg_iovlen = 1;
2230f66f451Sopenharmony_ci  msg.msg_control = &toybuf[iov.iov_len];
2240f66f451Sopenharmony_ci  msg.msg_controllen = len;
2250f66f451Sopenharmony_ci
2260f66f451Sopenharmony_ci  sigatexit(summary);
2270f66f451Sopenharmony_ci
2280f66f451Sopenharmony_ci  // Send/receive packets
2290f66f451Sopenharmony_ci  for (;;) {
2300f66f451Sopenharmony_ci    int waitms = INT_MAX;
2310f66f451Sopenharmony_ci
2320f66f451Sopenharmony_ci    // Exit due to timeout? (TODO: timeout is after last packet, waiting if
2330f66f451Sopenharmony_ci    // any packets ever dropped. Not timeout since packet was dropped.)
2340f66f451Sopenharmony_ci    tnow = millitime();
2350f66f451Sopenharmony_ci    if (tW) {
2360f66f451Sopenharmony_ci      if (0>=(waitms = tW-tnow) || !(TT.sent-TT.recv)) break;
2370f66f451Sopenharmony_ci      waitms = tW-tnow;
2380f66f451Sopenharmony_ci    }
2390f66f451Sopenharmony_ci    if (tw) {
2400f66f451Sopenharmony_ci      if (tnow>tw) break;
2410f66f451Sopenharmony_ci      else if (waitms>tw-tnow) waitms = tw-tnow;
2420f66f451Sopenharmony_ci    }
2430f66f451Sopenharmony_ci
2440f66f451Sopenharmony_ci    // Time to send the next packet?
2450f66f451Sopenharmony_ci    if (!tW && tnext-tnow <= 0) {
2460f66f451Sopenharmony_ci      tnext += TT.i;
2470f66f451Sopenharmony_ci
2480f66f451Sopenharmony_ci      memset(ih, 0, sizeof(*ih));
2490f66f451Sopenharmony_ci      ih->type = (ai->ai_family == AF_INET) ? 8 : 128;
2500f66f451Sopenharmony_ci      ih->un.echo.id = getpid();
2510f66f451Sopenharmony_ci      ih->un.echo.sequence = ++seq;
2520f66f451Sopenharmony_ci      if (TT.s >= 4) *(unsigned *)(ih+1) = tnow;
2530f66f451Sopenharmony_ci
2540f66f451Sopenharmony_ci      ih->checksum = pingchksum((void *)toybuf, TT.s+sizeof(*ih));
2550f66f451Sopenharmony_ci      xsendto(TT.sock, toybuf, TT.s+sizeof(*ih), TT.sa);
2560f66f451Sopenharmony_ci      TT.sent++;
2570f66f451Sopenharmony_ci      if (FLAG(f) && !FLAG(q)) xputc('.');
2580f66f451Sopenharmony_ci
2590f66f451Sopenharmony_ci      // last packet?
2600f66f451Sopenharmony_ci      if (TT.c) if (!--TT.c) {
2610f66f451Sopenharmony_ci        tW = tnow + TT.W*1000;
2620f66f451Sopenharmony_ci        waitms = 1; // check for immediate return even when W=0
2630f66f451Sopenharmony_ci      }
2640f66f451Sopenharmony_ci    }
2650f66f451Sopenharmony_ci
2660f66f451Sopenharmony_ci    // This is down here so it's against new period if we just sent a packet
2670f66f451Sopenharmony_ci    if (!tW && waitms>tnext-tnow) waitms = tnext-tnow;
2680f66f451Sopenharmony_ci
2690f66f451Sopenharmony_ci    // wait for next packet or timeout
2700f66f451Sopenharmony_ci
2710f66f451Sopenharmony_ci    if (waitms<0) waitms = 0;
2720f66f451Sopenharmony_ci    if (!(len = xrecvmsgwait(TT.sock, &msg, 0, &srcaddr2, waitms)))
2730f66f451Sopenharmony_ci      continue;
2740f66f451Sopenharmony_ci
2750f66f451Sopenharmony_ci    TT.recv++;
2760f66f451Sopenharmony_ci    TT.fugit += (pkttime = millitime()-*(unsigned *)(ih+1));
2770f66f451Sopenharmony_ci    if (pkttime < TT.min) TT.min = pkttime;
2780f66f451Sopenharmony_ci    if (pkttime > TT.max) TT.max = pkttime;
2790f66f451Sopenharmony_ci
2800f66f451Sopenharmony_ci    // reply id == 0 for ipv4, 129 for ipv6
2810f66f451Sopenharmony_ci
2820f66f451Sopenharmony_ci    cmsg = CMSG_FIRSTHDR(&msg);
2830f66f451Sopenharmony_ci    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
2840f66f451Sopenharmony_ci      if (cmsg->cmsg_level == IPPROTO_IP
2850f66f451Sopenharmony_ci        && cmsg->cmsg_type == IP_TTL) {
2860f66f451Sopenharmony_ci          ttl = *(uint8_t *)CMSG_DATA(cmsg);
2870f66f451Sopenharmony_ci          break;
2880f66f451Sopenharmony_ci      }
2890f66f451Sopenharmony_ci    };
2900f66f451Sopenharmony_ci
2910f66f451Sopenharmony_ci    if (!FLAG(q)) {
2920f66f451Sopenharmony_ci      if (FLAG(f)) xputc('\b');
2930f66f451Sopenharmony_ci      else {
2940f66f451Sopenharmony_ci        printf("%d bytes from %s: icmp_seq=%d ttl=%d", len, ntop(&srcaddr2.s),
2950f66f451Sopenharmony_ci               ih->un.echo.sequence, ttl);
2960f66f451Sopenharmony_ci        if (len >= sizeof(*ih)+4) printf(" time=%u ms", pkttime);
2970f66f451Sopenharmony_ci        xputc('\n');
2980f66f451Sopenharmony_ci      }
2990f66f451Sopenharmony_ci    }
3000f66f451Sopenharmony_ci
3010f66f451Sopenharmony_ci    toys.exitval = 0;
3020f66f451Sopenharmony_ci  }
3030f66f451Sopenharmony_ci
3040f66f451Sopenharmony_ci  sigatexit(0);
3050f66f451Sopenharmony_ci  summary(0);
3060f66f451Sopenharmony_ci
3070f66f451Sopenharmony_ci  if (CFG_TOYBOX_FREE) {
3080f66f451Sopenharmony_ci    freeaddrinfo(ai2);
3090f66f451Sopenharmony_ci    if (ifa2) freeifaddrs(ifa2);
3100f66f451Sopenharmony_ci  }
3110f66f451Sopenharmony_ci}
312