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