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