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