busybox/networking/arping.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * arping.c - Ping hosts by ARP requests/replies
   4 *
   5 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
   6 *
   7 * Author:      Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
   8 * Busybox port: Nick Fedchik <nick@fedchik.org.ua>
   9 */
  10
  11#include <arpa/inet.h>
  12#include <net/if.h>
  13#include <netinet/ether.h>
  14#include <netpacket/packet.h>
  15
  16#include "libbb.h"
  17
  18/* We don't expect to see 1000+ seconds delay, unsigned is enough */
  19#define MONOTONIC_US() ((unsigned)monotonic_us())
  20
  21enum {
  22        DAD = 1,
  23        UNSOLICITED = 2,
  24        ADVERT = 4,
  25        QUIET = 8,
  26        QUIT_ON_REPLY = 16,
  27        BCAST_ONLY = 32,
  28        UNICASTING = 64
  29};
  30
  31struct globals {
  32        struct in_addr src;
  33        struct in_addr dst;
  34        struct sockaddr_ll me;
  35        struct sockaddr_ll he;
  36        int sock_fd;
  37
  38        int count; // = -1;
  39        unsigned last;
  40        unsigned timeout_us;
  41        unsigned start;
  42
  43        unsigned sent;
  44        unsigned brd_sent;
  45        unsigned received;
  46        unsigned brd_recv;
  47        unsigned req_recv;
  48};
  49#define G (*(struct globals*)&bb_common_bufsiz1)
  50#define src        (G.src       )
  51#define dst        (G.dst       )
  52#define me         (G.me        )
  53#define he         (G.he        )
  54#define sock_fd    (G.sock_fd   )
  55#define count      (G.count     )
  56#define last       (G.last      )
  57#define timeout_us (G.timeout_us)
  58#define start      (G.start     )
  59#define sent       (G.sent      )
  60#define brd_sent   (G.brd_sent  )
  61#define received   (G.received  )
  62#define brd_recv   (G.brd_recv  )
  63#define req_recv   (G.req_recv  )
  64#define INIT_G() do { \
  65        count = -1; \
  66} while (0)
  67
  68// If GNUisms are not available...
  69//static void *mempcpy(void *_dst, const void *_src, int n)
  70//{
  71//      memcpy(_dst, _src, n);
  72//      return (char*)_dst + n;
  73//}
  74
  75static int send_pack(struct in_addr *src_addr,
  76                        struct in_addr *dst_addr, struct sockaddr_ll *ME,
  77                        struct sockaddr_ll *HE)
  78{
  79        int err;
  80        unsigned char buf[256];
  81        struct arphdr *ah = (struct arphdr *) buf;
  82        unsigned char *p = (unsigned char *) (ah + 1);
  83
  84        ah->ar_hrd = htons(ARPHRD_ETHER);
  85        ah->ar_pro = htons(ETH_P_IP);
  86        ah->ar_hln = ME->sll_halen;
  87        ah->ar_pln = 4;
  88        ah->ar_op = option_mask32 & ADVERT ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);
  89
  90        p = mempcpy(p, &ME->sll_addr, ah->ar_hln);
  91        p = mempcpy(p, src_addr, 4);
  92
  93        if (option_mask32 & ADVERT)
  94                p = mempcpy(p, &ME->sll_addr, ah->ar_hln);
  95        else
  96                p = mempcpy(p, &HE->sll_addr, ah->ar_hln);
  97
  98        p = mempcpy(p, dst_addr, 4);
  99
 100        err = sendto(sock_fd, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE));
 101        if (err == p - buf) {
 102                last = MONOTONIC_US();
 103                sent++;
 104                if (!(option_mask32 & UNICASTING))
 105                        brd_sent++;
 106        }
 107        return err;
 108}
 109
 110static void finish(void) NORETURN;
 111static void finish(void)
 112{
 113        if (!(option_mask32 & QUIET)) {
 114                printf("Sent %u probe(s) (%u broadcast(s))\n"
 115                        "Received %u repl%s"
 116                        " (%u request(s), %u broadcast(s))\n",
 117                        sent, brd_sent,
 118                        received, (received == 1) ? "ies" : "y",
 119                        req_recv, brd_recv);
 120        }
 121        if (option_mask32 & DAD)
 122                exit(!!received);
 123        if (option_mask32 & UNSOLICITED)
 124                exit(EXIT_SUCCESS);
 125        exit(!received);
 126}
 127
 128static void catcher(void)
 129{
 130        unsigned now;
 131
 132        now = MONOTONIC_US();
 133        if (start == 0)
 134                start = now;
 135
 136        if (count == 0 || (timeout_us && (now - start) > timeout_us))
 137                finish();
 138
 139        /* count < 0 means "infinite count" */
 140        if (count > 0)
 141                count--;
 142
 143        if (last == 0 || (now - last) > 500000) {
 144                send_pack(&src, &dst, &me, &he);
 145                if (count == 0 && (option_mask32 & UNSOLICITED))
 146                        finish();
 147        }
 148        alarm(1);
 149}
 150
 151static bool recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
 152{
 153        struct arphdr *ah = (struct arphdr *) buf;
 154        unsigned char *p = (unsigned char *) (ah + 1);
 155        struct in_addr src_ip, dst_ip;
 156        /* moves below assume in_addr is 4 bytes big, ensure that */
 157        struct BUG_in_addr_must_be_4 {
 158                char BUG_in_addr_must_be_4[
 159                        sizeof(struct in_addr) == 4 ? 1 : -1
 160                ];
 161                char BUG_s_addr_must_be_4[
 162                        sizeof(src_ip.s_addr) == 4 ? 1 : -1
 163                ];
 164        };
 165
 166        /* Filter out wild packets */
 167        if (FROM->sll_pkttype != PACKET_HOST
 168         && FROM->sll_pkttype != PACKET_BROADCAST
 169         && FROM->sll_pkttype != PACKET_MULTICAST)
 170                return false;
 171
 172        /* Only these types are recognized */
 173        if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY))
 174                return false;
 175
 176        /* ARPHRD check and this darned FDDI hack here :-( */
 177        if (ah->ar_hrd != htons(FROM->sll_hatype)
 178         && (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER)))
 179                return false;
 180
 181        /* Protocol must be IP. */
 182        if (ah->ar_pro != htons(ETH_P_IP)
 183         || (ah->ar_pln != 4)
 184         || (ah->ar_hln != me.sll_halen)
 185         || (len < (int)(sizeof(*ah) + 2 * (4 + ah->ar_hln))))
 186                return false;
 187
 188        move_from_unaligned32(src_ip.s_addr, p + ah->ar_hln);
 189        move_from_unaligned32(dst_ip.s_addr, p + ah->ar_hln + 4 + ah->ar_hln);
 190
 191        if (dst.s_addr != src_ip.s_addr)
 192                return false;
 193        if (!(option_mask32 & DAD)) {
 194                if ((src.s_addr != dst_ip.s_addr)
 195                        || (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln)))
 196                        return false;
 197        } else {
 198                /* DAD packet was:
 199                   src_ip = 0 (or some src)
 200                   src_hw = ME
 201                   dst_ip = tested address
 202                   dst_hw = <unspec>
 203
 204                   We fail, if receive request/reply with:
 205                   src_ip = tested_address
 206                   src_hw != ME
 207                   if src_ip in request was not zero, check
 208                   also that it matches to dst_ip, otherwise
 209                   dst_ip/dst_hw do not matter.
 210                 */
 211                if ((memcmp(p, &me.sll_addr, me.sll_halen) == 0)
 212                 || (src.s_addr && src.s_addr != dst_ip.s_addr))
 213                        return false;
 214        }
 215        if (!(option_mask32 & QUIET)) {
 216                int s_printed = 0;
 217
 218                printf("%scast re%s from %s [%s]",
 219                        FROM->sll_pkttype == PACKET_HOST ? "Uni" : "Broad",
 220                        ah->ar_op == htons(ARPOP_REPLY) ? "ply" : "quest",
 221                        inet_ntoa(src_ip),
 222                        ether_ntoa((struct ether_addr *) p));
 223                if (dst_ip.s_addr != src.s_addr) {
 224                        printf("for %s ", inet_ntoa(dst_ip));
 225                        s_printed = 1;
 226                }
 227                if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) {
 228                        if (!s_printed)
 229                                printf("for ");
 230                        printf("[%s]",
 231                                ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4));
 232                }
 233
 234                if (last) {
 235                        unsigned diff = MONOTONIC_US() - last;
 236                        printf(" %u.%03ums\n", diff / 1000, diff % 1000);
 237                } else {
 238                        printf(" UNSOLICITED?\n");
 239                }
 240                fflush(stdout);
 241        }
 242        received++;
 243        if (FROM->sll_pkttype != PACKET_HOST)
 244                brd_recv++;
 245        if (ah->ar_op == htons(ARPOP_REQUEST))
 246                req_recv++;
 247        if (option_mask32 & QUIT_ON_REPLY)
 248                finish();
 249        if (!(option_mask32 & BCAST_ONLY)) {
 250                memcpy(he.sll_addr, p, me.sll_halen);
 251                option_mask32 |= UNICASTING;
 252        }
 253        return true;
 254}
 255
 256int arping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 257int arping_main(int argc UNUSED_PARAM, char **argv)
 258{
 259        const char *device = "eth0";
 260        char *source = NULL;
 261        char *target;
 262        unsigned char *packet;
 263        char *err_str;
 264
 265        INIT_G();
 266
 267        sock_fd = xsocket(AF_PACKET, SOCK_DGRAM, 0);
 268
 269        // Drop suid root privileges
 270        // Need to remove SUID_NEVER from applets.h for this to work
 271        //xsetuid(getuid());
 272
 273        err_str = xasprintf("interface %s %%s", device);
 274        {
 275                unsigned opt;
 276                char *str_timeout;
 277
 278                /* Dad also sets quit_on_reply.
 279                 * Advert also sets unsolicited.
 280                 */
 281                opt_complementary = "=1:Df:AU:c+";
 282                opt = getopt32(argv, "DUAqfbc:w:I:s:",
 283                                &count, &str_timeout, &device, &source);
 284                if (opt & 0x80) /* -w: timeout */
 285                        timeout_us = xatou_range(str_timeout, 0, INT_MAX/2000000) * 1000000 + 500000;
 286                //if (opt & 0x200) /* -s: source */
 287                option_mask32 &= 0x3f; /* set respective flags */
 288        }
 289
 290        target = argv[optind];
 291
 292        xfunc_error_retval = 2;
 293
 294        {
 295                struct ifreq ifr;
 296
 297                memset(&ifr, 0, sizeof(ifr));
 298                strncpy_IFNAMSIZ(ifr.ifr_name, device);
 299                /* We use ifr.ifr_name in error msg so that problem
 300                 * with truncated name will be visible */
 301                ioctl_or_perror_and_die(sock_fd, SIOCGIFINDEX, &ifr, err_str, "not found");
 302                me.sll_ifindex = ifr.ifr_ifindex;
 303
 304                xioctl(sock_fd, SIOCGIFFLAGS, (char *) &ifr);
 305
 306                if (!(ifr.ifr_flags & IFF_UP)) {
 307                        bb_error_msg_and_die(err_str, "is down");
 308                }
 309                if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) {
 310                        bb_error_msg(err_str, "is not ARPable");
 311                        return (option_mask32 & DAD ? 0 : 2);
 312                }
 313        }
 314
 315        /* if (!inet_aton(target, &dst)) - not needed */ {
 316                len_and_sockaddr *lsa;
 317                lsa = xhost_and_af2sockaddr(target, 0, AF_INET);
 318                dst = lsa->u.sin.sin_addr;
 319                if (ENABLE_FEATURE_CLEAN_UP)
 320                        free(lsa);
 321        }
 322
 323        if (source && !inet_aton(source, &src)) {
 324                bb_error_msg_and_die("invalid source address %s", source);
 325        }
 326
 327        if ((option_mask32 & (DAD|UNSOLICITED)) == UNSOLICITED && src.s_addr == 0)
 328                src = dst;
 329
 330        if (!(option_mask32 & DAD) || src.s_addr) {
 331                struct sockaddr_in saddr;
 332                int probe_fd = xsocket(AF_INET, SOCK_DGRAM, 0);
 333
 334                setsockopt_bindtodevice(probe_fd, device);
 335                memset(&saddr, 0, sizeof(saddr));
 336                saddr.sin_family = AF_INET;
 337                if (src.s_addr) {
 338                        /* Check that this is indeed our IP */
 339                        saddr.sin_addr = src;
 340                        xbind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
 341                } else { /* !(option_mask32 & DAD) case */
 342                        /* Find IP address on this iface */
 343                        socklen_t alen = sizeof(saddr);
 344
 345                        saddr.sin_port = htons(1025);
 346                        saddr.sin_addr = dst;
 347
 348                        if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, &const_int_1, sizeof(const_int_1)) == -1)
 349                                bb_perror_msg("setsockopt(SO_DONTROUTE)");
 350                        xconnect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
 351                        getsockname(probe_fd, (struct sockaddr *) &saddr, &alen);
 352                        //never happens:
 353                        //if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) == -1)
 354                        //      bb_perror_msg_and_die("getsockname");
 355                        if (saddr.sin_family != AF_INET)
 356                                bb_error_msg_and_die("no IP address configured");
 357                        src = saddr.sin_addr;
 358                }
 359                close(probe_fd);
 360        }
 361
 362        me.sll_family = AF_PACKET;
 363        //me.sll_ifindex = ifindex; - done before
 364        me.sll_protocol = htons(ETH_P_ARP);
 365        xbind(sock_fd, (struct sockaddr *) &me, sizeof(me));
 366
 367        {
 368                socklen_t alen = sizeof(me);
 369                getsockname(sock_fd, (struct sockaddr *) &me, &alen);
 370                //never happens:
 371                //if (getsockname(sock_fd, (struct sockaddr *) &me, &alen) == -1)
 372                //      bb_perror_msg_and_die("getsockname");
 373        }
 374        if (me.sll_halen == 0) {
 375                bb_error_msg(err_str, "is not ARPable (no ll address)");
 376                return (option_mask32 & DAD ? 0 : 2);
 377        }
 378        he = me;
 379        memset(he.sll_addr, -1, he.sll_halen);
 380
 381        if (!(option_mask32 & QUIET)) {
 382                /* inet_ntoa uses static storage, can't use in same printf */
 383                printf("ARPING to %s", inet_ntoa(dst));
 384                printf(" from %s via %s\n", inet_ntoa(src), device);
 385        }
 386
 387        signal_SA_RESTART_empty_mask(SIGINT,  (void (*)(int))finish);
 388        signal_SA_RESTART_empty_mask(SIGALRM, (void (*)(int))catcher);
 389
 390        catcher();
 391
 392        packet = xmalloc(4096);
 393        while (1) {
 394                sigset_t sset, osset;
 395                struct sockaddr_ll from;
 396                socklen_t alen = sizeof(from);
 397                int cc;
 398
 399                cc = recvfrom(sock_fd, packet, 4096, 0, (struct sockaddr *) &from, &alen);
 400                if (cc < 0) {
 401                        bb_perror_msg("recvfrom");
 402                        continue;
 403                }
 404                sigemptyset(&sset);
 405                sigaddset(&sset, SIGALRM);
 406                sigaddset(&sset, SIGINT);
 407                sigprocmask(SIG_BLOCK, &sset, &osset);
 408                recv_pack(packet, cc, &from);
 409                sigprocmask(SIG_SETMASK, &osset, NULL);
 410        }
 411}
 412