Ignore:
Timestamp:
Nov 4, 2007, 3:16:40 AM (17 years ago)
Author:
Bruno Cornec
Message:

Update to busybox 1.7.2

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.5/mindi-busybox/networking/ping.c

    r821 r1765  
    11/* vi: set sw=4 ts=4: */
    22/*
    3  * $Id: ping.c,v 1.56 2004/03/15 08:28:48 andersen Exp $
    43 * Mini ping implementation for busybox
    54 *
     
    87 * Adapted from the ping in netkit-base 0.10:
    98 * Copyright (c) 1989 The Regents of the University of California.
    10  * Derived from software contributed to Berkeley by Mike Muuss.
     9 * All rights reserved.
     10 *
     11 * This code is derived from software contributed to Berkeley by
     12 * Mike Muuss.
    1113 *
    1214 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
    1315 */
    14 
    15 #include <sys/param.h>
    16 #include <sys/socket.h>
    17 #include <sys/file.h>
    18 #include <sys/times.h>
    19 #include <signal.h>
    20 
    21 #include <netinet/in.h>
    22 #include <netinet/ip.h>
     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>
    2329#include <netinet/ip_icmp.h>
    24 #include <arpa/inet.h>
    25 #include <netdb.h>
    26 #include <stdio.h>
    27 #include <stdlib.h>
    28 #include <errno.h>
    29 #include <unistd.h>
    30 #include <string.h>
    31 #include <stdlib.h>
    32 #include "busybox.h"
    33 
     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
    3441
    3542enum {
     
    4047    MAX_DUP_CHK = (8 * 128),
    4148    MAXWAIT = 10,
    42     PINGINTERVAL = 1        /* second */
     49    PINGINTERVAL = 1, /* 1 second */
    4350};
    4451
    45 #define O_QUIET         (1 << 0)
     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
    46284
    47285#define A(bit)      rcvd_tbl[(bit)>>3]  /* identify byte in array */
     
    51289#define TST(bit)    (A(bit) & B(bit))
    52290
    53 static void ping(const char *host);
    54 
    55 /* common routines */
    56 static int in_cksum(unsigned short *buf, int sz)
    57 {
    58     int nleft = sz;
    59     int sum = 0;
    60     unsigned short *w = buf;
    61     unsigned short ans = 0;
    62 
    63     while (nleft > 1) {
    64         sum += *w++;
    65         nleft -= 2;
    66     }
    67 
    68     if (nleft == 1) {
    69         *(unsigned char *) (&ans) = *(unsigned char *) w;
    70         sum += ans;
    71     }
    72 
    73     sum = (sum >> 16) + (sum & 0xFFFF);
    74     sum += (sum >> 16);
    75     ans = ~sum;
    76     return (ans);
    77 }
    78 
    79 /* simple version */
    80 #ifndef CONFIG_FEATURE_FANCY_PING
    81 static char *hostname = NULL;
    82 static void noresp(int ign)
    83 {
    84     printf("No response from %s\n", hostname);
    85     exit(EXIT_FAILURE);
    86 }
    87 
    88 static void ping(const char *host)
    89 {
    90     struct hostent *h;
    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 
    98     memset(&pingaddr, 0, sizeof(struct sockaddr_in));
    99 
    100     pingaddr.sin_family = AF_INET;
    101     h = xgethostbyname(host);
    102     memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));
    103     hostname = h->h_name;
    104 
    105     pkt = (struct icmp *) packet;
    106     memset(pkt, 0, sizeof(packet));
    107     pkt->icmp_type = ICMP_ECHO;
    108     pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
    109 
    110     c = sendto(pingsock, packet, DEFDATALEN + ICMP_MINLEN, 0,
    111                (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
    112 
    113     if (c < 0) {
    114         if (ENABLE_FEATURE_CLEAN_UP) close(pingsock);
    115         bb_perror_msg_and_die("sendto");
    116     }
    117 
    118     signal(SIGALRM, noresp);
    119     alarm(5);                   /* give the host 5000ms to respond */
    120     /* listen for replies */
    121     while (1) {
    122         struct sockaddr_in from;
    123         socklen_t fromlen = sizeof(from);
    124 
    125         if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
    126                           (struct sockaddr *) &from, &fromlen)) < 0) {
    127             if (errno == EINTR)
    128                 continue;
    129             bb_perror_msg("recvfrom");
    130             continue;
    131         }
    132         if (c >= 76) {          /* ip + icmp */
    133             struct iphdr *iphdr = (struct iphdr *) packet;
    134 
    135             pkt = (struct icmp *) (packet + (iphdr->ihl << 2)); /* skip ip hdr */
    136             if (pkt->icmp_type == ICMP_ECHOREPLY)
    137                 break;
    138         }
    139     }
    140     if (ENABLE_FEATURE_CLEAN_UP) close(pingsock);
    141     printf("%s is alive!\n", hostname);
    142     return;
    143 }
    144 
    145 int ping_main(int argc, char **argv)
    146 {
    147     argc--;
    148     argv++;
    149     if (argc < 1)
    150         bb_show_usage();
    151     ping(*argv);
    152     return EXIT_SUCCESS;
    153 }
    154 
    155 #else /* ! CONFIG_FEATURE_FANCY_PING */
    156 /* full(er) version */
    157 static struct sockaddr_in pingaddr;
    158 static int pingsock = -1;
    159 static int datalen; /* intentionally uninitialized to work around gcc bug */
    160 
    161 static long ntransmitted, nreceived, nrepeats, pingcount;
    162 static int myid, options;
    163 static unsigned long tmin = ULONG_MAX, tmax, tsum;
    164 static char rcvd_tbl[MAX_DUP_CHK / 8];
    165 
    166 #ifndef CONFIG_FEATURE_FANCY_PING6
    167 static
    168 #endif
    169     struct hostent *hostent;
    170 
    171 static void sendping(int);
    172 static void pingstats(int);
    173 static void unpack(char *, int, struct sockaddr_in *);
    174 
    175291/**************************************************************************/
    176292
    177 static void pingstats(int junk)
    178 {
    179     int status;
    180 
     293static void pingstats(int junk ATTRIBUTE_UNUSED)
     294{
    181295    signal(SIGINT, SIG_IGN);
    182296
    183     printf("\n--- %s ping statistics ---\n", hostent->h_name);
    184     printf("%ld packets transmitted, ", ntransmitted);
    185     printf("%ld packets received, ", nreceived);
     297    printf("\n--- %s ping statistics ---\n", hostname);
     298    printf("%lu packets transmitted, ", ntransmitted);
     299    printf("%lu packets received, ", nreceived);
    186300    if (nrepeats)
    187         printf("%ld duplicates, ", nrepeats);
     301        printf("%lu duplicates, ", nrepeats);
    188302    if (ntransmitted)
    189         printf("%ld%% packet loss\n",
    190                (ntransmitted - nreceived) * 100 / ntransmitted);
    191     if (nreceived)
    192         printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
    193                tmin / 10, tmin % 10,
    194                (tsum / (nreceived + nrepeats)) / 10,
    195                (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
    196     if (nreceived != 0)
    197         status = EXIT_SUCCESS;
    198     else
    199         status = EXIT_FAILURE;
    200     exit(status);
    201 }
    202 
    203 static void sendping(int junk)
    204 {
    205     struct icmp *pkt;
    206     int i;
    207     char packet[datalen + ICMP_MINLEN];
    208 
    209     pkt = (struct icmp *) packet;
     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);
    210343
    211344    pkt->icmp_type = ICMP_ECHO;
    212345    pkt->icmp_code = 0;
    213346    pkt->icmp_cksum = 0;
    214     pkt->icmp_seq = htons(ntransmitted++);
     347    pkt->icmp_seq = htons(ntransmitted); /* don't ++ here, it can be a macro */
    215348    pkt->icmp_id = myid;
    216     CLR(ntohs(pkt->icmp_seq) % MAX_DUP_CHK);
    217 
    218     gettimeofday((struct timeval *) &pkt->icmp_dun, NULL);
    219     pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
    220 
    221     i = sendto(pingsock, packet, sizeof(packet), 0,
    222                (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
    223 
    224     if (i < 0)
    225         bb_perror_msg_and_die("sendto");
    226     else if ((size_t)i != sizeof(packet))
    227         bb_error_msg_and_die("ping wrote %d chars; %d expected", i,
    228                (int)sizeof(packet));
    229 
    230     signal(SIGALRM, sendping);
    231     if (pingcount == 0 || ntransmitted < pingcount) {   /* schedule next in 1s */
    232         alarm(PINGINTERVAL);
    233     } else {                    /* done, wait for the last ping to come back */
    234         /* todo, don't necessarily need to wait so long... */
    235         signal(SIGALRM, pingstats);
    236         alarm(MAXWAIT);
    237     }
    238 }
    239 
    240 static char *icmp_type_name (int id)
     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)
    241377{
    242378    switch (id) {
    243     case ICMP_ECHOREPLY:        return "Echo Reply";
    244     case ICMP_DEST_UNREACH:     return "Destination Unreachable";
    245     case ICMP_SOURCE_QUENCH:    return "Source Quench";
    246     case ICMP_REDIRECT:         return "Redirect (change route)";
    247     case ICMP_ECHO:             return "Echo Request";
    248     case ICMP_TIME_EXCEEDED:    return "Time Exceeded";
    249     case ICMP_PARAMETERPROB:    return "Parameter Problem";
    250     case ICMP_TIMESTAMP:        return "Timestamp Request";
    251     case ICMP_TIMESTAMPREPLY:   return "Timestamp Reply";
    252     case ICMP_INFO_REQUEST:     return "Information Request";
    253     case ICMP_INFO_REPLY:       return "Information Reply";
    254     case ICMP_ADDRESS:          return "Address Mask Request";
    255     case ICMP_ADDRESSREPLY:     return "Address Mask Reply";
    256     default:                    return "unknown ICMP type";
    257     }
    258 }
    259 
    260 static void unpack(char *buf, int sz, struct sockaddr_in *from)
    261 {
    262     struct icmp *icmppkt;
    263     struct iphdr *iphdr;
    264     struct timeval tv, *tp;
    265     int hlen, dupflag;
    266     unsigned long triptime;
    267 
    268     gettimeofday(&tv, NULL);
    269 
    270     /* check IP header */
    271     iphdr = (struct iphdr *) buf;
    272     hlen = iphdr->ihl << 2;
    273     /* discard if too short */
    274     if (sz < (datalen + ICMP_MINLEN))
    275         return;
    276 
    277     sz -= hlen;
    278     icmppkt = (struct icmp *) (buf + hlen);
    279 
    280     if (icmppkt->icmp_id != myid)
    281         return;             /* not our ping */
    282 
    283     if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
    284         u_int16_t recv_seq = ntohs(icmppkt->icmp_seq);
    285         ++nreceived;
    286         tp = (struct timeval *) icmppkt->icmp_data;
    287 
    288         if ((tv.tv_usec -= tp->tv_usec) < 0) {
    289             --tv.tv_sec;
    290             tv.tv_usec += 1000000;
    291         }
    292         tv.tv_sec -= tp->tv_sec;
    293 
    294         triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
     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);
    295437        tsum += triptime;
    296438        if (triptime < tmin)
     
    298440        if (triptime > tmax)
    299441            tmax = triptime;
    300 
    301         if (TST(recv_seq % MAX_DUP_CHK)) {
    302             ++nrepeats;
    303             --nreceived;
    304             dupflag = 1;
    305         } else {
    306             SET(recv_seq % MAX_DUP_CHK);
    307             dupflag = 0;
    308         }
    309 
    310         if (options & O_QUIET)
    311             return;
    312 
    313         printf("%d bytes from %s: icmp_seq=%u", sz,
    314                inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
    315                recv_seq);
    316         printf(" ttl=%d", iphdr->ttl);
    317         printf(" time=%lu.%lu ms", triptime / 10, triptime % 10);
    318         if (dupflag)
    319             printf(" (DUP!)");
    320         printf("\n");
    321     } else
    322         if (icmppkt->icmp_type != ICMP_ECHO)
    323             bb_error_msg("Warning: Got ICMP %d (%s)",
    324                     icmppkt->icmp_type, icmp_type_name (icmppkt->icmp_type));
     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);
    325460    fflush(stdout);
    326461}
    327 
    328 static void ping(const char *host)
     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)
    329528{
    330529    char packet[datalen + MAXIPLEN + MAXICMPLEN];
     
    332531
    333532    pingsock = create_icmp_socket();
    334 
    335     memset(&pingaddr, 0, sizeof(struct sockaddr_in));
    336 
    337     pingaddr.sin_family = AF_INET;
    338     hostent = xgethostbyname(host);
    339     if (hostent->h_addrtype != AF_INET)
    340         bb_error_msg_and_die("unknown address type; only AF_INET is currently supported.");
    341 
    342     memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr));
     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    }
    343540
    344541    /* enable broadcast pings */
    345     sockopt = 1;
    346     setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt,
    347                sizeof(sockopt));
     542    setsockopt_broadcast(pingsock);
    348543
    349544    /* set recv buf for broadcast pings */
    350     sockopt = 48 * 1024;
    351     setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,
    352                sizeof(sockopt));
    353 
    354     printf("PING %s (%s): %d data bytes\n",
    355                hostent->h_name,
    356            inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr),
    357            datalen);
     545    sockopt = 48 * 1024; /* explain why 48k? */
     546    setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
    358547
    359548    signal(SIGINT, pingstats);
    360549
    361550    /* start the ping's going ... */
    362     sendping(0);
     551    sendping4(0);
    363552
    364553    /* listen for replies */
     
    368557        int c;
    369558
    370         if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
    371                           (struct sockaddr *) &from, &fromlen)) < 0) {
    372             if (errno == EINTR)
    373                 continue;
    374             bb_perror_msg("recvfrom");
     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");
    375564            continue;
    376565        }
    377         unpack(packet, c, &from);
     566        unpack4(packet, c, &from);
    378567        if (pingcount > 0 && nreceived >= pingcount)
    379568            break;
    380569    }
     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);
    381720    pingstats(0);
    382 }
    383 
    384 int ping_main(int argc, char **argv)
    385 {
    386     char *thisarg;
    387 
    388     datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
    389 
    390     argc--;
    391     argv++;
    392     options = 0;
    393     /* Parse any options */
    394     while (argc >= 1 && **argv == '-') {
    395         thisarg = *argv;
    396         thisarg++;
    397         switch (*thisarg) {
    398         case 'q':
    399             options |= O_QUIET;
    400             break;
    401         case 'c':
    402             if (--argc <= 0)
    403                     bb_show_usage();
    404             argv++;
    405             pingcount = atoi(*argv);
    406             break;
    407         case 's':
    408             if (--argc <= 0)
    409                     bb_show_usage();
    410             argv++;
    411             datalen = atoi(*argv);
    412             break;
    413         default:
    414             bb_show_usage();
    415         }
    416         argc--;
    417         argv++;
    418     }
    419     if (argc < 1)
    420         bb_show_usage();
    421 
    422     myid = getpid() & 0xFFFF;
    423     ping(*argv);
    424721    return EXIT_SUCCESS;
    425722}
    426 #endif /* ! CONFIG_FEATURE_FANCY_PING */
     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 TracChangeset for help on using the changeset viewer.