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 char *I;
51 long w, W, i, s, c, t, m;
52
53 struct sockaddr *sa;
54 int sock;
55 unsigned long sent, recv, fugit, min, max;
56)
57
58
59static void summary(int sig)
60{
61 if (!FLAG(q) && TT.sent && TT.sa) {
62 printf("\n--- %s ping statistics ---\n", ntop(TT.sa));
63 printf("%lu packets transmitted, %lu received, %ld%% packet loss\n",
64 TT.sent, TT.recv, ((TT.sent-TT.recv)*100)/(TT.sent?TT.sent:1));
65 if (TT.recv)
66 printf("round-trip min/avg/max = %lu/%lu/%lu ms\n",
67 TT.min, TT.fugit/TT.recv, TT.max);
68 }
69 TT.sa = 0;
70}
71
72
73static unsigned short pingchksum(unsigned short *data, int len)
74{
75 unsigned short u = 0, d;
76
77
78 while (len>0) {
79 d = *data++;
80 if (len == 1) d &= 255<<IS_BIG_ENDIAN;
81 if (d >= (u += d)) u++;
82 len -= 2;
83 }
84
85 return u;
86}
87
88static int xrecvmsgwait(int fd, struct msghdr *msg, int flag,
89 union socksaddr *sa, int timeout)
90{
91 socklen_t sl = sizeof(*sa);
92 int len;
93
94 if (timeout >= 0) {
95 struct pollfd pfd;
96
97 pfd.fd = fd;
98 pfd.events = POLLIN;
99 if (!xpoll(&pfd, 1, timeout)) return 0;
100 }
101
102 msg->msg_name = (void *)sa;
103 msg->msg_namelen = sl;
104 len = recvmsg(fd, msg, flag);
105 if (len<0) perror_exit("recvmsg");
106
107 return len;
108}
109
110void ping_main(void)
111{
112 struct addrinfo *ai, *ai2;
113 struct ifaddrs *ifa, *ifa2 = 0;
114 struct icmphdr *ih = (void *)toybuf;
115 struct msghdr msg;
116 struct cmsghdr *cmsg;
117 struct iovec iov;
118 union socksaddr srcaddr, srcaddr2;
119 struct sockaddr *sa = (void *)&srcaddr;
120 int family = 0, ttl = 0, len;
121 long long tnext, tW, tnow, tw;
122 unsigned short seq = 0, pkttime;
123
124
125 if (!FLAG(i)) TT.i = FLAG(f) ? 200 : 1000;
126 else if (TT.i<200 && geteuid()) error_exit("need root for -i <200");
127 if (!FLAG(s)) TT.s = 56;
128 if (FLAG(f) && !FLAG(c)) TT.c = 15;
129
130
131 if (FLAG(6) || strchr(toys.which->name, '6')) family = AF_INET6;
132 else if (FLAG(4)) family = AF_INET;
133 else family = 0;
134
135
136 memset(&srcaddr, 0, sizeof(srcaddr));
137 if (TT.I) {
138 if (!FLAG(6) && inet_pton(AF_INET, TT.I, (void *)&srcaddr.in.sin_addr))
139 family = AF_INET;
140 else if (!FLAG(4) && inet_pton(AF_INET6, TT.I, (void *)&srcaddr.in6.sin6_addr))
141 family = AF_INET6;
142 else if (getifaddrs(&ifa2)) perror_exit("getifaddrs");
143 }
144
145
146
147 ai2 = xgetaddrinfo(*toys.optargs, 0, family, 0, 0, 0);
148 for (ai = ai2; ai; ai = ai->ai_next) {
149
150
151 if (family && family!=ai->ai_family) continue;
152 if (ai->ai_family!=AF_INET && ai->ai_family!=AF_INET6) continue;
153
154
155 if (!TT.I || !ifa2) break;
156 for (ifa = ifa2; ifa; ifa = ifa->ifa_next) {
157 if (!ifa->ifa_addr || ifa->ifa_addr->sa_family!=ai->ai_family
158 || strcmp(ifa->ifa_name, TT.I)) continue;
159 sa = (void *)ifa->ifa_addr;
160
161 break;
162 }
163 if (ifa) break;
164 }
165
166 if (!ai)
167 error_exit("no v%d addr for -I %s", 4+2*(family==AF_INET6), TT.I);
168 TT.sa = ai->ai_addr;
169
170
171 sa->sa_family = ai->ai_family;
172 TT.sock = socket(ai->ai_family, SOCK_DGRAM,
173 len = (ai->ai_family == AF_INET) ? IPPROTO_ICMP : IPPROTO_ICMPV6);
174 if (TT.sock == -1) {
175 perror_msg("socket SOCK_DGRAM %x", len);
176 if (errno == EACCES) {
177 fprintf(stderr, "Kernel bug workaround:\n"
178 "echo 0 99999999 | sudo tee /proc/sys/net/ipv4/ping_group_range\n");
179 }
180 xexit();
181 }
182 if (TT.I) xbind(TT.sock, sa, sizeof(srcaddr));
183
184 len = 1;
185 xsetsockopt(TT.sock, SOL_IP, IP_RECVTTL, &len, sizeof(len));
186
187 if (FLAG(m)) {
188 len = TT.m;
189 xsetsockopt(TT.sock, SOL_SOCKET, SO_MARK, &len, sizeof(len));
190 }
191
192 if (TT.t) {
193 len = TT.t;
194 if (ai->ai_family == AF_INET)
195 xsetsockopt(TT.sock, IPPROTO_IP, IP_TTL, &len, 4);
196 else xsetsockopt(TT.sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &len, sizeof(len));
197 }
198
199 if (!FLAG(q)) {
200 printf("Ping %s (%s)", *toys.optargs, ntop(TT.sa));
201 if (TT.I) {
202 *toybuf = 0;
203 printf(" from %s (%s)", TT.I, ntop(sa));
204 }
205
206 printf(": %ld(%ld) bytes.\n", TT.s, TT.s+28);
207 }
208
209 TT.min = ULONG_MAX;
210 toys.exitval = 1;
211
212 tW = tw = 0;
213 tnext = millitime();
214 if (TT.w) tw = TT.w*1000+tnext;
215
216 memset(&msg, 0, sizeof(msg));
217
218 len = CMSG_SPACE(sizeof(uint8_t));
219 iov.iov_base = (void *)toybuf;
220 iov.iov_len = sizeof(toybuf) - len;
221 msg.msg_iov = &iov;
222 msg.msg_iovlen = 1;
223 msg.msg_control = &toybuf[iov.iov_len];
224 msg.msg_controllen = len;
225
226 sigatexit(summary);
227
228
229 for (;;) {
230 int waitms = INT_MAX;
231
232
233
234 tnow = millitime();
235 if (tW) {
236 if (0>=(waitms = tW-tnow) || !(TT.sent-TT.recv)) break;
237 waitms = tW-tnow;
238 }
239 if (tw) {
240 if (tnow>tw) break;
241 else if (waitms>tw-tnow) waitms = tw-tnow;
242 }
243
244
245 if (!tW && tnext-tnow <= 0) {
246 tnext += TT.i;
247
248 memset(ih, 0, sizeof(*ih));
249 ih->type = (ai->ai_family == AF_INET) ? 8 : 128;
250 ih->un.echo.id = getpid();
251 ih->un.echo.sequence = ++seq;
252 if (TT.s >= 4) *(unsigned *)(ih+1) = tnow;
253
254 ih->checksum = pingchksum((void *)toybuf, TT.s+sizeof(*ih));
255 xsendto(TT.sock, toybuf, TT.s+sizeof(*ih), TT.sa);
256 TT.sent++;
257 if (FLAG(f) && !FLAG(q)) xputc('.');
258
259
260 if (TT.c) if (!--TT.c) {
261 tW = tnow + TT.W*1000;
262 waitms = 1;
263 }
264 }
265
266
267 if (!tW && waitms>tnext-tnow) waitms = tnext-tnow;
268
269
270
271 if (waitms<0) waitms = 0;
272 if (!(len = xrecvmsgwait(TT.sock, &msg, 0, &srcaddr2, waitms)))
273 continue;
274
275 TT.recv++;
276 TT.fugit += (pkttime = millitime()-*(unsigned *)(ih+1));
277 if (pkttime < TT.min) TT.min = pkttime;
278 if (pkttime > TT.max) TT.max = pkttime;
279
280
281
282 cmsg = CMSG_FIRSTHDR(&msg);
283 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
284 if (cmsg->cmsg_level == IPPROTO_IP
285 && cmsg->cmsg_type == IP_TTL) {
286 ttl = *(uint8_t *)CMSG_DATA(cmsg);
287 break;
288 }
289 };
290
291 if (!FLAG(q)) {
292 if (FLAG(f)) xputc('\b');
293 else {
294 printf("%d bytes from %s: icmp_seq=%d ttl=%d", len, ntop(&srcaddr2.s),
295 ih->un.echo.sequence, ttl);
296 if (len >= sizeof(*ih)+4) printf(" time=%u ms", pkttime);
297 xputc('\n');
298 }
299 }
300
301 toys.exitval = 0;
302 }
303
304
305 if (CFG_TOYBOX_FREE) {
306 freeaddrinfo(ai2);
307 if (ifa2) freeifaddrs(ifa2);
308 }
309}
310