1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43#define FOR_ping
44#include "toys.h"
45
46#include <ifaddrs.h>
47#include <netinet/ip_icmp.h>
48
49GLOBALS(
50 long w;
51 long W;
52 char *i;
53 char *I;
54 long s;
55 long c;
56 long t;
57 long m;
58
59 struct sockaddr *sa;
60 int sock;
61 long i_ms;
62 unsigned long sent, recv, fugit, min, max;
63)
64
65static void xsendto(int sockfd, void *buf, size_t len, struct sockaddr *dest)
66{
67 int rc = sendto(TT.sock, buf, len, 0, dest,
68 dest->sa_family == AF_INET ? sizeof(struct sockaddr_in) :
69 sizeof(struct sockaddr_in6));
70
71 if (rc != len) perror_exit("sendto");
72}
73
74static void summary(int sig)
75{
76 if (!(toys.optflags&FLAG_q) && TT.sent && TT.sa) {
77 printf("\n--- %s ping statistics ---\n", ntop(TT.sa));
78 printf("%lu packets transmitted, %lu received, %ld%% packet loss\n",
79 TT.sent, TT.recv, ((TT.sent-TT.recv)*100)/TT.sent);
80 printf("round-trip min/avg/max = %lu/%lu/%lu ms\n",
81 TT.min, TT.max, TT.fugit/TT.recv);
82 }
83 TT.sa = 0;
84}
85
86
87static unsigned short pingchksum(unsigned short *data, int len)
88{
89 unsigned short u = 0, d;
90
91
92 while (len>0) {
93 d = *data++;
94 if (len == 1) d &= 255<<IS_BIG_ENDIAN;
95 if (d >= (u += d)) u++;
96 len -= 2;
97 }
98
99 return u;
100}
101
102void ping_main(void)
103{
104 struct addrinfo *ai, *ai2;
105 struct ifaddrs *ifa, *ifa2 = 0;
106 union {
107 struct sockaddr_in in;
108 struct sockaddr_in6 in6;
109 } src_addr, src_addr2;
110 struct sockaddr *sa = (void *)&src_addr, *sa2 = (void *)&src_addr2;
111 struct pollfd pfd;
112 int family = 0, len;
113 long long tnext, tW, tnow, tw;
114 unsigned short seq = 0, pkttime;
115 struct icmphdr *ih = (void *)toybuf;
116
117
118 if (TT.i) {
119 long frac;
120
121 TT.i_ms = xparsetime(TT.i, 1000, &frac) * 1000;
122 TT.i_ms += frac;
123 if (TT.i_ms<200 && getuid()) error_exit("need root for -i <200");
124 } else TT.i_ms = (toys.optflags&FLAG_f) ? 200 : 1000;
125 if (!(toys.optflags&FLAG_s)) TT.s = 56;
126 if ((toys.optflags&(FLAG_f|FLAG_c)) == FLAG_f) TT.c = 15;
127
128
129 if ((toys.optflags&FLAG_6) || toys.which->name[4] == '6') family = AF_INET6;
130 else if (toys.optflags&FLAG_4) family = AF_INET;
131 else family = 0;
132
133 sigatexit(summary);
134
135
136 memset(&src_addr, 0, sizeof(src_addr));
137 if (TT.I) {
138 if (!(toys.optflags&FLAG_6) && inet_pton(AF_INET, TT.I,
139 (void *)&src_addr.in.sin_addr))
140 family = AF_INET;
141 else if (!(toys.optflags&FLAG_4) && inet_pton(AF_INET6, TT.I,
142 (void *)&src_addr.in6.sin6_addr))
143 family = AF_INET6;
144 else if (getifaddrs(&ifa2)) perror_exit("getifaddrs");
145 }
146
147
148
149 ai2 = xgetaddrinfo(*toys.optargs, 0, family, 0, 0, 0);
150 for (ai = ai2; ai; ai = ai->ai_next) {
151
152
153 if (family && family!=ai->ai_family) continue;
154 if (ai->ai_family!=AF_INET && ai->ai_family!=AF_INET6) continue;
155
156
157 if (!TT.I || !ifa2) break;
158 for (ifa = ifa2; ifa; ifa = ifa->ifa_next) {
159 if (!ifa->ifa_addr || ifa->ifa_addr->sa_family!=ai->ai_family
160 || strcmp(ifa->ifa_name, TT.I)) continue;
161 sa = (void *)ifa->ifa_addr;
162
163 break;
164 }
165 if (ifa) break;
166 }
167
168 if (!ai)
169 error_exit("no v%d addr for -I %s", 4+2*(family==AF_INET6), TT.I);
170 TT.sa = ai->ai_addr;
171
172
173 sa->sa_family = ai->ai_family;
174 TT.sock = socket(ai->ai_family, SOCK_DGRAM,
175 len = (ai->ai_family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6);
176 if (TT.sock == -1) {
177 perror_msg("socket SOCK_DGRAM %x", len);
178 if (errno == EACCES) {
179 fprintf(stderr, "Kernel bug workaround (as root):\n");
180 fprintf(stderr, "echo 0 9999999 > /proc/sys/net/ipv4/ping_group_range\n");
181 }
182 xexit();
183 }
184 if (TT.I && bind(TT.sock, sa, sizeof(src_addr))) perror_exit("bind");
185
186 if (toys.optflags&FLAG_m) {
187 int mark = TT.m;
188
189 xsetsockopt(TT.sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark));
190 }
191
192 if (TT.t) {
193 len = TT.t;
194
195 if (ai->ai_family == AF_INET)
196 xsetsockopt(TT.sock, IPPROTO_IP, IP_TTL, &len, 4);
197 else xsetsockopt(TT.sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &len, 4);
198 }
199
200 if (!(toys.optflags&FLAG_q)) {
201 printf("Ping %s (%s)", *toys.optargs, ntop(TT.sa));
202 if (TT.I) {
203 *toybuf = 0;
204 printf(" from %s (%s)", TT.I, ntop(sa));
205 }
206
207 printf(": %ld(%ld) bytes.\n", TT.s, TT.s+28);
208 }
209 toys.exitval = 1;
210
211 tW = tw = 0;
212 tnext = millitime();
213 if (TT.w) tw = TT.w*1000+tnext;
214
215
216 for (;;) {
217 int waitms = INT_MAX;
218
219
220
221 tnow = millitime();
222 if (tW) if (0>=(waitms = tW-tnow) || !(TT.sent-TT.recv)) break;
223 if (tw) {
224 if (tnow>tw) break;
225 else if (waitms>tw-tnow) waitms = tw-tnow;
226
227 } else if (tnext-tnow <= 0) {
228 tnext += TT.i_ms;
229
230 memset(ih, 0, sizeof(*ih));
231 ih->type = (ai->ai_family == AF_INET) ? 8 : 128;
232 ih->un.echo.id = getpid();
233 ih->un.echo.sequence = ++seq;
234 if (TT.s >= 4) *(unsigned *)(ih+1) = tnow;
235
236 ih->checksum = 0;
237 ih->checksum = pingchksum((void *)toybuf, TT.s+sizeof(*ih));
238 xsendto(TT.sock, toybuf, TT.s+sizeof(*ih), TT.sa);
239 TT.sent++;
240 if ((toys.optflags&(FLAG_f|FLAG_q)) == FLAG_f) xputc('.');
241
242
243 if (TT.c) if (!--TT.c) {
244 if (!TT.W) break;
245 tW = tnow + TT.W*1000;
246 }
247 }
248
249
250 if (!tw && waitms>tnext-tnow) waitms = tnext-tnow;
251
252
253
254 if (waitms<0) waitms = 0;
255 pfd.fd = TT.sock;
256 pfd.events = POLLIN;
257 if (0>(len = poll(&pfd, 1, waitms))) break;
258 if (!len) continue;
259
260 len = sizeof(src_addr2);
261 len = recvfrom(TT.sock, toybuf, sizeof(toybuf), 0, sa2, (void *)&len);
262 TT.recv++;
263 TT.fugit += (pkttime = millitime()-*(unsigned *)(ih+1));
264
265
266
267 if (!(toys.optflags&FLAG_q)) {
268 if (toys.optflags&FLAG_f) xputc('\b');
269 else {
270 printf("%d bytes from %s: icmp_seq=%d ttl=%d", len, ntop(sa2),
271 ih->un.echo.sequence, 0);
272 if (len >= sizeof(*ih)+4)
273 printf(" time=%u ms", pkttime);
274 xputc('\n');
275 }
276 }
277
278 toys.exitval = 0;
279 }
280
281 summary(0);
282
283 if (CFG_TOYBOX_FREE) {
284 freeaddrinfo(ai2);
285 if (ifa2) freeifaddrs(ifa2);
286 }
287}
288