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