1 |
/* vi: set sw=4 ts=4: */ |
/* vi: set sw=4 ts=4: */ |
2 |
/* |
/* |
|
* $Id: ping.c,v 1.1 2007-09-01 22:43:53 niro Exp $ |
|
3 |
* Mini ping implementation for busybox |
* Mini ping implementation for busybox |
4 |
* |
* |
5 |
* Copyright (C) 1999 by Randolph Chung <tausq@debian.org> |
* Copyright (C) 1999 by Randolph Chung <tausq@debian.org> |
6 |
* |
* |
7 |
* Adapted from the ping in netkit-base 0.10: |
* Adapted from the ping in netkit-base 0.10: |
8 |
* Copyright (c) 1989 The Regents of the University of California. |
* Copyright (c) 1989 The Regents of the University of California. |
9 |
* Derived from software contributed to Berkeley by Mike Muuss. |
* All rights reserved. |
10 |
|
* |
11 |
|
* This code is derived from software contributed to Berkeley by |
12 |
|
* Mike Muuss. |
13 |
* |
* |
14 |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
15 |
*/ |
*/ |
16 |
|
/* from ping6.c: |
17 |
|
* Copyright (C) 1999 by Randolph Chung <tausq@debian.org> |
18 |
|
* |
19 |
|
* This version of ping is adapted from the ping in netkit-base 0.10, |
20 |
|
* which is: |
21 |
|
* |
22 |
|
* Original copyright notice is retained at the end of this file. |
23 |
|
* |
24 |
|
* This version is an adaptation of ping.c from busybox. |
25 |
|
* The code was modified by Bart Visscher <magick@linux-fan.com> |
26 |
|
*/ |
27 |
|
|
28 |
|
#include <net/if.h> |
29 |
#include <netinet/ip_icmp.h> |
#include <netinet/ip_icmp.h> |
30 |
#include "busybox.h" |
#include "libbb.h" |
31 |
|
|
32 |
|
#if ENABLE_PING6 |
33 |
|
#include <netinet/icmp6.h> |
34 |
|
/* I see RENUMBERED constants in bits/in.h - !!? |
35 |
|
* What a fuck is going on with libc? Is it a glibc joke? */ |
36 |
|
#ifdef IPV6_2292HOPLIMIT |
37 |
|
#undef IPV6_HOPLIMIT |
38 |
|
#define IPV6_HOPLIMIT IPV6_2292HOPLIMIT |
39 |
|
#endif |
40 |
|
#endif |
41 |
|
|
42 |
enum { |
enum { |
43 |
DEFDATALEN = 56, |
DEFDATALEN = 56, |
46 |
MAXPACKET = 65468, |
MAXPACKET = 65468, |
47 |
MAX_DUP_CHK = (8 * 128), |
MAX_DUP_CHK = (8 * 128), |
48 |
MAXWAIT = 10, |
MAXWAIT = 10, |
49 |
PINGINTERVAL = 1 /* second */ |
PINGINTERVAL = 1, /* 1 second */ |
50 |
}; |
}; |
51 |
|
|
|
static void ping(const char *host); |
|
|
|
|
52 |
/* common routines */ |
/* common routines */ |
53 |
|
|
54 |
static int in_cksum(unsigned short *buf, int sz) |
static int in_cksum(unsigned short *buf, int sz) |
74 |
return ans; |
return ans; |
75 |
} |
} |
76 |
|
|
77 |
#ifndef CONFIG_FEATURE_FANCY_PING |
#if !ENABLE_FEATURE_FANCY_PING |
78 |
|
|
79 |
/* simple version */ |
/* simple version */ |
80 |
|
|
81 |
static char *hostname; |
static char *hostname; |
82 |
|
|
83 |
static void noresp(int ign) |
static void noresp(int ign UNUSED_PARAM) |
84 |
{ |
{ |
85 |
printf("No response from %s\n", hostname); |
printf("No response from %s\n", hostname); |
86 |
exit(EXIT_FAILURE); |
exit(EXIT_FAILURE); |
87 |
} |
} |
88 |
|
|
89 |
static void ping(const char *host) |
static void ping4(len_and_sockaddr *lsa) |
90 |
{ |
{ |
|
struct hostent *h; |
|
91 |
struct sockaddr_in pingaddr; |
struct sockaddr_in pingaddr; |
92 |
struct icmp *pkt; |
struct icmp *pkt; |
93 |
int pingsock, c; |
int pingsock, c; |
94 |
char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN]; |
char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN]; |
95 |
|
|
96 |
pingsock = create_icmp_socket(); |
pingsock = create_icmp_socket(); |
97 |
|
pingaddr = lsa->u.sin; |
|
memset(&pingaddr, 0, sizeof(struct sockaddr_in)); |
|
|
|
|
|
pingaddr.sin_family = AF_INET; |
|
|
h = xgethostbyname(host); |
|
|
memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr)); |
|
|
hostname = h->h_name; |
|
98 |
|
|
99 |
pkt = (struct icmp *) packet; |
pkt = (struct icmp *) packet; |
100 |
memset(pkt, 0, sizeof(packet)); |
memset(pkt, 0, sizeof(packet)); |
101 |
pkt->icmp_type = ICMP_ECHO; |
pkt->icmp_type = ICMP_ECHO; |
102 |
pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet)); |
pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet)); |
103 |
|
|
104 |
c = sendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN, 0, |
c = xsendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN, |
105 |
(struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in)); |
(struct sockaddr *) &pingaddr, sizeof(pingaddr)); |
106 |
|
|
|
if (c < 0) { |
|
|
if (ENABLE_FEATURE_CLEAN_UP) close(pingsock); |
|
|
bb_perror_msg_and_die("sendto"); |
|
|
} |
|
|
|
|
|
signal(SIGALRM, noresp); |
|
|
alarm(5); /* give the host 5000ms to respond */ |
|
107 |
/* listen for replies */ |
/* listen for replies */ |
108 |
while (1) { |
while (1) { |
109 |
struct sockaddr_in from; |
struct sockaddr_in from; |
124 |
break; |
break; |
125 |
} |
} |
126 |
} |
} |
127 |
if (ENABLE_FEATURE_CLEAN_UP) close(pingsock); |
if (ENABLE_FEATURE_CLEAN_UP) |
128 |
printf("%s is alive!\n", hostname); |
close(pingsock); |
129 |
|
} |
130 |
|
|
131 |
|
#if ENABLE_PING6 |
132 |
|
static void ping6(len_and_sockaddr *lsa) |
133 |
|
{ |
134 |
|
struct sockaddr_in6 pingaddr; |
135 |
|
struct icmp6_hdr *pkt; |
136 |
|
int pingsock, c; |
137 |
|
int sockopt; |
138 |
|
char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN]; |
139 |
|
|
140 |
|
pingsock = create_icmp6_socket(); |
141 |
|
pingaddr = lsa->u.sin6; |
142 |
|
|
143 |
|
pkt = (struct icmp6_hdr *) packet; |
144 |
|
memset(pkt, 0, sizeof(packet)); |
145 |
|
pkt->icmp6_type = ICMP6_ECHO_REQUEST; |
146 |
|
|
147 |
|
sockopt = offsetof(struct icmp6_hdr, icmp6_cksum); |
148 |
|
setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt)); |
149 |
|
|
150 |
|
c = xsendto(pingsock, packet, DEFDATALEN + sizeof (struct icmp6_hdr), |
151 |
|
(struct sockaddr *) &pingaddr, sizeof(pingaddr)); |
152 |
|
|
153 |
|
/* listen for replies */ |
154 |
|
while (1) { |
155 |
|
struct sockaddr_in6 from; |
156 |
|
socklen_t fromlen = sizeof(from); |
157 |
|
|
158 |
|
c = recvfrom(pingsock, packet, sizeof(packet), 0, |
159 |
|
(struct sockaddr *) &from, &fromlen); |
160 |
|
if (c < 0) { |
161 |
|
if (errno != EINTR) |
162 |
|
bb_perror_msg("recvfrom"); |
163 |
|
continue; |
164 |
|
} |
165 |
|
if (c >= 8) { /* icmp6_hdr */ |
166 |
|
pkt = (struct icmp6_hdr *) packet; |
167 |
|
if (pkt->icmp6_type == ICMP6_ECHO_REPLY) |
168 |
|
break; |
169 |
|
} |
170 |
|
} |
171 |
|
if (ENABLE_FEATURE_CLEAN_UP) |
172 |
|
close(pingsock); |
173 |
} |
} |
174 |
|
#endif |
175 |
|
|
176 |
int ping_main(int argc, char **argv) |
int ping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
177 |
|
int ping_main(int argc UNUSED_PARAM, char **argv) |
178 |
{ |
{ |
179 |
argc--; |
len_and_sockaddr *lsa; |
180 |
|
#if ENABLE_PING6 |
181 |
|
sa_family_t af = AF_UNSPEC; |
182 |
|
|
183 |
|
while ((++argv)[0] && argv[0][0] == '-') { |
184 |
|
if (argv[0][1] == '4') { |
185 |
|
af = AF_INET; |
186 |
|
continue; |
187 |
|
} |
188 |
|
if (argv[0][1] == '6') { |
189 |
|
af = AF_INET6; |
190 |
|
continue; |
191 |
|
} |
192 |
|
bb_show_usage(); |
193 |
|
} |
194 |
|
#else |
195 |
argv++; |
argv++; |
196 |
if (argc < 1) |
#endif |
197 |
|
|
198 |
|
hostname = *argv; |
199 |
|
if (!hostname) |
200 |
bb_show_usage(); |
bb_show_usage(); |
201 |
ping(*argv); |
|
202 |
|
#if ENABLE_PING6 |
203 |
|
lsa = xhost_and_af2sockaddr(hostname, 0, af); |
204 |
|
#else |
205 |
|
lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET); |
206 |
|
#endif |
207 |
|
/* Set timer _after_ DNS resolution */ |
208 |
|
signal(SIGALRM, noresp); |
209 |
|
alarm(5); /* give the host 5000ms to respond */ |
210 |
|
|
211 |
|
#if ENABLE_PING6 |
212 |
|
if (lsa->u.sa.sa_family == AF_INET6) |
213 |
|
ping6(lsa); |
214 |
|
else |
215 |
|
#endif |
216 |
|
ping4(lsa); |
217 |
|
printf("%s is alive!\n", hostname); |
218 |
return EXIT_SUCCESS; |
return EXIT_SUCCESS; |
219 |
} |
} |
220 |
|
|
221 |
#else /* ! CONFIG_FEATURE_FANCY_PING */ |
|
222 |
|
#else /* FEATURE_FANCY_PING */ |
223 |
|
|
224 |
|
|
225 |
/* full(er) version */ |
/* full(er) version */ |
226 |
|
|
227 |
#define OPT_STRING "qc:s:I:" |
#define OPT_STRING ("qvc:s:w:W:I:4" USE_PING6("6")) |
228 |
enum { |
enum { |
229 |
OPT_QUIET = 1 << 0, |
OPT_QUIET = 1 << 0, |
230 |
|
OPT_VERBOSE = 1 << 1, |
231 |
|
OPT_c = 1 << 2, |
232 |
|
OPT_s = 1 << 3, |
233 |
|
OPT_w = 1 << 4, |
234 |
|
OPT_W = 1 << 5, |
235 |
|
OPT_I = 1 << 6, |
236 |
|
OPT_IPV4 = 1 << 7, |
237 |
|
OPT_IPV6 = (1 << 8) * ENABLE_PING6, |
238 |
|
}; |
239 |
|
|
240 |
|
|
241 |
|
struct globals { |
242 |
|
int pingsock; |
243 |
|
int if_index; |
244 |
|
char *str_I; |
245 |
|
len_and_sockaddr *source_lsa; |
246 |
|
unsigned datalen; |
247 |
|
unsigned pingcount; /* must be int-sized */ |
248 |
|
unsigned long ntransmitted, nreceived, nrepeats; |
249 |
|
uint16_t myid; |
250 |
|
unsigned tmin, tmax; /* in us */ |
251 |
|
unsigned long long tsum; /* in us, sum of all times */ |
252 |
|
unsigned deadline; |
253 |
|
unsigned timeout; |
254 |
|
unsigned total_secs; |
255 |
|
const char *hostname; |
256 |
|
const char *dotted; |
257 |
|
union { |
258 |
|
struct sockaddr sa; |
259 |
|
struct sockaddr_in sin; |
260 |
|
#if ENABLE_PING6 |
261 |
|
struct sockaddr_in6 sin6; |
262 |
|
#endif |
263 |
|
} pingaddr; |
264 |
|
char rcvd_tbl[MAX_DUP_CHK / 8]; |
265 |
}; |
}; |
266 |
|
#define G (*(struct globals*)&bb_common_bufsiz1) |
267 |
|
#define pingsock (G.pingsock ) |
268 |
|
#define if_index (G.if_index ) |
269 |
|
#define source_lsa (G.source_lsa ) |
270 |
|
#define str_I (G.str_I ) |
271 |
|
#define datalen (G.datalen ) |
272 |
|
#define ntransmitted (G.ntransmitted) |
273 |
|
#define nreceived (G.nreceived ) |
274 |
|
#define nrepeats (G.nrepeats ) |
275 |
|
#define pingcount (G.pingcount ) |
276 |
|
#define myid (G.myid ) |
277 |
|
#define tmin (G.tmin ) |
278 |
|
#define tmax (G.tmax ) |
279 |
|
#define tsum (G.tsum ) |
280 |
|
#define deadline (G.deadline ) |
281 |
|
#define timeout (G.timeout ) |
282 |
|
#define total_secs (G.total_secs ) |
283 |
|
#define hostname (G.hostname ) |
284 |
|
#define dotted (G.dotted ) |
285 |
|
#define pingaddr (G.pingaddr ) |
286 |
|
#define rcvd_tbl (G.rcvd_tbl ) |
287 |
|
void BUG_ping_globals_too_big(void); |
288 |
|
#define INIT_G() do { \ |
289 |
|
if (sizeof(G) > COMMON_BUFSIZE) \ |
290 |
|
BUG_ping_globals_too_big(); \ |
291 |
|
pingsock = -1; \ |
292 |
|
datalen = DEFDATALEN; \ |
293 |
|
timeout = MAXWAIT; \ |
294 |
|
tmin = UINT_MAX; \ |
295 |
|
} while (0) |
296 |
|
|
|
static struct sockaddr_in pingaddr; |
|
|
static struct sockaddr_in sourceaddr; |
|
|
static int pingsock = -1; |
|
|
static unsigned datalen; /* intentionally uninitialized to work around gcc bug */ |
|
|
|
|
|
static unsigned long ntransmitted, nreceived, nrepeats, pingcount; |
|
|
static int myid; |
|
|
static unsigned long tmin = ULONG_MAX, tmax, tsum; |
|
|
static char rcvd_tbl[MAX_DUP_CHK / 8]; |
|
|
|
|
|
static struct hostent *hostent; |
|
|
|
|
|
static void sendping(int); |
|
|
static void pingstats(int); |
|
|
static void unpack(char *, int, struct sockaddr_in *); |
|
297 |
|
|
298 |
#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ |
#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ |
299 |
#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ |
#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ |
303 |
|
|
304 |
/**************************************************************************/ |
/**************************************************************************/ |
305 |
|
|
306 |
static void pingstats(int junk) |
static void print_stats_and_exit(int junk) NORETURN; |
307 |
|
static void print_stats_and_exit(int junk UNUSED_PARAM) |
308 |
{ |
{ |
|
int status; |
|
|
|
|
309 |
signal(SIGINT, SIG_IGN); |
signal(SIGINT, SIG_IGN); |
310 |
|
|
311 |
printf("\n--- %s ping statistics ---\n", hostent->h_name); |
printf("\n--- %s ping statistics ---\n", hostname); |
312 |
printf("%lu packets transmitted, ", ntransmitted); |
printf("%lu packets transmitted, ", ntransmitted); |
313 |
printf("%lu packets received, ", nreceived); |
printf("%lu packets received, ", nreceived); |
314 |
if (nrepeats) |
if (nrepeats) |
315 |
printf("%lu duplicates, ", nrepeats); |
printf("%lu duplicates, ", nrepeats); |
316 |
if (ntransmitted) |
if (ntransmitted) |
317 |
printf("%lu%% packet loss\n", |
ntransmitted = (ntransmitted - nreceived) * 100 / ntransmitted; |
318 |
(ntransmitted - nreceived) * 100 / ntransmitted); |
printf("%lu%% packet loss\n", ntransmitted); |
319 |
if (nreceived) |
if (tmin != UINT_MAX) { |
320 |
printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n", |
unsigned tavg = tsum / (nreceived + nrepeats); |
321 |
tmin / 10, tmin % 10, |
printf("round-trip min/avg/max = %u.%03u/%u.%03u/%u.%03u ms\n", |
322 |
(tsum / (nreceived + nrepeats)) / 10, |
tmin / 1000, tmin % 1000, |
323 |
(tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10); |
tavg / 1000, tavg % 1000, |
324 |
if (nreceived != 0) |
tmax / 1000, tmax % 1000); |
325 |
status = EXIT_SUCCESS; |
} |
326 |
else |
/* if condition is true, exit with 1 -- 'failure' */ |
327 |
status = EXIT_FAILURE; |
exit(nreceived == 0 || (deadline && nreceived < pingcount)); |
|
exit(status); |
|
328 |
} |
} |
329 |
|
|
330 |
static void sendping(int junk) |
static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt) |
331 |
{ |
{ |
332 |
struct icmp *pkt; |
int sz; |
|
int i; |
|
|
char packet[datalen + ICMP_MINLEN]; |
|
333 |
|
|
334 |
pkt = (struct icmp *) packet; |
CLR((uint16_t)ntransmitted % MAX_DUP_CHK); |
335 |
|
ntransmitted++; |
336 |
|
|
337 |
|
/* sizeof(pingaddr) can be larger than real sa size, but I think |
338 |
|
* it doesn't matter */ |
339 |
|
sz = xsendto(pingsock, pkt, size_pkt, &pingaddr.sa, sizeof(pingaddr)); |
340 |
|
if (sz != size_pkt) |
341 |
|
bb_error_msg_and_die(bb_msg_write_error); |
342 |
|
|
343 |
|
if (pingcount == 0 || deadline || ntransmitted < pingcount) { |
344 |
|
/* Didn't send all pings yet - schedule next in 1s */ |
345 |
|
signal(SIGALRM, sp); |
346 |
|
if (deadline) { |
347 |
|
total_secs += PINGINTERVAL; |
348 |
|
if (total_secs >= deadline) |
349 |
|
signal(SIGALRM, print_stats_and_exit); |
350 |
|
} |
351 |
|
alarm(PINGINTERVAL); |
352 |
|
} else { /* -c NN, and all NN are sent (and no deadline) */ |
353 |
|
/* Wait for the last ping to come back. |
354 |
|
* -W timeout: wait for a response in seconds. |
355 |
|
* Affects only timeout in absense of any responses, |
356 |
|
* otherwise ping waits for two RTTs. */ |
357 |
|
unsigned expire = timeout; |
358 |
|
|
359 |
|
if (nreceived) { |
360 |
|
/* approx. 2*tmax, in seconds (2 RTT) */ |
361 |
|
expire = tmax / (512*1024); |
362 |
|
if (expire == 0) |
363 |
|
expire = 1; |
364 |
|
} |
365 |
|
signal(SIGALRM, print_stats_and_exit); |
366 |
|
alarm(expire); |
367 |
|
} |
368 |
|
} |
369 |
|
|
370 |
|
static void sendping4(int junk UNUSED_PARAM) |
371 |
|
{ |
372 |
|
/* +4 reserves a place for timestamp, which may end up sitting |
373 |
|
* *after* packet. Saves one if() */ |
374 |
|
struct icmp *pkt = alloca(datalen + ICMP_MINLEN + 4); |
375 |
|
|
376 |
|
memset(pkt, 0, datalen + ICMP_MINLEN + 4); |
377 |
pkt->icmp_type = ICMP_ECHO; |
pkt->icmp_type = ICMP_ECHO; |
378 |
pkt->icmp_code = 0; |
/*pkt->icmp_code = 0;*/ |
379 |
pkt->icmp_cksum = 0; |
/*pkt->icmp_cksum = 0;*/ |
380 |
pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */ |
pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */ |
381 |
pkt->icmp_id = myid; |
pkt->icmp_id = myid; |
|
CLR(ntohs(pkt->icmp_seq) % MAX_DUP_CHK); |
|
|
ntransmitted++; |
|
382 |
|
|
383 |
gettimeofday((struct timeval *) &pkt->icmp_dun, NULL); |
/* We don't do hton, because we will read it back on the same machine */ |
384 |
pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet)); |
/*if (datalen >= 4)*/ |
385 |
|
*(uint32_t*)&pkt->icmp_dun = monotonic_us(); |
386 |
|
|
387 |
i = sendto(pingsock, packet, sizeof(packet), 0, |
pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN); |
|
(struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in)); |
|
388 |
|
|
389 |
if (i < 0) |
sendping_tail(sendping4, pkt, datalen + ICMP_MINLEN); |
390 |
bb_perror_msg_and_die("sendto"); |
} |
391 |
if ((size_t)i != sizeof(packet)) |
#if ENABLE_PING6 |
392 |
bb_error_msg_and_die("ping wrote %d chars; %d expected", i, |
static void sendping6(int junk UNUSED_PARAM) |
393 |
(int)sizeof(packet)); |
{ |
394 |
|
struct icmp6_hdr *pkt = alloca(datalen + sizeof(struct icmp6_hdr) + 4); |
395 |
|
|
396 |
signal(SIGALRM, sendping); |
memset(pkt, 0, datalen + sizeof(struct icmp6_hdr) + 4); |
397 |
if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */ |
pkt->icmp6_type = ICMP6_ECHO_REQUEST; |
398 |
alarm(PINGINTERVAL); |
/*pkt->icmp6_code = 0;*/ |
399 |
} else { /* done, wait for the last ping to come back */ |
/*pkt->icmp6_cksum = 0;*/ |
400 |
/* todo, don't necessarily need to wait so long... */ |
pkt->icmp6_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */ |
401 |
signal(SIGALRM, pingstats); |
pkt->icmp6_id = myid; |
402 |
alarm(MAXWAIT); |
|
403 |
} |
/*if (datalen >= 4)*/ |
404 |
|
*(uint32_t*)(&pkt->icmp6_data8[4]) = monotonic_us(); |
405 |
|
|
406 |
|
sendping_tail(sendping6, pkt, datalen + sizeof(struct icmp6_hdr)); |
407 |
} |
} |
408 |
|
#endif |
409 |
|
|
410 |
static char *icmp_type_name(int id) |
static const char *icmp_type_name(int id) |
411 |
{ |
{ |
412 |
switch (id) { |
switch (id) { |
413 |
case ICMP_ECHOREPLY: return "Echo Reply"; |
case ICMP_ECHOREPLY: return "Echo Reply"; |
414 |
case ICMP_DEST_UNREACH: return "Destination Unreachable"; |
case ICMP_DEST_UNREACH: return "Destination Unreachable"; |
415 |
case ICMP_SOURCE_QUENCH: return "Source Quench"; |
case ICMP_SOURCE_QUENCH: return "Source Quench"; |
416 |
case ICMP_REDIRECT: return "Redirect (change route)"; |
case ICMP_REDIRECT: return "Redirect (change route)"; |
417 |
case ICMP_ECHO: return "Echo Request"; |
case ICMP_ECHO: return "Echo Request"; |
418 |
case ICMP_TIME_EXCEEDED: return "Time Exceeded"; |
case ICMP_TIME_EXCEEDED: return "Time Exceeded"; |
419 |
case ICMP_PARAMETERPROB: return "Parameter Problem"; |
case ICMP_PARAMETERPROB: return "Parameter Problem"; |
420 |
case ICMP_TIMESTAMP: return "Timestamp Request"; |
case ICMP_TIMESTAMP: return "Timestamp Request"; |
421 |
case ICMP_TIMESTAMPREPLY: return "Timestamp Reply"; |
case ICMP_TIMESTAMPREPLY: return "Timestamp Reply"; |
422 |
case ICMP_INFO_REQUEST: return "Information Request"; |
case ICMP_INFO_REQUEST: return "Information Request"; |
423 |
case ICMP_INFO_REPLY: return "Information Reply"; |
case ICMP_INFO_REPLY: return "Information Reply"; |
424 |
case ICMP_ADDRESS: return "Address Mask Request"; |
case ICMP_ADDRESS: return "Address Mask Request"; |
425 |
case ICMP_ADDRESSREPLY: return "Address Mask Reply"; |
case ICMP_ADDRESSREPLY: return "Address Mask Reply"; |
426 |
default: return "unknown ICMP type"; |
default: return "unknown ICMP type"; |
427 |
|
} |
428 |
|
} |
429 |
|
#if ENABLE_PING6 |
430 |
|
/* RFC3542 changed some definitions from RFC2292 for no good reason, whee! |
431 |
|
* the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */ |
432 |
|
#ifndef MLD_LISTENER_QUERY |
433 |
|
# define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY |
434 |
|
#endif |
435 |
|
#ifndef MLD_LISTENER_REPORT |
436 |
|
# define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT |
437 |
|
#endif |
438 |
|
#ifndef MLD_LISTENER_REDUCTION |
439 |
|
# define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION |
440 |
|
#endif |
441 |
|
static const char *icmp6_type_name(int id) |
442 |
|
{ |
443 |
|
switch (id) { |
444 |
|
case ICMP6_DST_UNREACH: return "Destination Unreachable"; |
445 |
|
case ICMP6_PACKET_TOO_BIG: return "Packet too big"; |
446 |
|
case ICMP6_TIME_EXCEEDED: return "Time Exceeded"; |
447 |
|
case ICMP6_PARAM_PROB: return "Parameter Problem"; |
448 |
|
case ICMP6_ECHO_REPLY: return "Echo Reply"; |
449 |
|
case ICMP6_ECHO_REQUEST: return "Echo Request"; |
450 |
|
case MLD_LISTENER_QUERY: return "Listener Query"; |
451 |
|
case MLD_LISTENER_REPORT: return "Listener Report"; |
452 |
|
case MLD_LISTENER_REDUCTION: return "Listener Reduction"; |
453 |
|
default: return "unknown ICMP type"; |
454 |
|
} |
455 |
|
} |
456 |
|
#endif |
457 |
|
|
458 |
|
static void unpack_tail(int sz, uint32_t *tp, |
459 |
|
const char *from_str, |
460 |
|
uint16_t recv_seq, int ttl) |
461 |
|
{ |
462 |
|
const char *dupmsg = " (DUP!)"; |
463 |
|
unsigned triptime = triptime; /* for gcc */ |
464 |
|
|
465 |
|
++nreceived; |
466 |
|
|
467 |
|
if (tp) { |
468 |
|
/* (int32_t) cast is for hypothetical 64-bit unsigned */ |
469 |
|
/* (doesn't hurt 32-bit real-world anyway) */ |
470 |
|
triptime = (int32_t) ((uint32_t)monotonic_us() - *tp); |
471 |
|
tsum += triptime; |
472 |
|
if (triptime < tmin) |
473 |
|
tmin = triptime; |
474 |
|
if (triptime > tmax) |
475 |
|
tmax = triptime; |
476 |
} |
} |
|
} |
|
477 |
|
|
478 |
static void unpack(char *buf, int sz, struct sockaddr_in *from) |
if (TST(recv_seq % MAX_DUP_CHK)) { |
479 |
|
++nrepeats; |
480 |
|
--nreceived; |
481 |
|
} else { |
482 |
|
SET(recv_seq % MAX_DUP_CHK); |
483 |
|
dupmsg += 7; |
484 |
|
} |
485 |
|
|
486 |
|
if (option_mask32 & OPT_QUIET) |
487 |
|
return; |
488 |
|
|
489 |
|
printf("%d bytes from %s: seq=%u ttl=%d", sz, |
490 |
|
from_str, recv_seq, ttl); |
491 |
|
if (tp) |
492 |
|
printf(" time=%u.%03u ms", triptime / 1000, triptime % 1000); |
493 |
|
puts(dupmsg); |
494 |
|
fflush(stdout); |
495 |
|
} |
496 |
|
static void unpack4(char *buf, int sz, struct sockaddr_in *from) |
497 |
{ |
{ |
498 |
struct icmp *icmppkt; |
struct icmp *icmppkt; |
499 |
struct iphdr *iphdr; |
struct iphdr *iphdr; |
500 |
struct timeval tv, *tp; |
int hlen; |
|
int hlen, dupflag; |
|
|
unsigned long triptime; |
|
|
|
|
|
gettimeofday(&tv, NULL); |
|
501 |
|
|
502 |
/* discard if too short */ |
/* discard if too short */ |
503 |
if (sz < (datalen + ICMP_MINLEN)) |
if (sz < (datalen + ICMP_MINLEN)) |
512 |
return; /* not our ping */ |
return; /* not our ping */ |
513 |
|
|
514 |
if (icmppkt->icmp_type == ICMP_ECHOREPLY) { |
if (icmppkt->icmp_type == ICMP_ECHOREPLY) { |
515 |
u_int16_t recv_seq = ntohs(icmppkt->icmp_seq); |
uint16_t recv_seq = ntohs(icmppkt->icmp_seq); |
516 |
++nreceived; |
uint32_t *tp = NULL; |
|
tp = (struct timeval *) icmppkt->icmp_data; |
|
|
|
|
|
if ((tv.tv_usec -= tp->tv_usec) < 0) { |
|
|
--tv.tv_sec; |
|
|
tv.tv_usec += 1000000; |
|
|
} |
|
|
tv.tv_sec -= tp->tv_sec; |
|
517 |
|
|
518 |
triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100); |
if (sz >= ICMP_MINLEN + sizeof(uint32_t)) |
519 |
tsum += triptime; |
tp = (uint32_t *) icmppkt->icmp_data; |
520 |
if (triptime < tmin) |
unpack_tail(sz, tp, |
521 |
tmin = triptime; |
inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr), |
522 |
if (triptime > tmax) |
recv_seq, iphdr->ttl); |
523 |
tmax = triptime; |
} else if (icmppkt->icmp_type != ICMP_ECHO) { |
524 |
|
bb_error_msg("warning: got ICMP %d (%s)", |
525 |
|
icmppkt->icmp_type, |
526 |
|
icmp_type_name(icmppkt->icmp_type)); |
527 |
|
} |
528 |
|
} |
529 |
|
#if ENABLE_PING6 |
530 |
|
static void unpack6(char *packet, int sz, /*struct sockaddr_in6 *from,*/ int hoplimit) |
531 |
|
{ |
532 |
|
struct icmp6_hdr *icmppkt; |
533 |
|
char buf[INET6_ADDRSTRLEN]; |
534 |
|
|
535 |
if (TST(recv_seq % MAX_DUP_CHK)) { |
/* discard if too short */ |
536 |
++nrepeats; |
if (sz < (datalen + sizeof(struct icmp6_hdr))) |
537 |
--nreceived; |
return; |
|
dupflag = 1; |
|
|
} else { |
|
|
SET(recv_seq % MAX_DUP_CHK); |
|
|
dupflag = 0; |
|
|
} |
|
538 |
|
|
539 |
if (option_mask32 & OPT_QUIET) |
icmppkt = (struct icmp6_hdr *) packet; |
540 |
return; |
if (icmppkt->icmp6_id != myid) |
541 |
|
return; /* not our ping */ |
542 |
|
|
543 |
printf("%d bytes from %s: icmp_seq=%u", sz, |
if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) { |
544 |
inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr), |
uint16_t recv_seq = ntohs(icmppkt->icmp6_seq); |
545 |
recv_seq); |
uint32_t *tp = NULL; |
546 |
printf(" ttl=%d", iphdr->ttl); |
|
547 |
printf(" time=%lu.%lu ms", triptime / 10, triptime % 10); |
if (sz >= sizeof(struct icmp6_hdr) + sizeof(uint32_t)) |
548 |
if (dupflag) |
tp = (uint32_t *) &icmppkt->icmp6_data8[4]; |
549 |
printf(" (DUP!)"); |
unpack_tail(sz, tp, |
550 |
puts(""); |
inet_ntop(AF_INET6, &pingaddr.sin6.sin6_addr, |
551 |
} else |
buf, sizeof(buf)), |
552 |
if (icmppkt->icmp_type != ICMP_ECHO) |
recv_seq, hoplimit); |
553 |
bb_error_msg("warning: got ICMP %d (%s)", |
} else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) { |
554 |
icmppkt->icmp_type, |
bb_error_msg("warning: got ICMP %d (%s)", |
555 |
icmp_type_name(icmppkt->icmp_type)); |
icmppkt->icmp6_type, |
556 |
fflush(stdout); |
icmp6_type_name(icmppkt->icmp6_type)); |
557 |
|
} |
558 |
} |
} |
559 |
|
#endif |
560 |
|
|
561 |
static void ping(const char *host) |
static void ping4(len_and_sockaddr *lsa) |
562 |
{ |
{ |
563 |
char packet[datalen + MAXIPLEN + MAXICMPLEN]; |
char packet[datalen + MAXIPLEN + MAXICMPLEN]; |
564 |
int sockopt; |
int sockopt; |
565 |
|
|
566 |
pingsock = create_icmp_socket(); |
pingsock = create_icmp_socket(); |
567 |
|
pingaddr.sin = lsa->u.sin; |
568 |
if (sourceaddr.sin_addr.s_addr) { |
if (source_lsa) { |
569 |
xbind(pingsock, (struct sockaddr*)&sourceaddr, sizeof(sourceaddr)); |
if (setsockopt(pingsock, IPPROTO_IP, IP_MULTICAST_IF, |
570 |
|
&source_lsa->u.sa, source_lsa->len)) |
571 |
|
bb_error_msg_and_die("can't set multicast source interface"); |
572 |
|
xbind(pingsock, &source_lsa->u.sa, source_lsa->len); |
573 |
} |
} |
574 |
|
if (str_I) |
575 |
memset(&pingaddr, 0, sizeof(struct sockaddr_in)); |
setsockopt_bindtodevice(pingsock, str_I); |
|
|
|
|
pingaddr.sin_family = AF_INET; |
|
|
hostent = xgethostbyname(host); |
|
|
if (hostent->h_addrtype != AF_INET) |
|
|
bb_error_msg_and_die("unknown address type; only AF_INET is currently supported"); |
|
|
|
|
|
memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr)); |
|
576 |
|
|
577 |
/* enable broadcast pings */ |
/* enable broadcast pings */ |
578 |
setsockopt_broadcast(pingsock); |
setsockopt_broadcast(pingsock); |
579 |
|
|
580 |
/* set recv buf for broadcast pings */ |
/* set recv buf (needed if we can get lots of responses: flood ping, |
581 |
sockopt = 48 * 1024; |
* broadcast ping etc) */ |
582 |
setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt, |
sockopt = (datalen * 2) + 7 * 1024; /* giving it a bit of extra room */ |
583 |
sizeof(sockopt)); |
setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt)); |
|
|
|
|
printf("PING %s (%s)", |
|
|
hostent->h_name, |
|
|
inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr)); |
|
|
if (sourceaddr.sin_addr.s_addr) { |
|
|
printf(" from %s", |
|
|
inet_ntoa(*(struct in_addr *) &sourceaddr.sin_addr.s_addr)); |
|
|
} |
|
|
printf(": %d data bytes\n", datalen); |
|
584 |
|
|
585 |
signal(SIGINT, pingstats); |
signal(SIGINT, print_stats_and_exit); |
586 |
|
|
587 |
/* start the ping's going ... */ |
/* start the ping's going ... */ |
588 |
sendping(0); |
sendping4(0); |
589 |
|
|
590 |
/* listen for replies */ |
/* listen for replies */ |
591 |
while (1) { |
while (1) { |
600 |
bb_perror_msg("recvfrom"); |
bb_perror_msg("recvfrom"); |
601 |
continue; |
continue; |
602 |
} |
} |
603 |
unpack(packet, c, &from); |
unpack4(packet, c, &from); |
604 |
if (pingcount > 0 && nreceived >= pingcount) |
if (pingcount && nreceived >= pingcount) |
605 |
break; |
break; |
606 |
} |
} |
|
pingstats(0); |
|
607 |
} |
} |
608 |
|
#if ENABLE_PING6 |
609 |
|
extern int BUG_bad_offsetof_icmp6_cksum(void); |
610 |
|
static void ping6(len_and_sockaddr *lsa) |
611 |
|
{ |
612 |
|
char packet[datalen + MAXIPLEN + MAXICMPLEN]; |
613 |
|
int sockopt; |
614 |
|
struct msghdr msg; |
615 |
|
struct sockaddr_in6 from; |
616 |
|
struct iovec iov; |
617 |
|
char control_buf[CMSG_SPACE(36)]; |
618 |
|
|
619 |
|
pingsock = create_icmp6_socket(); |
620 |
|
pingaddr.sin6 = lsa->u.sin6; |
621 |
|
/* untested whether "-I addr" really works for IPv6: */ |
622 |
|
if (source_lsa) |
623 |
|
xbind(pingsock, &source_lsa->u.sa, source_lsa->len); |
624 |
|
if (str_I) |
625 |
|
setsockopt_bindtodevice(pingsock, str_I); |
626 |
|
|
627 |
|
#ifdef ICMP6_FILTER |
628 |
|
{ |
629 |
|
struct icmp6_filter filt; |
630 |
|
if (!(option_mask32 & OPT_VERBOSE)) { |
631 |
|
ICMP6_FILTER_SETBLOCKALL(&filt); |
632 |
|
ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt); |
633 |
|
} else { |
634 |
|
ICMP6_FILTER_SETPASSALL(&filt); |
635 |
|
} |
636 |
|
if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, |
637 |
|
sizeof(filt)) < 0) |
638 |
|
bb_error_msg_and_die("setsockopt(ICMP6_FILTER)"); |
639 |
|
} |
640 |
|
#endif /*ICMP6_FILTER*/ |
641 |
|
|
642 |
/* TODO: consolidate ether-wake.c, dnsd.c, ifupdown.c, nslookup.c |
/* enable broadcast pings */ |
643 |
* versions of below thing. BTW we have far too many "%u.%u.%u.%u" too... |
setsockopt_broadcast(pingsock); |
644 |
*/ |
|
645 |
static int parse_nipquad(const char *str, struct sockaddr_in* addr) |
/* set recv buf (needed if we can get lots of responses: flood ping, |
646 |
{ |
* broadcast ping etc) */ |
647 |
char dummy; |
sockopt = (datalen * 2) + 7 * 1024; /* giving it a bit of extra room */ |
648 |
unsigned i1, i2, i3, i4; |
setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt)); |
649 |
if (sscanf(str, "%u.%u.%u.%u%c", |
|
650 |
&i1, &i2, &i3, &i4, &dummy) == 4 |
sockopt = offsetof(struct icmp6_hdr, icmp6_cksum); |
651 |
&& ( (i1|i2|i3|i4) <= 0xff ) |
if (offsetof(struct icmp6_hdr, icmp6_cksum) != 2) |
652 |
) { |
BUG_bad_offsetof_icmp6_cksum(); |
653 |
uint8_t* ptr = (uint8_t*)&addr->sin_addr; |
setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt)); |
654 |
ptr[0] = i1; |
|
655 |
ptr[1] = i2; |
/* request ttl info to be returned in ancillary data */ |
656 |
ptr[2] = i3; |
setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, &const_int_1, sizeof(const_int_1)); |
657 |
ptr[3] = i4; |
|
658 |
return 0; |
if (if_index) |
659 |
} |
pingaddr.sin6.sin6_scope_id = if_index; |
660 |
return 1; /* error */ |
|
661 |
} |
signal(SIGINT, print_stats_and_exit); |
662 |
|
|
663 |
int ping_main(int argc, char **argv) |
/* start the ping's going ... */ |
664 |
{ |
sendping6(0); |
665 |
char *opt_c, *opt_s, *opt_I; |
|
666 |
|
/* listen for replies */ |
667 |
datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */ |
msg.msg_name = &from; |
668 |
|
msg.msg_namelen = sizeof(from); |
669 |
/* exactly one argument needed */ |
msg.msg_iov = &iov; |
670 |
opt_complementary = "=1"; |
msg.msg_iovlen = 1; |
671 |
getopt32(argc, argv, OPT_STRING, &opt_c, &opt_s, &opt_I); |
msg.msg_control = control_buf; |
672 |
if (option_mask32 & 2) pingcount = xatoul(opt_c); // -c |
iov.iov_base = packet; |
673 |
if (option_mask32 & 4) datalen = xatou16(opt_s); // -s |
iov.iov_len = sizeof(packet); |
674 |
if (option_mask32 & 8) { // -I |
while (1) { |
675 |
/* TODO: ping6 accepts iface too: |
int c; |
676 |
if_index = if_nametoindex(*argv); |
struct cmsghdr *mp; |
677 |
if (!if_index) ... |
int hoplimit = -1; |
678 |
make it true for ping. */ |
msg.msg_controllen = sizeof(control_buf); |
679 |
if (parse_nipquad(opt_I, &sourceaddr)) |
|
680 |
bb_show_usage(); |
c = recvmsg(pingsock, &msg, 0); |
681 |
|
if (c < 0) { |
682 |
|
if (errno != EINTR) |
683 |
|
bb_perror_msg("recvfrom"); |
684 |
|
continue; |
685 |
|
} |
686 |
|
for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) { |
687 |
|
if (mp->cmsg_level == SOL_IPV6 |
688 |
|
&& mp->cmsg_type == IPV6_HOPLIMIT |
689 |
|
/* don't check len - we trust the kernel: */ |
690 |
|
/* && mp->cmsg_len >= CMSG_LEN(sizeof(int)) */ |
691 |
|
) { |
692 |
|
hoplimit = *(int*)CMSG_DATA(mp); |
693 |
|
} |
694 |
|
} |
695 |
|
unpack6(packet, c, /*&from,*/ hoplimit); |
696 |
|
if (pingcount && nreceived >= pingcount) |
697 |
|
break; |
698 |
} |
} |
699 |
|
} |
700 |
|
#endif |
701 |
|
|
702 |
myid = (int16_t) getpid(); |
static void ping(len_and_sockaddr *lsa) |
703 |
ping(argv[optind]); |
{ |
704 |
return EXIT_SUCCESS; |
printf("PING %s (%s)", hostname, dotted); |
705 |
|
if (source_lsa) { |
706 |
|
printf(" from %s", |
707 |
|
xmalloc_sockaddr2dotted_noport(&source_lsa->u.sa)); |
708 |
|
} |
709 |
|
printf(": %d data bytes\n", datalen); |
710 |
|
|
711 |
|
#if ENABLE_PING6 |
712 |
|
if (lsa->u.sa.sa_family == AF_INET6) |
713 |
|
ping6(lsa); |
714 |
|
else |
715 |
|
#endif |
716 |
|
ping4(lsa); |
717 |
|
} |
718 |
|
|
719 |
|
int ping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
720 |
|
int ping_main(int argc UNUSED_PARAM, char **argv) |
721 |
|
{ |
722 |
|
len_and_sockaddr *lsa; |
723 |
|
char *str_s; |
724 |
|
int opt; |
725 |
|
|
726 |
|
INIT_G(); |
727 |
|
|
728 |
|
/* exactly one argument needed; -v and -q don't mix; -c NUM, -w NUM, -W NUM */ |
729 |
|
opt_complementary = "=1:q--v:v--q:c+:w+:W+"; |
730 |
|
opt = getopt32(argv, OPT_STRING, &pingcount, &str_s, &deadline, &timeout, &str_I); |
731 |
|
if (opt & OPT_s) |
732 |
|
datalen = xatou16(str_s); // -s |
733 |
|
if (opt & OPT_I) { // -I |
734 |
|
if_index = if_nametoindex(str_I); |
735 |
|
if (!if_index) { |
736 |
|
/* TODO: I'm not sure it takes IPv6 unless in [XX:XX..] format */ |
737 |
|
source_lsa = xdotted2sockaddr(str_I, 0); |
738 |
|
str_I = NULL; /* don't try to bind to device later */ |
739 |
|
} |
740 |
|
} |
741 |
|
myid = (uint16_t) getpid(); |
742 |
|
hostname = argv[optind]; |
743 |
|
#if ENABLE_PING6 |
744 |
|
{ |
745 |
|
sa_family_t af = AF_UNSPEC; |
746 |
|
if (opt & OPT_IPV4) |
747 |
|
af = AF_INET; |
748 |
|
if (opt & OPT_IPV6) |
749 |
|
af = AF_INET6; |
750 |
|
lsa = xhost_and_af2sockaddr(hostname, 0, af); |
751 |
|
} |
752 |
|
#else |
753 |
|
lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET); |
754 |
|
#endif |
755 |
|
|
756 |
|
if (source_lsa && source_lsa->u.sa.sa_family != lsa->u.sa.sa_family) |
757 |
|
/* leaking it here... */ |
758 |
|
source_lsa = NULL; |
759 |
|
|
760 |
|
dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa); |
761 |
|
ping(lsa); |
762 |
|
print_stats_and_exit(EXIT_SUCCESS); |
763 |
|
/*return EXIT_SUCCESS;*/ |
764 |
|
} |
765 |
|
#endif /* FEATURE_FANCY_PING */ |
766 |
|
|
767 |
|
|
768 |
|
#if ENABLE_PING6 |
769 |
|
int ping6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
770 |
|
int ping6_main(int argc UNUSED_PARAM, char **argv) |
771 |
|
{ |
772 |
|
argv[0] = (char*)"-6"; |
773 |
|
return ping_main(0 /* argc+1 - but it's unused anyway */, |
774 |
|
argv - 1); |
775 |
} |
} |
776 |
#endif /* ! CONFIG_FEATURE_FANCY_PING */ |
#endif |
777 |
|
|
778 |
|
/* from ping6.c: |
779 |
|
* Copyright (c) 1989 The Regents of the University of California. |
780 |
|
* All rights reserved. |
781 |
|
* |
782 |
|
* This code is derived from software contributed to Berkeley by |
783 |
|
* Mike Muuss. |
784 |
|
* |
785 |
|
* Redistribution and use in source and binary forms, with or without |
786 |
|
* modification, are permitted provided that the following conditions |
787 |
|
* are met: |
788 |
|
* 1. Redistributions of source code must retain the above copyright |
789 |
|
* notice, this list of conditions and the following disclaimer. |
790 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
791 |
|
* notice, this list of conditions and the following disclaimer in the |
792 |
|
* documentation and/or other materials provided with the distribution. |
793 |
|
* |
794 |
|
* 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change |
795 |
|
* ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change> |
796 |
|
* |
797 |
|
* 4. Neither the name of the University nor the names of its contributors |
798 |
|
* may be used to endorse or promote products derived from this software |
799 |
|
* without specific prior written permission. |
800 |
|
* |
801 |
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
802 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
803 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
804 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
805 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
806 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
807 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
808 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
809 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
810 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
811 |
|
* SUCH DAMAGE. |
812 |
|
*/ |