• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 $(((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 // -s > 4088 = sizeof(toybuf)-sizeof(struct icmphdr), then kernel adds 20 bytes
14 USE_PING(NEWTOY(ping, "<1>1m#t#<0>255=64c#<0=3s#<0>4088=56i%W#<0=3w#<0qf46I:[-46]", TOYFLAG_USR|TOYFLAG_BIN))
15 
16 config PING
17   bool "ping"
18   default y
19   help
20     usage: ping [OPTIONS] HOST
21 
22     Check network connectivity by sending packets to a host and reporting
23     its response.
24 
25     Send ICMP ECHO_REQUEST packets to ipv4 or ipv6 addresses and prints each
26     echo it receives back, with round trip time. Returns true if host alive.
27 
28     Options:
29     -4,		Force IPv4
30     -c CNT		Send CNT many packets (default 3, 0 = infinite)
31     -f		Flood (print . and \b to show drops, default -c 15 -i 0.2)
32     -i TIME		Interval between packets (default 1, need root for < .2)
33     -q		Quiet (stops after one returns true if host is alive)
34     -s SIZE		Data SIZE in bytes (default 56)
35 */
36 
37 #define FOR_ping
38 #include "toys.h"
39 
40 #include <ifaddrs.h>
41 #include <netinet/ip_icmp.h>
42 
GLOBALS(char * I;long w,W,i,s,c,t,m;struct sockaddr * sa;int sock;unsigned long sent,recv,fugit,min,max;)43 GLOBALS(
44   char *I;
45   long w, W, i, s, c, t, m;
46 
47   struct sockaddr *sa;
48   int sock;
49   unsigned long sent, recv, fugit, min, max;
50 )
51 
52 // Print a summary. Called as a single handler or at exit.
53 static void summary(int sig)
54 {
55   unsigned long lostret = 0;
56   lostret = TT.sent > TT.recv ? ((TT.sent-TT.recv)*100)/(TT.sent?TT.sent:1) : 0;
57   if (!(toys.optflags&FLAG_q) && TT.sent && TT.sa) {
58     printf("\n--- %s ping statistics ---\n", ntop(TT.sa));
59     printf("%lu packets transmitted, %lu received, %lu%% packet loss\n",
60       TT.sent, TT.recv, lostret);
61     printf("round-trip min/avg/max = %lu/%lu/%lu ms\n",
62       TT.min, TT.max, TT.fugit/(TT.recv?TT.recv:1));
63   }
64   TT.sa = 0;
65 }
66 
67 // assumes aligned and can read even number of bytes
pingchksum(unsigned short * data,int len)68 static unsigned short pingchksum(unsigned short *data, int len)
69 {
70   u_int32_t sum = 0;
71   int nwords = len >> 1;
72 
73   while (nwords-- != 0) sum += *data++;
74   if (len & 1) {
75     union {
76       u_int16_t w;
77       u_int8_t c[2];
78     } u;
79     u.c[0] = *(u_char *) data;
80     u.c[1] = 0;
81     sum += u.w;
82   }
83   // end-around-carry
84   sum = (sum >> 16) + (sum & 0xffff);
85   sum += (sum >> 16);
86   return (~sum);
87 }
88 
ping_main(void)89 void ping_main(void)
90 {
91   struct addrinfo *ai, *ai2;
92   struct ifaddrs *ifa, *ifa2 = 0;
93   struct icmphdr *ih = (void *)toybuf;
94   union socksaddr srcaddr, srcaddr2;
95   struct sockaddr *sa = (void *)&srcaddr;
96   int family = 0, len;
97   int recvLen = 0;
98   long long tnext, tW, tnow, tw;
99   unsigned short seq = 0 ,pkttime;
100   long long tmp_tnow;
101 
102   // Set nonstatic default values
103   if (!(toys.optflags&FLAG_i)) TT.i = (toys.optflags&FLAG_f) ? 200 : 1000;
104   else if (TT.i<200 && getuid()) error_exit("need root for -i <200");
105   if (!(toys.optflags&FLAG_s)) TT.s = 56; // 64-PHDR_LEN
106   if ((toys.optflags&(FLAG_f|FLAG_c)) == FLAG_f) TT.c = 15;
107 
108   // ipv4 or ipv6? (0 = autodetect if -I or arg have only one address type.)
109   if (FLAG(6) || strchr(toys.which->name, '6')) family = AF_INET6;
110   else if (FLAG(4)) family = AF_INET;
111   else family = 0;
112 
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 (!(toys.optflags&FLAG_6) && inet_pton(AF_INET, TT.I,
117       (void *)&srcaddr.in.sin_addr))
118         family = AF_INET;
119     else if (!(toys.optflags&FLAG_4) && inet_pton(AF_INET6, TT.I,
120       (void *)&srcaddr.in6.sin6_addr))
121         family = AF_INET6;
122     else if (getifaddrs(&ifa2)) perror_exit("getifaddrs");
123   }
124 
125   // Look up HOST address, filtering for correct type and interface.
126   // If -I but no -46 then find compatible type between -I and HOST
127   ai2 = xgetaddrinfo(*toys.optargs, 0, family, 0, 0, 0);
128   for (ai = ai2; ai; ai = ai->ai_next) {
129 
130     // correct type?
131     if (family && family!=ai->ai_family) continue;
132     if (ai->ai_family!=AF_INET && ai->ai_family!=AF_INET6) continue;
133 
134     // correct interface?
135     if (!TT.I || !ifa2) break;
136     for (ifa = ifa2; ifa; ifa = ifa->ifa_next) {
137       if (!ifa->ifa_addr || ifa->ifa_addr->sa_family!=ai->ai_family
138           || strcmp(ifa->ifa_name, TT.I)) continue;
139       sa = (void *)ifa->ifa_addr;
140 
141       break;
142     }
143     if (ifa) break;
144   }
145 
146   if (!ai)
147     error_exit("no v%d addr for -I %s", 4+2*(family==AF_INET6), TT.I);
148   TT.sa = ai->ai_addr;
149 
150   // Open DGRAM socket
151   sa->sa_family = ai->ai_family;
152   TT.sock = socket(ai->ai_family, SOCK_RAW,
153     len = (ai->ai_family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6);
154 
155   if (TT.sock == -1) {
156     perror_msg("socket SOCK_DGRAM %x", len);
157     if (errno == EACCES) {
158       fprintf(stderr, "Kernel bug workaround (as root):\n");
159       fprintf(stderr, "echo 0 9999999 > /proc/sys/net/ipv4/ping_group_range\n");
160     }
161     xexit();
162   }
163   if (TT.I) xbind(TT.sock, sa, sizeof(srcaddr));
164 
165   if (toys.optflags&FLAG_m) {
166       int mark = TT.m;
167 
168       xsetsockopt(TT.sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark));
169   }
170 
171   if (TT.t) {
172     len = TT.t;
173 
174     if (ai->ai_family == AF_INET)
175       xsetsockopt(TT.sock, IPPROTO_IP, IP_TTL, &len, 4);
176     else xsetsockopt(TT.sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &len, 4);
177   }
178 
179   if (!(toys.optflags&FLAG_q)) {
180     printf("Ping %s (%s)", *toys.optargs, ntop(TT.sa));
181     if (TT.I) {
182       *toybuf = 0;
183       printf(" from %s (%s)", TT.I, ntop(sa));
184     }
185     // 20 byte TCP header, 8 byte ICMP header, plus data payload
186     printf(": %ld(%ld) bytes.\n", TT.s, TT.s+28);
187   }
188   toys.exitval = 1;
189 
190   tW = tw = 0;
191   tnext = millitime();
192   if (TT.w) tw = TT.w*1000+tnext;
193 
194   // Send/receive packets
195   for (;;) {
196     int waitms = INT_MAX;
197 
198     // Exit due to timeout? (TODO: timeout is after last packet, waiting if
199     // any packets ever dropped. Not timeout since packet was dropped.)
200     tnow = millitime();
201     if (tW) {
202       if (0>=(waitms = tW-tnow) || !(TT.sent-TT.recv)) break;
203       waitms = tW-tnow;
204     }
205     if (tw) {
206       if (tnow>tw) break;
207       else if (waitms>tw-tnow) waitms = tw-tnow;
208     }
209 
210     // Time to send the next packet?
211     if (!tW && tnext-tnow <= 0) {
212       tnext += TT.i;
213 
214       memset(ih, 0, sizeof(*ih));
215       ih->type = (ai->ai_family == AF_INET) ? 8 : 128;
216       ih->un.echo.id = getpid();
217       ih->un.echo.sequence = ++seq;
218       if (TT.s >= 4) tmp_tnow = tnow;
219 
220       ih->checksum = 0;
221       ih->checksum = pingchksum((void *)toybuf, TT.s+sizeof(*ih));
222       xsendto(TT.sock, toybuf, TT.s+sizeof(*ih), TT.sa);
223       TT.sent++;
224       if ((toys.optflags&(FLAG_f|FLAG_q)) == FLAG_f) xputc('.');
225 
226       // last packet?
227       if (TT.c) if (!--TT.c) {
228         tW = tnow + TT.W*1000;
229         waitms = 1; // check for immediate return even when W=0
230       }
231     }
232 
233     // This is down here so it's against new period if we just sent a packet
234     if (!tW && waitms>tnext-tnow) waitms = tnext-tnow;
235 
236     // wait for next packet or timeout
237     if (waitms<0) waitms = 0;
238     len = xrecvwait(TT.sock, toybuf, sizeof(toybuf), &srcaddr2, waitms);
239     recvLen += len;
240     if ((len == 0) && (recvLen != 0)) {
241       TT.recv++;
242 
243       // reply id == 0 for ipv4, 129 for ipv6
244       if (!(toys.optflags&FLAG_q)) {
245         if (toys.optflags&FLAG_f) xputc('\b');
246         else {
247           printf("len %d bytes from %s: icmp_seq=%d ttl=%d", recvLen, ntop(&srcaddr2.s),
248                  ih->un.echo.sequence, 0);
249           if (len >= sizeof(*ih)+4)
250             printf(" time=%u ms", pkttime);
251           xputc('\n');
252         }
253       }
254       recvLen = 0;
255       continue;
256     }
257     TT.fugit += (pkttime = millitime()-tmp_tnow);
258     toys.exitval = 0;
259   }
260 
261   summary(0);
262 
263   if (CFG_TOYBOX_FREE) {
264     freeaddrinfo(ai2);
265     if (ifa2) freeifaddrs(ifa2);
266   }
267 }
268