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 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
GLOBALS(long w;long W;char * i;char * I;long s;long c;long t;int sock;long i_ms;)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
pingchksum(unsigned short * data,int len)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
ping_main(void)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