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