make a few struct bb_applet members conditional
[oweals/busybox.git] / networking / ping.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini ping implementation for busybox
4  *
5  * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
6  *
7  * Adapted from the ping in netkit-base 0.10:
8  * Copyright (c) 1989 The Regents of the University of California.
9  * 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.
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>
30 #include "busybox.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 {
43         DEFDATALEN = 56,
44         MAXIPLEN = 60,
45         MAXICMPLEN = 76,
46         MAXPACKET = 65468,
47         MAX_DUP_CHK = (8 * 128),
48         MAXWAIT = 10,
49         PINGINTERVAL = 1, /* 1 second */
50 };
51
52 /* common routines */
53
54 static int in_cksum(unsigned short *buf, int sz)
55 {
56         int nleft = sz;
57         int sum = 0;
58         unsigned short *w = buf;
59         unsigned short ans = 0;
60
61         while (nleft > 1) {
62                 sum += *w++;
63                 nleft -= 2;
64         }
65
66         if (nleft == 1) {
67                 *(unsigned char *) (&ans) = *(unsigned char *) w;
68                 sum += ans;
69         }
70
71         sum = (sum >> 16) + (sum & 0xFFFF);
72         sum += (sum >> 16);
73         ans = ~sum;
74         return ans;
75 }
76
77 #if !ENABLE_FEATURE_FANCY_PING
78
79 /* simple version */
80
81 static char *hostname;
82
83 static void noresp(int ign ATTRIBUTE_UNUSED)
84 {
85         printf("No response from %s\n", hostname);
86         exit(EXIT_FAILURE);
87 }
88
89 static void ping4(len_and_sockaddr *lsa)
90 {
91         struct sockaddr_in pingaddr;
92         struct icmp *pkt;
93         int pingsock, c;
94         char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
95
96         pingsock = create_icmp_socket();
97         pingaddr = lsa->sin;
98
99         pkt = (struct icmp *) packet;
100         memset(pkt, 0, sizeof(packet));
101         pkt->icmp_type = ICMP_ECHO;
102         pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
103
104         c = sendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN, 0,
105                            (struct sockaddr *) &pingaddr, sizeof(pingaddr));
106
107         if (c < 0) {
108                 if (ENABLE_FEATURE_CLEAN_UP)
109                         close(pingsock);
110                 bb_perror_msg_and_die("sendto");
111         }
112
113         /* listen for replies */
114         while (1) {
115                 struct sockaddr_in from;
116                 socklen_t fromlen = sizeof(from);
117
118                 c = recvfrom(pingsock, packet, sizeof(packet), 0,
119                                 (struct sockaddr *) &from, &fromlen);
120                 if (c < 0) {
121                         if (errno != EINTR)
122                                 bb_perror_msg("recvfrom");
123                         continue;
124                 }
125                 if (c >= 76) {                  /* ip + icmp */
126                         struct iphdr *iphdr = (struct iphdr *) packet;
127
128                         pkt = (struct icmp *) (packet + (iphdr->ihl << 2));     /* skip ip hdr */
129                         if (pkt->icmp_type == ICMP_ECHOREPLY)
130                                 break;
131                 }
132         }
133         if (ENABLE_FEATURE_CLEAN_UP)
134                 close(pingsock);
135 }
136
137 #if ENABLE_PING6
138 static void ping6(len_and_sockaddr *lsa)
139 {
140         struct sockaddr_in6 pingaddr;
141         struct icmp6_hdr *pkt;
142         int pingsock, c;
143         int sockopt;
144         char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
145
146         pingsock = create_icmp6_socket();
147         pingaddr = lsa->sin6;
148
149         pkt = (struct icmp6_hdr *) packet;
150         memset(pkt, 0, sizeof(packet));
151         pkt->icmp6_type = ICMP6_ECHO_REQUEST;
152
153         sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
154         setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
155
156         c = sendto(pingsock, packet, DEFDATALEN + sizeof (struct icmp6_hdr), 0,
157                            (struct sockaddr *) &pingaddr, sizeof(pingaddr));
158
159         if (c < 0) {
160                 if (ENABLE_FEATURE_CLEAN_UP)
161                         close(pingsock);
162                 bb_perror_msg_and_die("sendto");
163         }
164
165         /* listen for replies */
166         while (1) {
167                 struct sockaddr_in6 from;
168                 socklen_t fromlen = sizeof(from);
169
170                 c = recvfrom(pingsock, packet, sizeof(packet), 0,
171                                 (struct sockaddr *) &from, &fromlen);
172                 if (c < 0) {
173                         if (errno != EINTR)
174                                 bb_perror_msg("recvfrom");
175                         continue;
176                 }
177                 if (c >= 8) {                   /* icmp6_hdr */
178                         pkt = (struct icmp6_hdr *) packet;
179                         if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
180                                 break;
181                 }
182         }
183         if (ENABLE_FEATURE_CLEAN_UP)
184                 close(pingsock);
185 }
186 #endif
187
188 int ping_main(int argc, char **argv);
189 int ping_main(int argc, char **argv)
190 {
191         len_and_sockaddr *lsa;
192 #if ENABLE_PING6
193         sa_family_t af = AF_UNSPEC;
194         while (++argv[0][0] == '-') {
195                 if (argv[0][1] == '4') {
196                         af = AF_INET;
197                         continue;
198                 }
199                 if (argv[0][1] == '6') {
200                         af = AF_INET6;
201                         continue;
202                 }
203                 bb_show_usage();
204         }
205 #else
206         argv++;
207 #endif
208
209         hostname = *argv;
210         if (!hostname)
211                 bb_show_usage();
212
213 #if ENABLE_PING6
214         lsa = xhost_and_af2sockaddr(hostname, 0, af);
215 #else
216         lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
217 #endif
218         /* Set timer _after_ DNS resolution */
219         signal(SIGALRM, noresp);
220         alarm(5); /* give the host 5000ms to respond */
221
222 #if ENABLE_PING6
223         if (lsa->sa.sa_family == AF_INET6)
224                 ping6(lsa);
225         else
226 #endif
227                 ping4(lsa);
228         printf("%s is alive!\n", hostname);
229         return EXIT_SUCCESS;
230 }
231
232
233 #else /* FEATURE_FANCY_PING */
234
235
236 /* full(er) version */
237
238 #define OPT_STRING ("qvc:s:I:4" USE_PING6("6"))
239 enum {
240         OPT_QUIET = 1 << 0,
241         OPT_VERBOSE = 1 << 1,
242         OPT_c = 1 << 2,
243         OPT_s = 1 << 3,
244         OPT_I = 1 << 4,
245         OPT_IPV4 = 1 << 5,
246         OPT_IPV6 = (1 << 6) * ENABLE_PING6,
247 };
248
249
250 static union {
251         struct sockaddr sa;
252         struct sockaddr_in sin;
253 #if ENABLE_PING6
254         struct sockaddr_in6 sin6;
255 #endif
256 } pingaddr;
257 static len_and_sockaddr *source_lsa;
258 static int pingsock = -1;
259 static unsigned datalen; /* intentionally uninitialized to work around gcc bug */
260
261 static int if_index;
262
263 static unsigned long ntransmitted, nreceived, nrepeats, pingcount;
264 static int myid;
265 static unsigned tmin = UINT_MAX, tmax;
266 static unsigned long tsum;
267 static char rcvd_tbl[MAX_DUP_CHK / 8];
268
269 static const char *hostname;
270 static const char *dotted;
271
272 #define A(bit)          rcvd_tbl[(bit)>>3]      /* identify byte in array */
273 #define B(bit)          (1 << ((bit) & 0x07))   /* identify bit in byte */
274 #define SET(bit)        (A(bit) |= B(bit))
275 #define CLR(bit)        (A(bit) &= (~B(bit)))
276 #define TST(bit)        (A(bit) & B(bit))
277
278 /**************************************************************************/
279
280 static void pingstats(int junk ATTRIBUTE_UNUSED)
281 {
282         signal(SIGINT, SIG_IGN);
283
284         printf("\n--- %s ping statistics ---\n", hostname);
285         printf("%lu packets transmitted, ", ntransmitted);
286         printf("%lu packets received, ", nreceived);
287         if (nrepeats)
288                 printf("%lu duplicates, ", nrepeats);
289         if (ntransmitted)
290                 ntransmitted = (ntransmitted - nreceived) * 100 / ntransmitted;
291         printf("%lu%% packet loss\n", ntransmitted);
292         if (tmin != UINT_MAX)
293                 printf("round-trip min/avg/max = %u.%u/%lu.%lu/%u.%u ms\n",
294                         tmin / 10, tmin % 10,
295                         (tsum / (nreceived + nrepeats)) / 10,
296                         (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
297         exit(nreceived == 0); /* (nreceived == 0) is true (1) -- 'failure' */
298 }
299
300 static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt)
301 {
302         int sz;
303
304         CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
305         ntransmitted++;
306
307         /* sizeof(pingaddr) can be larger than real sa size, but I think
308          * it doesn't matter */
309         sz = sendto(pingsock, pkt, size_pkt, 0, &pingaddr.sa, sizeof(pingaddr));
310         if (sz < 0)
311                 bb_perror_msg_and_die("sendto");
312         if (sz != size_pkt)
313                 bb_error_msg_and_die("ping wrote %d chars; %d expected", sz,
314                         size_pkt);
315
316         signal(SIGALRM, sp);
317         if (pingcount == 0 || ntransmitted < pingcount) {       /* schedule next in 1s */
318                 alarm(PINGINTERVAL);
319         } else { /* done, wait for the last ping to come back */
320                 /* todo, don't necessarily need to wait so long... */
321                 signal(SIGALRM, pingstats);
322                 alarm(MAXWAIT);
323         }
324 }
325
326 static void sendping4(int junk ATTRIBUTE_UNUSED)
327 {
328         struct icmp *pkt = alloca(datalen + ICMP_MINLEN);
329
330         pkt->icmp_type = ICMP_ECHO;
331         pkt->icmp_code = 0;
332         pkt->icmp_cksum = 0;
333         pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
334         pkt->icmp_id = myid;
335         gettimeofday((struct timeval *) &pkt->icmp_dun, NULL);
336         pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN);
337
338         sendping_tail(sendping4, pkt, datalen + ICMP_MINLEN);
339 }
340 #if ENABLE_PING6
341 static void sendping6(int junk ATTRIBUTE_UNUSED)
342 {
343         struct icmp6_hdr *pkt = alloca(datalen + sizeof(struct icmp6_hdr));
344
345         pkt->icmp6_type = ICMP6_ECHO_REQUEST;
346         pkt->icmp6_code = 0;
347         pkt->icmp6_cksum = 0;
348         pkt->icmp6_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
349         pkt->icmp6_id = myid;
350         gettimeofday((struct timeval *) &pkt->icmp6_data8[4], NULL);
351
352         sendping_tail(sendping6, pkt, datalen + sizeof(struct icmp6_hdr));
353 }
354 #endif
355
356 static const char *icmp_type_name(int id)
357 {
358         switch (id) {
359         case ICMP_ECHOREPLY:      return "Echo Reply";
360         case ICMP_DEST_UNREACH:   return "Destination Unreachable";
361         case ICMP_SOURCE_QUENCH:  return "Source Quench";
362         case ICMP_REDIRECT:       return "Redirect (change route)";
363         case ICMP_ECHO:           return "Echo Request";
364         case ICMP_TIME_EXCEEDED:  return "Time Exceeded";
365         case ICMP_PARAMETERPROB:  return "Parameter Problem";
366         case ICMP_TIMESTAMP:      return "Timestamp Request";
367         case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
368         case ICMP_INFO_REQUEST:   return "Information Request";
369         case ICMP_INFO_REPLY:     return "Information Reply";
370         case ICMP_ADDRESS:        return "Address Mask Request";
371         case ICMP_ADDRESSREPLY:   return "Address Mask Reply";
372         default:                  return "unknown ICMP type";
373         }
374 }
375 #if ENABLE_PING6
376 /* RFC3542 changed some definitions from RFC2292 for no good reason, whee!
377  * the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */
378 #ifndef MLD_LISTENER_QUERY
379 # define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY
380 #endif
381 #ifndef MLD_LISTENER_REPORT
382 # define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT
383 #endif
384 #ifndef MLD_LISTENER_REDUCTION
385 # define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION
386 #endif
387 static const char *icmp6_type_name(int id)
388 {
389         switch (id) {
390         case ICMP6_DST_UNREACH:      return "Destination Unreachable";
391         case ICMP6_PACKET_TOO_BIG:   return "Packet too big";
392         case ICMP6_TIME_EXCEEDED:    return "Time Exceeded";
393         case ICMP6_PARAM_PROB:       return "Parameter Problem";
394         case ICMP6_ECHO_REPLY:       return "Echo Reply";
395         case ICMP6_ECHO_REQUEST:     return "Echo Request";
396         case MLD_LISTENER_QUERY:     return "Listener Query";
397         case MLD_LISTENER_REPORT:    return "Listener Report";
398         case MLD_LISTENER_REDUCTION: return "Listener Reduction";
399         default:                     return "unknown ICMP type";
400         }
401 }
402 #endif
403
404 static void unpack_tail(int sz, struct timeval *tp,
405                 const char *from_str,
406                 uint16_t recv_seq, int ttl)
407 {
408         const char *dupmsg = " (DUP!)";
409         unsigned triptime = triptime; /* for gcc */
410
411         ++nreceived;
412
413         if (tp) {
414                 struct timeval tv;
415
416                 gettimeofday(&tv, NULL);
417                 tv.tv_usec -= tp->tv_usec;
418                 if (tv.tv_usec < 0) {
419                         --tv.tv_sec;
420                         tv.tv_usec += 1000000;
421                 }
422                 tv.tv_sec -= tp->tv_sec;
423
424                 triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
425                 tsum += triptime;
426                 if (triptime < tmin)
427                         tmin = triptime;
428                 if (triptime > tmax)
429                         tmax = triptime;
430         }
431
432         if (TST(recv_seq % MAX_DUP_CHK)) {
433                 ++nrepeats;
434                 --nreceived;
435         } else {
436                 SET(recv_seq % MAX_DUP_CHK);
437                 dupmsg += 7;
438         }
439
440         if (option_mask32 & OPT_QUIET)
441                 return;
442
443         printf("%d bytes from %s: seq=%u ttl=%d", sz,
444                 from_str, recv_seq, ttl);
445         if (tp)
446                 printf(" time=%u.%u ms", triptime / 10, triptime % 10);
447         puts(dupmsg);
448         fflush(stdout);
449 }
450 static void unpack4(char *buf, int sz, struct sockaddr_in *from)
451 {
452         struct icmp *icmppkt;
453         struct iphdr *iphdr;
454         int hlen;
455
456         /* discard if too short */
457         if (sz < (datalen + ICMP_MINLEN))
458                 return;
459
460         /* check IP header */
461         iphdr = (struct iphdr *) buf;
462         hlen = iphdr->ihl << 2;
463         sz -= hlen;
464         icmppkt = (struct icmp *) (buf + hlen);
465         if (icmppkt->icmp_id != myid)
466                 return;                         /* not our ping */
467
468         if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
469                 uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
470                 struct timeval *tp = NULL;
471
472                 if (sz >= ICMP_MINLEN + sizeof(struct timeval))
473                         tp = (struct timeval *) icmppkt->icmp_data;
474                 unpack_tail(sz, tp,
475                         inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
476                         recv_seq, iphdr->ttl);
477         } else if (icmppkt->icmp_type != ICMP_ECHO) {
478                 bb_error_msg("warning: got ICMP %d (%s)",
479                                 icmppkt->icmp_type,
480                                 icmp_type_name(icmppkt->icmp_type));
481         }
482 }
483 #if ENABLE_PING6
484 static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
485 {
486         struct icmp6_hdr *icmppkt;
487         char buf[INET6_ADDRSTRLEN];
488
489         /* discard if too short */
490         if (sz < (datalen + sizeof(struct icmp6_hdr)))
491                 return;
492
493         icmppkt = (struct icmp6_hdr *) packet;
494         if (icmppkt->icmp6_id != myid)
495                 return;                         /* not our ping */
496
497         if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
498                 uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
499                 struct timeval *tp = NULL;
500
501                 if (sz >= sizeof(struct icmp6_hdr) + sizeof(struct timeval))
502                         tp = (struct timeval *) &icmppkt->icmp6_data8[4];
503                 unpack_tail(sz, tp,
504                         inet_ntop(AF_INET6, &pingaddr.sin6.sin6_addr,
505                                         buf, sizeof(buf)),
506                         recv_seq, hoplimit);
507         } else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
508                 bb_error_msg("warning: got ICMP %d (%s)",
509                                 icmppkt->icmp6_type,
510                                 icmp6_type_name(icmppkt->icmp6_type));
511         }
512 }
513 #endif
514
515 static void ping4(len_and_sockaddr *lsa)
516 {
517         char packet[datalen + MAXIPLEN + MAXICMPLEN];
518         int sockopt;
519
520         pingsock = create_icmp_socket();
521         pingaddr.sin = lsa->sin;
522         if (source_lsa)
523                 xbind(pingsock, &lsa->sa, lsa->len);
524
525         /* enable broadcast pings */
526         setsockopt_broadcast(pingsock);
527
528         /* set recv buf for broadcast pings */
529         sockopt = 48 * 1024; /* explain why 48k? */
530         setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
531
532         signal(SIGINT, pingstats);
533
534         /* start the ping's going ... */
535         sendping4(0);
536
537         /* listen for replies */
538         while (1) {
539                 struct sockaddr_in from;
540                 socklen_t fromlen = (socklen_t) sizeof(from);
541                 int c;
542
543                 c = recvfrom(pingsock, packet, sizeof(packet), 0,
544                                 (struct sockaddr *) &from, &fromlen);
545                 if (c < 0) {
546                         if (errno != EINTR)
547                                 bb_perror_msg("recvfrom");
548                         continue;
549                 }
550                 unpack4(packet, c, &from);
551                 if (pingcount > 0 && nreceived >= pingcount)
552                         break;
553         }
554 }
555 #if ENABLE_PING6
556 extern int BUG_bad_offsetof_icmp6_cksum(void);
557 static void ping6(len_and_sockaddr *lsa)
558 {
559         char packet[datalen + MAXIPLEN + MAXICMPLEN];
560         int sockopt;
561         struct msghdr msg;
562         struct sockaddr_in6 from;
563         struct iovec iov;
564         char control_buf[CMSG_SPACE(36)];
565
566         pingsock = create_icmp6_socket();
567         pingaddr.sin6 = lsa->sin6;
568         /* untested whether "-I addr" really works for IPv6: */
569         if (source_lsa)
570                 xbind(pingsock, &lsa->sa, lsa->len);
571
572 #ifdef ICMP6_FILTER
573         {
574                 struct icmp6_filter filt;
575                 if (!(option_mask32 & OPT_VERBOSE)) {
576                         ICMP6_FILTER_SETBLOCKALL(&filt);
577                         ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
578                 } else {
579                         ICMP6_FILTER_SETPASSALL(&filt);
580                 }
581                 if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
582                                            sizeof(filt)) < 0)
583                         bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
584         }
585 #endif /*ICMP6_FILTER*/
586
587         /* enable broadcast pings */
588         setsockopt_broadcast(pingsock);
589
590         /* set recv buf for broadcast pings */
591         sockopt = 48 * 1024; /* explain why 48k? */
592         setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
593
594         sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
595         if (offsetof(struct icmp6_hdr, icmp6_cksum) != 2)
596                 BUG_bad_offsetof_icmp6_cksum();
597         setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
598
599         /* request ttl info to be returned in ancillary data */
600         setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, &const_int_1, sizeof(const_int_1));
601
602         if (if_index)
603                 pingaddr.sin6.sin6_scope_id = if_index;
604
605         signal(SIGINT, pingstats);
606
607         /* start the ping's going ... */
608         sendping6(0);
609
610         /* listen for replies */
611         msg.msg_name = &from;
612         msg.msg_namelen = sizeof(from);
613         msg.msg_iov = &iov;
614         msg.msg_iovlen = 1;
615         msg.msg_control = control_buf;
616         iov.iov_base = packet;
617         iov.iov_len = sizeof(packet);
618         while (1) {
619                 int c;
620                 struct cmsghdr *mp;
621                 int hoplimit = -1;
622                 msg.msg_controllen = sizeof(control_buf);
623
624                 c = recvmsg(pingsock, &msg, 0);
625                 if (c < 0) {
626                         if (errno != EINTR)
627                                 bb_perror_msg("recvfrom");
628                         continue;
629                 }
630                 for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) {
631                         if (mp->cmsg_level == SOL_IPV6
632                          && mp->cmsg_type == IPV6_HOPLIMIT
633                          /* don't check len - we trust the kernel: */
634                          /* && mp->cmsg_len >= CMSG_LEN(sizeof(int)) */
635                         ) {
636                                 hoplimit = *(int*)CMSG_DATA(mp);
637                         }
638                 }
639                 unpack6(packet, c, &from, hoplimit);
640                 if (pingcount > 0 && nreceived >= pingcount)
641                         break;
642         }
643 }
644 #endif
645
646 static void ping(len_and_sockaddr *lsa)
647 {
648         printf("PING %s (%s)", hostname, dotted);
649         if (source_lsa) {
650                 printf(" from %s",
651                         xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len));
652         }
653         printf(": %d data bytes\n", datalen);
654
655 #if ENABLE_PING6
656         if (lsa->sa.sa_family == AF_INET6)
657                 ping6(lsa);
658         else
659 #endif
660                 ping4(lsa);
661 }
662
663 int ping_main(int argc, char **argv);
664 int ping_main(int argc, char **argv)
665 {
666         len_and_sockaddr *lsa;
667         char *opt_c, *opt_s, *opt_I;
668         USE_PING6(sa_family_t af = AF_UNSPEC;)
669
670         datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
671
672         /* exactly one argument needed, -v and -q don't mix */
673         opt_complementary = "=1:q--v:v--q";
674         getopt32(argc, argv, OPT_STRING, &opt_c, &opt_s, &opt_I);
675         if (option_mask32 & OPT_c) pingcount = xatoul(opt_c); // -c
676         if (option_mask32 & OPT_s) datalen = xatou16(opt_s); // -s
677         if (option_mask32 & OPT_I) { // -I
678                 if_index = if_nametoindex(opt_I);
679                 if (!if_index) {
680                         /* TODO: I'm not sure it takes IPv6 unless in [XX:XX..] format */
681                         /* (ping doesn't support source IPv6 addresses yet anyway) */
682                         source_lsa = xdotted2sockaddr(opt_I, 0);
683                 }
684         }
685         myid = (int16_t) getpid();
686         hostname = argv[optind];
687 #if ENABLE_PING6
688         if (option_mask32 & OPT_IPV4)
689                 af = AF_INET;
690         if (option_mask32 & OPT_IPV6)
691                 af = AF_INET6;
692         lsa = xhost_and_af2sockaddr(hostname, 0, af);
693 #else
694         lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
695 #endif
696
697         if (source_lsa && source_lsa->sa.sa_family != lsa->sa.sa_family)
698                 /* leaking it here... */
699                 source_lsa = NULL;
700
701         dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len);
702         ping(lsa);
703         pingstats(0);
704         return EXIT_SUCCESS;
705 }
706 #endif /* FEATURE_FANCY_PING */
707
708
709 #if ENABLE_PING6
710 int ping6_main(int argc, char **argv);
711 int ping6_main(int argc, char **argv)
712 {
713         argv[0] = (char*)"-6";
714         return ping_main(argc + 1, argv - 1);
715 }
716 #endif
717
718 /* from ping6.c:
719  * Copyright (c) 1989 The Regents of the University of California.
720  * All rights reserved.
721  *
722  * This code is derived from software contributed to Berkeley by
723  * Mike Muuss.
724  *
725  * Redistribution and use in source and binary forms, with or without
726  * modification, are permitted provided that the following conditions
727  * are met:
728  * 1. Redistributions of source code must retain the above copyright
729  *    notice, this list of conditions and the following disclaimer.
730  * 2. Redistributions in binary form must reproduce the above copyright
731  *    notice, this list of conditions and the following disclaimer in the
732  *    documentation and/or other materials provided with the distribution.
733  *
734  * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
735  *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
736  *
737  * 4. Neither the name of the University nor the names of its contributors
738  *    may be used to endorse or promote products derived from this software
739  *    without specific prior written permission.
740  *
741  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
742  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
743  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
744  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
745  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
746  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
747  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
748  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
749  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
750  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
751  * SUCH DAMAGE.
752  */