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