ping.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. /* ping.c - check network connectivity
  2. *
  3. * Copyright 2014 Rob Landley <rob@landley.net>
  4. *
  5. * Not in SUSv4.
  6. *
  7. * Note: ping_group_range should never have existed. To disable it, do:
  8. * echo 0 999999999 > /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. // -s > 4064 = sizeof(toybuf)-sizeof(struct icmphdr)-CMSG_SPACE(sizeof(uint8_t)), then kernel adds 20 bytes
  13. USE_PING(NEWTOY(ping, "<1>1m#t#<0>255=64c#<0=3s#<0>4064=56i%W#<0=3w#<0qf46I:[-46]", TOYFLAG_USR|TOYFLAG_BIN))
  14. USE_PING(OLDTOY(ping6, ping, TOYFLAG_USR|TOYFLAG_BIN))
  15. config PING
  16. bool "ping"
  17. default y
  18. help
  19. usage: ping [OPTIONS] HOST
  20. Check network connectivity by sending packets to a host and reporting
  21. its response.
  22. Send ICMP ECHO_REQUEST packets to ipv4 or ipv6 addresses and prints each
  23. echo it receives back, with round trip time. Returns true if host alive.
  24. Options:
  25. -4, -6 Force IPv4 or IPv6
  26. -c CNT Send CNT many packets (default 3, 0 = infinite)
  27. -f Flood (print . and \b to show drops, default -c 15 -i 0.2)
  28. -i TIME Interval between packets (default 1, need root for < .2)
  29. -I IFACE/IP Source interface or address
  30. -m MARK Tag outgoing packets using SO_MARK
  31. -q Quiet (stops after one returns true if host is alive)
  32. -s SIZE Data SIZE in bytes (default 56)
  33. -t TTL Set Time To Live (number of hops)
  34. -W SEC Seconds to wait for response after last -c packet (default 3)
  35. -w SEC Exit after this many seconds
  36. */
  37. #define FOR_ping
  38. #include "toys.h"
  39. #include <ifaddrs.h>
  40. #include <netinet/ip_icmp.h>
  41. GLOBALS(
  42. char *I;
  43. long w, W, i, s, c, t, m;
  44. struct sockaddr *sa;
  45. int sock;
  46. unsigned long sent, recv, fugit, min, max;
  47. )
  48. // Print a summary. Called as a single handler or at exit.
  49. static void summary(int sig)
  50. {
  51. if (!FLAG(q) && TT.sent && TT.sa) {
  52. printf("\n--- %s ping statistics ---\n", ntop(TT.sa));
  53. printf("%lu packets transmitted, %lu received, %ld%% packet loss\n",
  54. TT.sent, TT.recv, ((TT.sent-TT.recv)*100)/(TT.sent?TT.sent:1));
  55. if (TT.recv)
  56. printf("round-trip min/avg/max = %lu/%lu/%lu ms\n",
  57. TT.min, TT.fugit/TT.recv, TT.max);
  58. }
  59. TT.sa = 0;
  60. }
  61. // assumes aligned and can read even number of bytes
  62. static unsigned short pingchksum(unsigned short *data, int len)
  63. {
  64. unsigned short u = 0, d;
  65. // circular carry is endian independent: bits from high byte go to low byte
  66. while (len>0) {
  67. d = *data++;
  68. if (len == 1) d &= 255<<IS_BIG_ENDIAN;
  69. if (d >= (u += d)) u++;
  70. len -= 2;
  71. }
  72. return u;
  73. }
  74. static int xrecvmsgwait(int fd, struct msghdr *msg, int flag,
  75. union socksaddr *sa, int timeout)
  76. {
  77. socklen_t sl = sizeof(*sa);
  78. int len;
  79. if (timeout >= 0) {
  80. struct pollfd pfd;
  81. pfd.fd = fd;
  82. pfd.events = POLLIN;
  83. if (!xpoll(&pfd, 1, timeout)) return 0;
  84. }
  85. msg->msg_name = (void *)sa;
  86. msg->msg_namelen = sl;
  87. len = recvmsg(fd, msg, flag);
  88. if (len<0) perror_exit("recvmsg");
  89. return len;
  90. }
  91. void ping_main(void)
  92. {
  93. struct addrinfo *ai, *ai2;
  94. struct ifaddrs *ifa, *ifa2 = 0;
  95. struct icmphdr *ih = (void *)toybuf;
  96. struct msghdr msg;
  97. struct cmsghdr *cmsg;
  98. struct iovec iov;
  99. union socksaddr srcaddr, srcaddr2;
  100. struct sockaddr *sa = (void *)&srcaddr;
  101. int family = 0, ttl = 0, len;
  102. long long tnext, tW, tnow, tw;
  103. unsigned short seq = 0, pkttime;
  104. // Set nonstatic default values
  105. if (!FLAG(i)) TT.i = FLAG(f) ? 200 : 1000;
  106. else if (TT.i<200 && geteuid()) error_exit("need root for -i <200");
  107. if (!FLAG(s)) TT.s = 56; // 64-PHDR_LEN
  108. if (FLAG(f) && !FLAG(c)) TT.c = 15;
  109. // ipv4 or ipv6? (0 = autodetect if -I or arg have only one address type.)
  110. if (FLAG(6) || strchr(toys.which->name, '6')) family = AF_INET6;
  111. else if (FLAG(4)) family = AF_INET;
  112. else family = 0;
  113. // If -I srcaddr look it up. Allow numeric address of correct type.
  114. memset(&srcaddr, 0, sizeof(srcaddr));
  115. if (TT.I) {
  116. if (!FLAG(6) && inet_pton(AF_INET, TT.I, (void *)&srcaddr.in.sin_addr))
  117. family = AF_INET;
  118. else if (!FLAG(4) && inet_pton(AF_INET6, TT.I, (void *)&srcaddr.in6.sin6_addr))
  119. family = AF_INET6;
  120. else if (getifaddrs(&ifa2)) perror_exit("getifaddrs");
  121. }
  122. // Look up HOST address, filtering for correct type and interface.
  123. // If -I but no -46 then find compatible type between -I and HOST
  124. ai2 = xgetaddrinfo(*toys.optargs, 0, family, 0, 0, 0);
  125. for (ai = ai2; ai; ai = ai->ai_next) {
  126. // correct type?
  127. if (family && family!=ai->ai_family) continue;
  128. if (ai->ai_family!=AF_INET && ai->ai_family!=AF_INET6) continue;
  129. // correct interface?
  130. if (!TT.I || !ifa2) break;
  131. for (ifa = ifa2; ifa; ifa = ifa->ifa_next) {
  132. if (!ifa->ifa_addr || ifa->ifa_addr->sa_family!=ai->ai_family
  133. || strcmp(ifa->ifa_name, TT.I)) continue;
  134. sa = (void *)ifa->ifa_addr;
  135. break;
  136. }
  137. if (ifa) break;
  138. }
  139. if (!ai)
  140. error_exit("no v%d addr for -I %s", 4+2*(family==AF_INET6), TT.I);
  141. TT.sa = ai->ai_addr;
  142. // Open DGRAM socket
  143. sa->sa_family = ai->ai_family;
  144. TT.sock = socket(ai->ai_family, SOCK_DGRAM,
  145. len = (ai->ai_family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6);
  146. if (TT.sock == -1) {
  147. perror_msg("socket SOCK_DGRAM %x", len);
  148. if (errno == EACCES) {
  149. fprintf(stderr, "Kernel bug workaround:\n"
  150. "echo 0 99999999 | sudo tee /proc/sys/net/ipv4/ping_group_range\n");
  151. }
  152. xexit();
  153. }
  154. if (TT.I) xbind(TT.sock, sa, sizeof(srcaddr));
  155. len = 1;
  156. xsetsockopt(TT.sock, SOL_IP, IP_RECVTTL, &len, sizeof(len));
  157. if (FLAG(m)) {
  158. len = TT.m;
  159. xsetsockopt(TT.sock, SOL_SOCKET, SO_MARK, &len, sizeof(len));
  160. }
  161. if (TT.t) {
  162. len = TT.t;
  163. if (ai->ai_family == AF_INET)
  164. xsetsockopt(TT.sock, IPPROTO_IP, IP_TTL, &len, 4);
  165. else xsetsockopt(TT.sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &len, sizeof(len));
  166. }
  167. if (!FLAG(q)) {
  168. printf("Ping %s (%s)", *toys.optargs, ntop(TT.sa));
  169. if (TT.I) {
  170. *toybuf = 0;
  171. printf(" from %s (%s)", TT.I, ntop(sa));
  172. }
  173. // 20 byte TCP header, 8 byte ICMP header, plus data payload
  174. printf(": %ld(%ld) bytes.\n", TT.s, TT.s+28);
  175. }
  176. TT.min = ULONG_MAX;
  177. toys.exitval = 1;
  178. tW = tw = 0;
  179. tnext = millitime();
  180. if (TT.w) tw = TT.w*1000+tnext;
  181. memset(&msg, 0, sizeof(msg));
  182. // left enought space to store ttl value
  183. len = CMSG_SPACE(sizeof(uint8_t));
  184. iov.iov_base = (void *)toybuf;
  185. iov.iov_len = sizeof(toybuf) - len;
  186. msg.msg_iov = &iov;
  187. msg.msg_iovlen = 1;
  188. msg.msg_control = &toybuf[iov.iov_len];
  189. msg.msg_controllen = len;
  190. sigatexit(summary);
  191. // Send/receive packets
  192. for (;;) {
  193. int waitms = INT_MAX;
  194. // Exit due to timeout? (TODO: timeout is after last packet, waiting if
  195. // any packets ever dropped. Not timeout since packet was dropped.)
  196. tnow = millitime();
  197. if (tW) {
  198. if (0>=(waitms = tW-tnow) || !(TT.sent-TT.recv)) break;
  199. waitms = tW-tnow;
  200. }
  201. if (tw) {
  202. if (tnow>tw) break;
  203. else if (waitms>tw-tnow) waitms = tw-tnow;
  204. }
  205. // Time to send the next packet?
  206. if (!tW && tnext-tnow <= 0) {
  207. tnext += TT.i;
  208. memset(ih, 0, sizeof(*ih));
  209. ih->type = (ai->ai_family == AF_INET) ? 8 : 128;
  210. ih->un.echo.id = getpid();
  211. ih->un.echo.sequence = ++seq;
  212. if (TT.s >= 4) *(unsigned *)(ih+1) = tnow;
  213. ih->checksum = pingchksum((void *)toybuf, TT.s+sizeof(*ih));
  214. xsendto(TT.sock, toybuf, TT.s+sizeof(*ih), TT.sa);
  215. TT.sent++;
  216. if (FLAG(f) && !FLAG(q)) xputc('.');
  217. // last packet?
  218. if (TT.c) if (!--TT.c) {
  219. tW = tnow + TT.W*1000;
  220. waitms = 1; // check for immediate return even when W=0
  221. }
  222. }
  223. // This is down here so it's against new period if we just sent a packet
  224. if (!tW && waitms>tnext-tnow) waitms = tnext-tnow;
  225. // wait for next packet or timeout
  226. if (waitms<0) waitms = 0;
  227. if (!(len = xrecvmsgwait(TT.sock, &msg, 0, &srcaddr2, waitms)))
  228. continue;
  229. TT.recv++;
  230. TT.fugit += (pkttime = millitime()-*(unsigned *)(ih+1));
  231. if (pkttime < TT.min) TT.min = pkttime;
  232. if (pkttime > TT.max) TT.max = pkttime;
  233. // reply id == 0 for ipv4, 129 for ipv6
  234. cmsg = CMSG_FIRSTHDR(&msg);
  235. for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
  236. if (cmsg->cmsg_level == IPPROTO_IP
  237. && cmsg->cmsg_type == IP_TTL) {
  238. ttl = *(uint8_t *)CMSG_DATA(cmsg);
  239. break;
  240. }
  241. };
  242. if (!FLAG(q)) {
  243. if (FLAG(f)) xputc('\b');
  244. else {
  245. printf("%d bytes from %s: icmp_seq=%d ttl=%d", len, ntop(&srcaddr2.s),
  246. ih->un.echo.sequence, ttl);
  247. if (len >= sizeof(*ih)+4) printf(" time=%u ms", pkttime);
  248. xputc('\n');
  249. }
  250. }
  251. toys.exitval = 0;
  252. }
  253. // summary(0) gets called for us atexit.
  254. if (CFG_TOYBOX_FREE) {
  255. freeaddrinfo(ai2);
  256. if (ifa2) freeifaddrs(ifa2);
  257. }
  258. }