busybox/networking/arp.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * arp.c - Manipulate the system ARP cache
   4 *
   5 * This program is free software; you can redistribute it and/or
   6 * modify it under the terms of the GNU General Public License
   7 * as published by the Free Software Foundation; either version
   8 * 2 of the License, or (at your option) any later version.
   9 *
  10 * Author: Fred N. van Kempen, <waltje at uwalt.nl.mugnet.org>
  11 * Busybox port: Paul van Gool <pvangool at mimotech.com>
  12 *
  13 * modified for getopt32 by Arne Bernin <arne [at] alamut.de>
  14 */
  15//config:config ARP
  16//config:       bool "arp (10 kb)"
  17//config:       default y
  18//config:       help
  19//config:       Manipulate the system ARP cache.
  20
  21//applet:IF_ARP(APPLET(arp, BB_DIR_SBIN, BB_SUID_DROP))
  22
  23//kbuild:lib-$(CONFIG_ARP) += arp.o interface.o
  24
  25//usage:#define arp_trivial_usage
  26//usage:     "\n[-vn]   [-H HWTYPE] [-i IF] -a [HOSTNAME]"
  27//usage:     "\n[-v]                [-i IF] -d HOSTNAME [pub]"
  28//usage:     "\n[-v]    [-H HWTYPE] [-i IF] -s HOSTNAME HWADDR [temp]"
  29//usage:     "\n[-v]    [-H HWTYPE] [-i IF] -s HOSTNAME HWADDR [netmask MASK] pub"
  30//usage:     "\n[-v]    [-H HWTYPE] [-i IF] -Ds HOSTNAME IFACE [netmask MASK] pub"
  31//usage:#define arp_full_usage "\n\n"
  32//usage:       "Manipulate ARP cache\n"
  33//usage:       "\n      -a              Display (all) hosts"
  34//usage:       "\n      -d              Delete ARP entry"
  35//usage:       "\n      -s              Set new entry"
  36//usage:       "\n      -v              Verbose"
  37//usage:       "\n      -n              Don't resolve names"
  38//usage:       "\n      -i IF           Network interface"
  39//usage:       "\n      -D              Read HWADDR from IFACE"
  40//usage:       "\n      -A,-p AF        Protocol family"
  41//usage:       "\n      -H HWTYPE       Hardware address type"
  42
  43#include "libbb.h"
  44#include "common_bufsiz.h"
  45#include "inet_common.h"
  46
  47#include <arpa/inet.h>
  48#include <net/if.h>
  49#include <net/if_arp.h>
  50#include <netinet/ether.h>
  51#include <netpacket/packet.h>
  52
  53#define DEBUG 0
  54
  55#define DFLT_AF "inet"
  56#define DFLT_HW "ether"
  57
  58enum {
  59        ARP_OPT_A = (1 << 0),
  60        ARP_OPT_p = (1 << 1),
  61        ARP_OPT_H = (1 << 2),
  62        ARP_OPT_t = (1 << 3),
  63        ARP_OPT_i = (1 << 4),
  64        ARP_OPT_a = (1 << 5),
  65        ARP_OPT_d = (1 << 6),
  66        ARP_OPT_n = (1 << 7), /* do not resolve addresses */
  67        ARP_OPT_D = (1 << 8), /* HW-address is devicename */
  68        ARP_OPT_s = (1 << 9),
  69        ARP_OPT_v = (1 << 10) * DEBUG, /* debugging output flag */
  70};
  71
  72enum {
  73        sockfd = 3, /* active socket descriptor */
  74};
  75
  76struct globals {
  77        const struct aftype *ap; /* current address family */
  78        const struct hwtype *hw; /* current hardware type */
  79        const char *device;      /* current device */
  80        smallint hw_set;         /* flag if hw-type was set (-H) */
  81} FIX_ALIASING;
  82#define G (*(struct globals*)bb_common_bufsiz1)
  83#define ap         (G.ap        )
  84#define hw         (G.hw        )
  85#define device     (G.device    )
  86#define hw_set     (G.hw_set    )
  87#define INIT_G() do { \
  88        setup_common_bufsiz(); \
  89        device = ""; \
  90} while (0)
  91
  92
  93static const char options[] ALIGN1 =
  94        "pub\0"
  95        "priv\0"
  96        "temp\0"
  97        "trail\0"
  98        "dontpub\0"
  99        "auto\0"
 100        "dev\0"
 101        "netmask\0";
 102
 103/* Delete an entry from the ARP cache. */
 104/* Called only from main, once */
 105static int arp_del(char **args)
 106{
 107        char *host;
 108        struct arpreq req;
 109        struct sockaddr sa;
 110        int flags = 0;
 111        int err;
 112
 113        memset(&req, 0, sizeof(req));
 114
 115        /* Resolve the host name. */
 116        host = *args;
 117        if (ap->input(host, &sa) < 0) {
 118                bb_simple_herror_msg_and_die(host);
 119        }
 120
 121        /* If a host has more than one address, use the correct one! */
 122        memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr));
 123
 124        if (hw_set)
 125                req.arp_ha.sa_family = hw->type;
 126
 127        req.arp_flags = ATF_PERM;
 128        args++;
 129        while (*args != NULL) {
 130                switch (index_in_strings(options, *args)) {
 131                case 0: /* "pub" */
 132                        flags |= 1;
 133                        args++;
 134                        break;
 135                case 1: /* "priv" */
 136                        flags |= 2;
 137                        args++;
 138                        break;
 139                case 2: /* "temp" */
 140                        req.arp_flags &= ~ATF_PERM;
 141                        args++;
 142                        break;
 143                case 3: /* "trail" */
 144                        req.arp_flags |= ATF_USETRAILERS;
 145                        args++;
 146                        break;
 147                case 4: /* "dontpub" */
 148#ifdef HAVE_ATF_DONTPUB
 149                        req.arp_flags |= ATF_DONTPUB;
 150#else
 151                        bb_simple_error_msg("feature ATF_DONTPUB is not supported");
 152#endif
 153                        args++;
 154                        break;
 155                case 5: /* "auto" */
 156#ifdef HAVE_ATF_MAGIC
 157                        req.arp_flags |= ATF_MAGIC;
 158#else
 159                        bb_simple_error_msg("feature ATF_MAGIC is not supported");
 160#endif
 161                        args++;
 162                        break;
 163                case 6: /* "dev" */
 164                        if (*++args == NULL)
 165                                bb_show_usage();
 166                        device = *args;
 167                        args++;
 168                        break;
 169                case 7: /* "netmask" */
 170                        if (*++args == NULL)
 171                                bb_show_usage();
 172                        if (strcmp(*args, "255.255.255.255") != 0) {
 173                                host = *args;
 174                                if (ap->input(host, &sa) < 0) {
 175                                        bb_simple_herror_msg_and_die(host);
 176                                }
 177                                memcpy(&req.arp_netmask, &sa, sizeof(struct sockaddr));
 178                                req.arp_flags |= ATF_NETMASK;
 179                        }
 180                        args++;
 181                        break;
 182                default:
 183                        bb_show_usage();
 184                        break;
 185                }
 186        }
 187        if (flags == 0)
 188                flags = 3;
 189
 190        strncpy_IFNAMSIZ(req.arp_dev, device);
 191
 192        err = -1;
 193
 194        /* Call the kernel. */
 195        if (flags & 2) {
 196                if (option_mask32 & ARP_OPT_v)
 197                        bb_simple_error_msg("SIOCDARP(nopub)");
 198                err = ioctl(sockfd, SIOCDARP, &req);
 199                if (err < 0) {
 200                        if (errno == ENXIO) {
 201                                if (flags & 1)
 202                                        goto nopub;
 203                                printf("No ARP entry for %s\n", host);
 204                                return -1;
 205                        }
 206                        bb_simple_perror_msg_and_die("SIOCDARP(priv)");
 207                }
 208        }
 209        if ((flags & 1) && err) {
 210 nopub:
 211                req.arp_flags |= ATF_PUBL;
 212                if (option_mask32 & ARP_OPT_v)
 213                        bb_simple_error_msg("SIOCDARP(pub)");
 214                if (ioctl(sockfd, SIOCDARP, &req) < 0) {
 215                        if (errno == ENXIO) {
 216                                printf("No ARP entry for %s\n", host);
 217                                return -1;
 218                        }
 219                        bb_simple_perror_msg_and_die("SIOCDARP(pub)");
 220                }
 221        }
 222        return 0;
 223}
 224
 225/* Get the hardware address to a specified interface name */
 226static void arp_getdevhw(char *ifname, struct sockaddr *sa)
 227{
 228        struct ifreq ifr;
 229        const struct hwtype *xhw;
 230
 231        strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
 232        ioctl_or_perror_and_die(sockfd, SIOCGIFHWADDR, &ifr,
 233                                        "can't get HW-Address for '%s'", ifname);
 234        if (hw_set && (ifr.ifr_hwaddr.sa_family != hw->type)) {
 235                bb_simple_error_msg_and_die("protocol type mismatch");
 236        }
 237        memcpy(sa, &(ifr.ifr_hwaddr), sizeof(struct sockaddr));
 238
 239        if (option_mask32 & ARP_OPT_v) {
 240                xhw = get_hwntype(ifr.ifr_hwaddr.sa_family);
 241                if (!xhw || !xhw->print) {
 242                        xhw = get_hwntype(-1);
 243                }
 244                bb_error_msg("device '%s' has HW address %s '%s'",
 245                                ifname, xhw->name,
 246                                xhw->print((unsigned char *) &ifr.ifr_hwaddr.sa_data));
 247        }
 248}
 249
 250/* Set an entry in the ARP cache. */
 251/* Called only from main, once */
 252static int arp_set(char **args)
 253{
 254        char *host;
 255        struct arpreq req;
 256        struct sockaddr sa;
 257        int flags;
 258
 259        memset(&req, 0, sizeof(req));
 260
 261        host = *args++;
 262        if (ap->input(host, &sa) < 0) {
 263                bb_simple_herror_msg_and_die(host);
 264        }
 265        /* If a host has more than one address, use the correct one! */
 266        memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr));
 267
 268        /* Fetch the hardware address. */
 269        if (*args == NULL) {
 270                bb_simple_error_msg_and_die("need hardware address");
 271        }
 272        if (option_mask32 & ARP_OPT_D) {
 273                arp_getdevhw(*args++, &req.arp_ha);
 274        } else {
 275                if (hw->input(*args++, &req.arp_ha) < 0) {
 276                        bb_simple_error_msg_and_die("invalid hardware address");
 277                }
 278        }
 279
 280        /* Check out any modifiers. */
 281        flags = ATF_PERM | ATF_COM;
 282        while (*args != NULL) {
 283                switch (index_in_strings(options, *args)) {
 284                case 0: /* "pub" */
 285                        flags |= ATF_PUBL;
 286                        args++;
 287                        break;
 288                case 1: /* "priv" */
 289                        flags &= ~ATF_PUBL;
 290                        args++;
 291                        break;
 292                case 2: /* "temp" */
 293                        flags &= ~ATF_PERM;
 294                        args++;
 295                        break;
 296                case 3: /* "trail" */
 297                        flags |= ATF_USETRAILERS;
 298                        args++;
 299                        break;
 300                case 4: /* "dontpub" */
 301#ifdef HAVE_ATF_DONTPUB
 302                        flags |= ATF_DONTPUB;
 303#else
 304                        bb_simple_error_msg("feature ATF_DONTPUB is not supported");
 305#endif
 306                        args++;
 307                        break;
 308                case 5: /* "auto" */
 309#ifdef HAVE_ATF_MAGIC
 310                        flags |= ATF_MAGIC;
 311#else
 312                        bb_simple_error_msg("feature ATF_MAGIC is not supported");
 313#endif
 314                        args++;
 315                        break;
 316                case 6: /* "dev" */
 317                        if (*++args == NULL)
 318                                bb_show_usage();
 319                        device = *args;
 320                        args++;
 321                        break;
 322                case 7: /* "netmask" */
 323                        if (*++args == NULL)
 324                                bb_show_usage();
 325                        if (strcmp(*args, "255.255.255.255") != 0) {
 326                                host = *args;
 327                                if (ap->input(host, &sa) < 0) {
 328                                        bb_simple_herror_msg_and_die(host);
 329                                }
 330                                memcpy(&req.arp_netmask, &sa, sizeof(struct sockaddr));
 331                                flags |= ATF_NETMASK;
 332                        }
 333                        args++;
 334                        break;
 335                default:
 336                        bb_show_usage();
 337                        break;
 338                }
 339        }
 340
 341        /* Fill in the remainder of the request. */
 342        req.arp_flags = flags;
 343
 344        strncpy_IFNAMSIZ(req.arp_dev, device);
 345
 346        /* Call the kernel. */
 347        if (option_mask32 & ARP_OPT_v)
 348                bb_simple_error_msg("SIOCSARP()");
 349        xioctl(sockfd, SIOCSARP, &req);
 350        return 0;
 351}
 352
 353
 354/* Print the contents of an ARP request block. */
 355static void
 356arp_disp(const char *name, char *ip, int type, int arp_flags,
 357                char *hwa, char *mask, char *dev)
 358{
 359        static const int arp_masks[] = {
 360                ATF_PERM, ATF_PUBL,
 361#ifdef HAVE_ATF_MAGIC
 362                ATF_MAGIC,
 363#endif
 364#ifdef HAVE_ATF_DONTPUB
 365                ATF_DONTPUB,
 366#endif
 367                ATF_USETRAILERS,
 368        };
 369        static const char arp_labels[] ALIGN1 = "PERM\0""PUP\0"
 370#ifdef HAVE_ATF_MAGIC
 371                "AUTO\0"
 372#endif
 373#ifdef HAVE_ATF_DONTPUB
 374                "DONTPUB\0"
 375#endif
 376                "TRAIL\0"
 377        ;
 378
 379        const struct hwtype *xhw;
 380
 381        xhw = get_hwntype(type);
 382        if (xhw == NULL)
 383                xhw = get_hwtype(DFLT_HW);
 384
 385        printf("%s (%s) at ", name, ip);
 386
 387        if (!(arp_flags & ATF_COM)) {
 388                if (arp_flags & ATF_PUBL)
 389                        printf("* ");
 390                else
 391                        printf("<incomplete> ");
 392        } else {
 393                printf("%s [%s] ", hwa, xhw->name);
 394        }
 395
 396        if (arp_flags & ATF_NETMASK)
 397                printf("netmask %s ", mask);
 398
 399        print_flags_separated(arp_masks, arp_labels, arp_flags, " ");
 400        printf(" on %s\n", dev);
 401}
 402
 403/* Display the contents of the ARP cache in the kernel. */
 404/* Called only from main, once */
 405static int arp_show(char *name)
 406{
 407        const char *host;
 408        const char *hostname;
 409        FILE *fp;
 410        struct sockaddr sa;
 411        int type, flags;
 412        int num;
 413        unsigned entries = 0, shown = 0;
 414        char ip[128];
 415        char hwa[128];
 416        char mask[128];
 417        char line[128];
 418        char dev[128];
 419
 420        host = NULL;
 421        if (name != NULL) {
 422                /* Resolve the host name. */
 423                if (ap->input(name, &sa) < 0) {
 424                        bb_simple_herror_msg_and_die(name);
 425                }
 426                host = xstrdup(ap->sprint(&sa, 1));
 427        }
 428        fp = xfopen_for_read("/proc/net/arp");
 429        /* Bypass header -- read one line */
 430        fgets(line, sizeof(line), fp);
 431
 432        /* Read the ARP cache entries. */
 433        while (fgets(line, sizeof(line), fp)) {
 434
 435                mask[0] = '-'; mask[1] = '\0';
 436                dev[0] = '-'; dev[1] = '\0';
 437                /* All these strings can't overflow
 438                 * because fgets above reads limited amount of data */
 439                num = sscanf(line, "%s 0x%x 0x%x %s %s %s\n",
 440                                        ip, &type, &flags, hwa, mask, dev);
 441                if (num < 4)
 442                        break;
 443
 444                entries++;
 445                /* if the user specified hw-type differs, skip it */
 446                if (hw_set && (type != hw->type))
 447                        continue;
 448
 449                /* if the user specified address differs, skip it */
 450                if (host && strcmp(ip, host) != 0)
 451                        continue;
 452
 453                /* if the user specified device differs, skip it */
 454                if (device[0] && strcmp(dev, device) != 0)
 455                        continue;
 456
 457                shown++;
 458                /* This IS ugly but it works -be */
 459                hostname = "?";
 460                if (!(option_mask32 & ARP_OPT_n)) {
 461                        if (ap->input(ip, &sa) < 0)
 462                                hostname = ip;
 463                        else
 464                                hostname = ap->sprint(&sa, (option_mask32 & ARP_OPT_n) | 0x8000);
 465                        if (strcmp(hostname, ip) == 0)
 466                                hostname = "?";
 467                }
 468
 469                arp_disp(hostname, ip, type, flags, hwa, mask, dev);
 470        }
 471        if (option_mask32 & ARP_OPT_v)
 472                printf("Entries: %u\tSkipped: %u\tFound: %u\n",
 473                                entries, entries - shown, shown);
 474
 475        if (!shown) {
 476                if (hw_set || host || device[0])
 477                        printf("No match found in %u entries\n", entries);
 478        }
 479        if (ENABLE_FEATURE_CLEAN_UP) {
 480                free((char*)host);
 481                fclose(fp);
 482        }
 483        return 0;
 484}
 485
 486int arp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 487int arp_main(int argc UNUSED_PARAM, char **argv)
 488{
 489        const char *hw_type;
 490        const char *protocol;
 491        unsigned opts;
 492
 493        INIT_G();
 494
 495        xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sockfd);
 496
 497        ap = get_aftype(DFLT_AF);
 498        /* Defaults are always supported */
 499        //if (!ap)
 500        //      bb_error_msg_and_die("%s: %s not supported", DFLT_AF, "address family");
 501        hw = get_hwtype(DFLT_HW);
 502        //if (!hw)
 503        //      bb_error_msg_and_die("%s: %s not supported", DFLT_HW, "hardware type");
 504
 505        opts = getopt32(argv, "A:p:H:t:i:adnDsv", &protocol, &protocol,
 506                                 &hw_type, &hw_type, &device);
 507        argv += optind;
 508        if (opts & (ARP_OPT_A | ARP_OPT_p)) {
 509                ap = get_aftype(protocol);
 510                if (!ap)
 511                        bb_error_msg_and_die("%s: unknown %s", protocol, "address family");
 512        }
 513        if (opts & (ARP_OPT_H | ARP_OPT_t)) {
 514                hw = get_hwtype(hw_type);
 515                if (!hw)
 516                        bb_error_msg_and_die("%s: unknown %s", hw_type, "hardware type");
 517                hw_set = 1;
 518        }
 519        //if (opts & ARP_OPT_i)... -i
 520
 521        if (ap->af != AF_INET) {
 522                bb_error_msg_and_die("%s: kernel only supports 'inet'", ap->name);
 523        }
 524        if (hw->alen <= 0) {
 525                bb_error_msg_and_die("%s: %s without ARP support",
 526                                hw->name, "hardware type");
 527        }
 528
 529        /* Now see what we have to do here... */
 530        if (opts & (ARP_OPT_d | ARP_OPT_s)) {
 531                if (argv[0] == NULL)
 532                        bb_simple_error_msg_and_die("need host name");
 533                if (opts & ARP_OPT_s)
 534                        return arp_set(argv);
 535                return arp_del(argv);
 536        }
 537
 538        //if (opts & ARP_OPT_a) - default
 539        return arp_show(argv[0]);
 540}
 541