source: MondoRescue/branches/stable/mindi-busybox/networking/ping.c @ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 12 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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