busybox/networking/libiproute/ipneigh.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 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   6 *
   7 * Ported to Busybox by:  Curt Brune <curt@cumulusnetworks.com>
   8 */
   9#include "ip_common.h"  /* #include "libbb.h" is inside */
  10#include "common_bufsiz.h"
  11#include "rt_names.h"
  12#include "utils.h"
  13#include <linux/neighbour.h>
  14#include <net/if_arp.h>
  15
  16//static int xshow_stats = 3;
  17enum { xshow_stats = 3 };
  18
  19static inline uint32_t rta_getattr_u32(const struct rtattr *rta)
  20{
  21        return *(uint32_t *)RTA_DATA(rta);
  22}
  23
  24#ifndef RTAX_RTTVAR
  25#define RTAX_RTTVAR RTAX_HOPS
  26#endif
  27
  28
  29struct filter_t {
  30        int family;
  31        int index;
  32        int state;
  33        int unused_only;
  34        inet_prefix pfx;
  35        /* Misnomer. Does not mean "flushed N something" */
  36        /* More like "no_of_flush_commands_constructed_by_print_neigh()" */
  37        int flushed;
  38        /* Flush cmd buf. If !NULL, print_neigh() constructs flush commands in it */
  39        char *flushb;
  40        int flushp;
  41        int flushe;
  42        struct rtnl_handle *rth;
  43} FIX_ALIASING;
  44typedef struct filter_t filter_t;
  45
  46#define G_filter (*(filter_t*)bb_common_bufsiz1)
  47#define INIT_G() do { setup_common_bufsiz(); } while (0)
  48
  49static int flush_update(void)
  50{
  51        if (rtnl_send_check(G_filter.rth, G_filter.flushb, G_filter.flushp) < 0) {
  52                bb_simple_perror_msg("can't send flush request");
  53                return -1;
  54        }
  55        G_filter.flushp = 0;
  56        return 0;
  57}
  58
  59static unsigned nud_state_a2n(char *arg)
  60{
  61        static const char keywords[] ALIGN1 =
  62                /* "ip neigh show/flush" parameters: */
  63                "permanent\0" "reachable\0"   "noarp\0"  "none\0"
  64                "stale\0"     "incomplete\0"  "delay\0"  "probe\0"
  65                "failed\0"
  66                ;
  67        static uint8_t nuds[] ALIGN1 = {
  68                NUD_PERMANENT,NUD_REACHABLE, NUD_NOARP,NUD_NONE,
  69                NUD_STALE,    NUD_INCOMPLETE,NUD_DELAY,NUD_PROBE,
  70                NUD_FAILED
  71        };
  72        int id;
  73
  74        BUILD_BUG_ON(
  75                (NUD_PERMANENT|NUD_REACHABLE| NUD_NOARP|NUD_NONE|
  76                NUD_STALE|    NUD_INCOMPLETE|NUD_DELAY|NUD_PROBE|
  77                NUD_FAILED) > 0xff
  78        );
  79
  80        id = index_in_substrings(keywords, arg);
  81        if (id < 0)
  82                bb_error_msg_and_die(bb_msg_invalid_arg_to, arg, "nud state");
  83        return nuds[id];
  84}
  85
  86#ifndef NDA_RTA
  87#define NDA_RTA(r) \
  88        ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
  89#endif
  90
  91
  92static int FAST_FUNC print_neigh(const struct sockaddr_nl *who UNUSED_PARAM,
  93                                 struct nlmsghdr *n, void *arg UNUSED_PARAM)
  94{
  95        struct ndmsg *r = NLMSG_DATA(n);
  96        int len = n->nlmsg_len;
  97        struct rtattr *tb[NDA_MAX+1];
  98
  99        if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) {
 100                bb_error_msg_and_die("not RTM_NEWNEIGH: %08x %08x %08x",
 101                                     n->nlmsg_len, n->nlmsg_type,
 102                                     n->nlmsg_flags);
 103        }
 104        len -= NLMSG_LENGTH(sizeof(*r));
 105        if (len < 0) {
 106                bb_error_msg_and_die("BUG: wrong nlmsg len %d", len);
 107        }
 108
 109        if (G_filter.flushb && n->nlmsg_type != RTM_NEWNEIGH)
 110                return 0;
 111
 112        if (G_filter.family && G_filter.family != r->ndm_family)
 113                return 0;
 114        if (G_filter.index && G_filter.index != r->ndm_ifindex)
 115                return 0;
 116        if (!(G_filter.state&r->ndm_state)
 117         && !(r->ndm_flags & NTF_PROXY)
 118         && (r->ndm_state || !(G_filter.state & 0x100))
 119         && (r->ndm_family != AF_DECnet)
 120        ) {
 121                return 0;
 122        }
 123
 124        parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
 125
 126        if (tb[NDA_DST]) {
 127                if (G_filter.pfx.family) {
 128                        inet_prefix dst;
 129                        memset(&dst, 0, sizeof(dst));
 130                        dst.family = r->ndm_family;
 131                        memcpy(&dst.data, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
 132                        if (inet_addr_match(&dst, &G_filter.pfx, G_filter.pfx.bitlen))
 133                                return 0;
 134                }
 135        }
 136        if (G_filter.unused_only && tb[NDA_CACHEINFO]) {
 137                struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
 138                if (ci->ndm_refcnt)
 139                        return 0;
 140        }
 141
 142        if (G_filter.flushb) {
 143                struct nlmsghdr *fn;
 144                if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) {
 145                        if (flush_update())
 146                                return -1;
 147                }
 148                fn = (struct nlmsghdr*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
 149                memcpy(fn, n, n->nlmsg_len);
 150                fn->nlmsg_type = RTM_DELNEIGH;
 151                fn->nlmsg_flags = NLM_F_REQUEST;
 152                fn->nlmsg_seq = ++(G_filter.rth->seq);
 153                G_filter.flushp = (((char*)fn) + n->nlmsg_len) - G_filter.flushb;
 154                G_filter.flushed++;
 155                if (xshow_stats < 2)
 156                        return 0;
 157        }
 158
 159        if (tb[NDA_DST]) {
 160                printf("%s ",
 161                       format_host(r->ndm_family,
 162                                   RTA_PAYLOAD(tb[NDA_DST]),
 163                                   RTA_DATA(tb[NDA_DST]))
 164                );
 165        }
 166        if (!G_filter.index && r->ndm_ifindex)
 167                printf("dev %s ", ll_index_to_name(r->ndm_ifindex));
 168        if (tb[NDA_LLADDR]) {
 169                SPRINT_BUF(b1);
 170                printf("lladdr %s", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
 171                                                RTA_PAYLOAD(tb[NDA_LLADDR]),
 172                                                ARPHRD_ETHER,
 173                                                b1, sizeof(b1)));
 174        }
 175        if (r->ndm_flags & NTF_ROUTER) {
 176                printf(" router");
 177        }
 178        if (r->ndm_flags & NTF_PROXY) {
 179                printf(" proxy");
 180        }
 181        if (tb[NDA_CACHEINFO] && xshow_stats) {
 182                struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
 183                int hz = get_hz();
 184
 185                if (ci->ndm_refcnt)
 186                        printf(" ref %d", ci->ndm_refcnt);
 187                printf(" used %d/%d/%d", ci->ndm_used/hz,
 188                       ci->ndm_confirmed/hz, ci->ndm_updated/hz);
 189        }
 190
 191        if (tb[NDA_PROBES] && xshow_stats) {
 192                uint32_t p = rta_getattr_u32(tb[NDA_PROBES]);
 193                printf(" probes %u", p);
 194        }
 195
 196        /*if (r->ndm_state)*/ {
 197                int nud = r->ndm_state;
 198                char c = ' ';
 199#define PRINT_FLAG(f) \
 200                if (nud & NUD_##f) { \
 201                        printf("%c"#f, c); \
 202                        c = ','; \
 203                }
 204                PRINT_FLAG(INCOMPLETE);
 205                PRINT_FLAG(REACHABLE);
 206                PRINT_FLAG(STALE);
 207                PRINT_FLAG(DELAY);
 208                PRINT_FLAG(PROBE);
 209                PRINT_FLAG(FAILED);
 210                PRINT_FLAG(NOARP);
 211                PRINT_FLAG(PERMANENT);
 212#undef PRINT_FLAG
 213        }
 214        bb_putchar('\n');
 215
 216        return 0;
 217}
 218
 219static void ipneigh_reset_filter(void)
 220{
 221        memset(&G_filter, 0, sizeof(G_filter));
 222        G_filter.state = ~0;
 223}
 224
 225#define MAX_ROUNDS      10
 226/* Return value becomes exitcode. It's okay to not return at all */
 227static int FAST_FUNC ipneigh_list_or_flush(char **argv, int flush)
 228{
 229        static const char keywords[] ALIGN1 =
 230                /* "ip neigh show/flush" parameters: */
 231                "to\0" "dev\0"   "nud\0";
 232        enum {
 233                KW_to, KW_dev, KW_nud,
 234        };
 235        struct rtnl_handle rth;
 236        struct ndmsg ndm = { 0 };
 237        char *filter_dev = NULL;
 238        int state_given = 0;
 239        int arg;
 240
 241        ipneigh_reset_filter();
 242
 243        if (flush && !*argv)
 244                bb_error_msg_and_die(bb_msg_requires_arg, "\"ip neigh flush\"");
 245
 246        if (!G_filter.family)
 247                G_filter.family = preferred_family;
 248
 249        G_filter.state = (flush) ?
 250                ~(NUD_PERMANENT|NUD_NOARP) : 0xFF & ~NUD_NOARP;
 251
 252        while (*argv) {
 253                arg = index_in_substrings(keywords, *argv);
 254                if (arg == KW_dev) {
 255                        NEXT_ARG();
 256                        filter_dev = *argv;
 257                } else if (arg == KW_nud) {
 258                        unsigned state;
 259                        NEXT_ARG();
 260                        if (!state_given) {
 261                                state_given = 1;
 262                                G_filter.state = 0;
 263                        }
 264                        if (strcmp(*argv, "all") == 0) {
 265                                state = ~0;
 266                                if (flush)
 267                                        state &= ~NUD_NOARP;
 268                        } else {
 269                                state = nud_state_a2n(*argv);
 270                        }
 271                        if (state == 0)
 272                                state = 0x100;
 273                        G_filter.state |= state;
 274                } else {
 275                        if (arg == KW_to) {
 276                                NEXT_ARG();
 277                        }
 278                        get_prefix(&G_filter.pfx, *argv, G_filter.family);
 279                        if (G_filter.family == AF_UNSPEC)
 280                                G_filter.family = G_filter.pfx.family;
 281                }
 282                argv++;
 283        }
 284
 285        xrtnl_open(&rth);
 286        ll_init_map(&rth);
 287
 288        if (filter_dev)  {
 289                G_filter.index = xll_name_to_index(filter_dev);
 290                if (G_filter.index == 0) {
 291                        bb_error_msg_and_die("can't find device '%s'", filter_dev);
 292                }
 293        }
 294
 295        if (flush) {
 296                int round = 0;
 297                char flushb[4096-512];
 298                G_filter.flushb = flushb;
 299                G_filter.flushp = 0;
 300                G_filter.flushe = sizeof(flushb);
 301                G_filter.state &= ~NUD_FAILED;
 302                G_filter.rth = &rth;
 303
 304                while (round < MAX_ROUNDS) {
 305                        xrtnl_wilddump_request(&rth, G_filter.family, RTM_GETNEIGH);
 306                        G_filter.flushed = 0;
 307                        if (xrtnl_dump_filter(&rth, print_neigh, NULL) < 0) {
 308                                bb_simple_perror_msg_and_die("flush terminated");
 309                        }
 310                        if (G_filter.flushed == 0) {
 311                                if (round == 0)
 312                                        puts("Nothing to flush");
 313                                else
 314                                        printf("*** Flush is complete after %d round(s) ***\n", round);
 315                                return 0;
 316                        }
 317                        round++;
 318                        if (flush_update() < 0)
 319                                xfunc_die();
 320                        printf("\n*** Round %d, deleting %d entries ***\n", round, G_filter.flushed);
 321                }
 322                bb_error_msg_and_die("*** Flush not complete bailing out after %d rounds", MAX_ROUNDS);
 323        }
 324
 325        ndm.ndm_family = G_filter.family;
 326
 327        if (rtnl_dump_request(&rth, RTM_GETNEIGH, &ndm, sizeof(struct ndmsg)) < 0) {
 328                bb_simple_perror_msg_and_die("can't send dump request");
 329        }
 330
 331        if (xrtnl_dump_filter(&rth, print_neigh, NULL) < 0) {
 332                bb_simple_error_msg_and_die("dump terminated");
 333        }
 334
 335        return 0;
 336}
 337
 338/* Return value becomes exitcode. It's okay to not return at all */
 339int FAST_FUNC do_ipneigh(char **argv)
 340{
 341        static const char ip_neigh_commands[] ALIGN1 =
 342                /*0-1*/ "show\0"  "flush\0";
 343        int command_num;
 344
 345        INIT_G();
 346
 347        if (!*argv)
 348                return ipneigh_list_or_flush(argv, 0);
 349
 350        command_num = index_in_substrings(ip_neigh_commands, *argv);
 351        switch (command_num) {
 352                case 0: /* show */
 353                        return ipneigh_list_or_flush(argv + 1, 0);
 354                case 1: /* flush */
 355                        return ipneigh_list_or_flush(argv + 1, 1);
 356        }
 357        invarg_1_to_2(*argv, applet_name);
 358        return 1;
 359}
 360