diff options
author | Curt Brune <curt@cumulusnetworks.com> | 2015-10-14 13:53:47 +0300 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2015-10-14 13:53:47 +0300 |
commit | 69934701fd1b18327b3a779cb292a728834b2d0d (patch) | |
tree | cdae967517ee981a7e7f07bddfec7047c1f51221 /networking/libiproute | |
parent | 7b85ec30b5941f0b90c48a990f2f6840aca87bce (diff) |
networking: add 'ip neigh' command
This patch ports the 'ip neigh' command, originally written by Alexey
Kuznetsov, <kuznet@ms2.inr.ac.ru>, to busybox.
The base of the port is the version of iproute that shipped with
Debian Squeeze, taken from:
http://http.debian.net/debian/pool/main/i/iproute/iproute_20100519.orig.tar.gz
This patch has actively been used by the Open Network Install
Environment (ONIE) project for over 3 years without incident.
function old new delta
print_neigh - 933 +933
ipneigh_list_or_flush - 742 +742
get_hz - 109 +109
do_ipneigh - 62 +62
do_iproute 2112 2153 +41
packed_usage 30647 30667 +20
ipneigh_main - 14 +14
static.ip_neigh_commands - 12 +12
static.nuds - 9 +9
static.ip_func_ptrs 32 36 +4
print_route 1858 1727 -131
------------------------------------------------------------------------------
(add/remove: 8/0 grow/shrink: 3/1 up/down: 1946/-131) Total: 1815 bytes
Signed-off-by: Curt Brune <curt@cumulusnetworks.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'networking/libiproute')
-rw-r--r-- | networking/libiproute/Kbuild.src | 8 | ||||
-rw-r--r-- | networking/libiproute/ip_common.h | 2 | ||||
-rw-r--r-- | networking/libiproute/ipneigh.c | 354 | ||||
-rw-r--r-- | networking/libiproute/iproute.c | 26 | ||||
-rw-r--r-- | networking/libiproute/utils.c | 22 | ||||
-rw-r--r-- | networking/libiproute/utils.h | 2 |
6 files changed, 389 insertions, 25 deletions
diff --git a/networking/libiproute/Kbuild.src b/networking/libiproute/Kbuild.src index 7c78f3c6a..c20e2fee8 100644 --- a/networking/libiproute/Kbuild.src +++ b/networking/libiproute/Kbuild.src @@ -64,3 +64,11 @@ lib-$(CONFIG_FEATURE_IP_RULE) += \ iprule.o \ rt_names.o \ utils.o + +lib-$(CONFIG_FEATURE_IP_NEIGH) += \ + ip_parse_common_args.o \ + ipneigh.o \ + libnetlink.o \ + ll_map.o \ + rt_names.o \ + utils.o diff --git a/networking/libiproute/ip_common.h b/networking/libiproute/ip_common.h index 30c7e595b..40171bed9 100644 --- a/networking/libiproute/ip_common.h +++ b/networking/libiproute/ip_common.h @@ -24,7 +24,7 @@ int FAST_FUNC ipaddr_list_or_flush(char **argv, int flush); int FAST_FUNC do_ipaddr(char **argv); int FAST_FUNC do_iproute(char **argv); int FAST_FUNC do_iprule(char **argv); -//int FAST_FUNC do_ipneigh(char **argv); +int FAST_FUNC do_ipneigh(char **argv); int FAST_FUNC do_iptunnel(char **argv); int FAST_FUNC do_iplink(char **argv); //int FAST_FUNC do_ipmonitor(char **argv); diff --git a/networking/libiproute/ipneigh.c b/networking/libiproute/ipneigh.c new file mode 100644 index 000000000..03a15d845 --- /dev/null +++ b/networking/libiproute/ipneigh.c @@ -0,0 +1,354 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + * Ported to Busybox by: Curt Brune <curt@cumulusnetworks.com> + */ + +#include "ip_common.h" /* #include "libbb.h" is inside */ +#include "rt_names.h" +#include "utils.h" +#include <linux/neighbour.h> +#include <net/if_arp.h> + +//static int xshow_stats = 3; +enum { xshow_stats = 3 }; + +static inline uint32_t rta_getattr_u32(const struct rtattr *rta) +{ + return *(uint32_t *)RTA_DATA(rta); +} + +#ifndef RTAX_RTTVAR +#define RTAX_RTTVAR RTAX_HOPS +#endif + + +struct filter_t { + int family; + int index; + int state; + int unused_only; + inet_prefix pfx; + int flushed; + char *flushb; + int flushp; + int flushe; + struct rtnl_handle *rth; +} FIX_ALIASING; +typedef struct filter_t filter_t; + +#define G_filter (*(filter_t*)&bb_common_bufsiz1) + +static int flush_update(void) +{ + if (rtnl_send(G_filter.rth, G_filter.flushb, G_filter.flushp) < 0) { + bb_perror_msg("can't send flush request"); + return -1; + } + G_filter.flushp = 0; + return 0; +} + +static unsigned nud_state_a2n(char *arg) +{ + static const char keywords[] ALIGN1 = + /* "ip neigh show/flush" parameters: */ + "permanent\0" "reachable\0" "noarp\0" "none\0" + "stale\0" "incomplete\0" "delay\0" "probe\0" + "failed\0" + ; + static uint8_t nuds[] = { + NUD_PERMANENT,NUD_REACHABLE, NUD_NOARP,NUD_NONE, + NUD_STALE, NUD_INCOMPLETE,NUD_DELAY,NUD_PROBE, + NUD_FAILED + }; + int id; + + BUILD_BUG_ON( + (NUD_PERMANENT|NUD_REACHABLE| NUD_NOARP|NUD_NONE| + NUD_STALE| NUD_INCOMPLETE|NUD_DELAY|NUD_PROBE| + NUD_FAILED) > 0xff + ); + + id = index_in_substrings(keywords, arg); + if (id < 0) + bb_error_msg_and_die(bb_msg_invalid_arg, arg, "nud state"); + return nuds[id]; +} + +#ifndef NDA_RTA +#define NDA_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) +#endif + + +static int FAST_FUNC print_neigh(const struct sockaddr_nl *who UNUSED_PARAM, + struct nlmsghdr *n, void *arg UNUSED_PARAM) +{ + struct ndmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[NDA_MAX+1]; + char abuf[256]; + + if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) { + bb_error_msg_and_die("not RTM_NEWNEIGH: %08x %08x %08x", + n->nlmsg_len, n->nlmsg_type, + n->nlmsg_flags); + } + len -= NLMSG_LENGTH(sizeof(*r)); + if (len < 0) { + bb_error_msg_and_die("BUG: wrong nlmsg len %d", len); + } + + if (G_filter.flushb && n->nlmsg_type != RTM_NEWNEIGH) + return 0; + + if (G_filter.family && G_filter.family != r->ndm_family) + return 0; + if (G_filter.index && G_filter.index != r->ndm_ifindex) + return 0; + if (!(G_filter.state&r->ndm_state) && + !(r->ndm_flags & NTF_PROXY) && + (r->ndm_state || !(G_filter.state & 0x100)) && + (r->ndm_family != AF_DECnet)) + return 0; + + parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + + if (tb[NDA_DST]) { + if (G_filter.pfx.family) { + inet_prefix dst; + memset(&dst, 0, sizeof(dst)); + dst.family = r->ndm_family; + memcpy(&dst.data, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST])); + if (inet_addr_match(&dst, &G_filter.pfx, G_filter.pfx.bitlen)) + return 0; + } + } + if (G_filter.unused_only && tb[NDA_CACHEINFO]) { + struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); + if (ci->ndm_refcnt) + return 0; + } + + if (G_filter.flushb) { + struct nlmsghdr *fn; + if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) { + if (flush_update()) + return -1; + } + fn = (struct nlmsghdr*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp)); + memcpy(fn, n, n->nlmsg_len); + fn->nlmsg_type = RTM_DELNEIGH; + fn->nlmsg_flags = NLM_F_REQUEST; + fn->nlmsg_seq = ++(G_filter.rth->seq); + G_filter.flushp = (((char*)fn) + n->nlmsg_len) - G_filter.flushb; + G_filter.flushed++; + if (xshow_stats < 2) + return 0; + } + + if (tb[NDA_DST]) { + printf("%s ", + format_host(r->ndm_family, + RTA_PAYLOAD(tb[NDA_DST]), + RTA_DATA(tb[NDA_DST]), + abuf, sizeof(abuf))); + } + if (!G_filter.index && r->ndm_ifindex) + printf("dev %s ", ll_index_to_name(r->ndm_ifindex)); + if (tb[NDA_LLADDR]) { + SPRINT_BUF(b1); + printf("lladdr %s", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]), + RTA_PAYLOAD(tb[NDA_LLADDR]), + ARPHRD_ETHER, + b1, sizeof(b1))); + } + if (r->ndm_flags & NTF_ROUTER) { + printf(" router"); + } + if (r->ndm_flags & NTF_PROXY) { + printf(" proxy"); + } + if (tb[NDA_CACHEINFO] && xshow_stats) { + struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); + int hz = get_hz(); + + if (ci->ndm_refcnt) + printf(" ref %d", ci->ndm_refcnt); + printf(" used %d/%d/%d", ci->ndm_used/hz, + ci->ndm_confirmed/hz, ci->ndm_updated/hz); + } + + if (tb[NDA_PROBES] && xshow_stats) { + uint32_t p = rta_getattr_u32(tb[NDA_PROBES]); + printf(" probes %u", p); + } + + /*if (r->ndm_state)*/ { + int nud = r->ndm_state; + char c = ' '; +#define PRINT_FLAG(f) \ + if (nud & NUD_##f) { \ + printf("%c"#f, c); \ + c = ','; \ + } + PRINT_FLAG(INCOMPLETE); + PRINT_FLAG(REACHABLE); + PRINT_FLAG(STALE); + PRINT_FLAG(DELAY); + PRINT_FLAG(PROBE); + PRINT_FLAG(FAILED); + PRINT_FLAG(NOARP); + PRINT_FLAG(PERMANENT); +#undef PRINT_FLAG + } + bb_putchar('\n'); + + return 0; +} + +static void ipneigh_reset_filter(void) +{ + memset(&G_filter, 0, sizeof(G_filter)); + G_filter.state = ~0; +} + +#define MAX_ROUNDS 10 +/* Return value becomes exitcode. It's okay to not return at all */ +static int FAST_FUNC ipneigh_list_or_flush(char **argv, int flush) +{ + static const char keywords[] ALIGN1 = + /* "ip neigh show/flush" parameters: */ + "to\0" "dev\0" "nud\0"; + enum { + KW_to, KW_dev, KW_nud, + }; + struct rtnl_handle rth; + struct ndmsg ndm = { 0 }; + char *filter_dev = NULL; + int state_given = 0; + int arg; + + ipneigh_reset_filter(); + + if (flush && !*argv) + bb_error_msg_and_die(bb_msg_requires_arg, "\"ip neigh flush\""); + + if (!G_filter.family) + G_filter.family = preferred_family; + + G_filter.state = (flush) ? + ~(NUD_PERMANENT|NUD_NOARP) : 0xFF & ~NUD_NOARP; + + while (*argv) { + arg = index_in_substrings(keywords, *argv); + if (arg == KW_dev) { + NEXT_ARG(); + filter_dev = *argv; + } else if (arg == KW_nud) { + unsigned state; + NEXT_ARG(); + if (!state_given) { + state_given = 1; + G_filter.state = 0; + } + if (strcmp(*argv, "all") == 0) { + state = ~0; + if (flush) + state &= ~NUD_NOARP; + } else { + state = nud_state_a2n(*argv); + } + if (state == 0) + state = 0x100; + G_filter.state |= state; + } else { + if (arg == KW_to) { + NEXT_ARG(); + } + get_prefix(&G_filter.pfx, *argv, G_filter.family); + if (G_filter.family == AF_UNSPEC) + G_filter.family = G_filter.pfx.family; + } + argv++; + } + + xrtnl_open(&rth); + ll_init_map(&rth); + + if (filter_dev) { + if ((G_filter.index = xll_name_to_index(filter_dev)) == 0) { + bb_error_msg_and_die(bb_msg_invalid_arg, + filter_dev, "Cannot find device"); + } + } + + if (flush) { + int round = 0; + char flushb[4096-512]; + G_filter.flushb = flushb; + G_filter.flushp = 0; + G_filter.flushe = sizeof(flushb); + G_filter.state &= ~NUD_FAILED; + G_filter.rth = &rth; + + while (round < MAX_ROUNDS) { + if (xrtnl_wilddump_request(&rth, G_filter.family, RTM_GETNEIGH) < 0) { + bb_perror_msg_and_die("can't send dump request"); + } + G_filter.flushed = 0; + if (xrtnl_dump_filter(&rth, print_neigh, NULL) < 0) { + bb_perror_msg_and_die("flush terminated"); + } + if (G_filter.flushed == 0) { + if (round == 0) + puts("Nothing to flush"); + else + printf("*** Flush is complete after %d round(s) ***\n", round); + return 0; + } + round++; + if (flush_update() < 0) + xfunc_die(); + printf("\n*** Round %d, deleting %d entries ***\n", round, G_filter.flushed); + } + bb_error_msg_and_die("*** Flush not complete bailing out after %d rounds", MAX_ROUNDS); + } + + ndm.ndm_family = G_filter.family; + + if (rtnl_dump_request(&rth, RTM_GETNEIGH, &ndm, sizeof(struct ndmsg)) < 0) { + bb_perror_msg_and_die("can't send dump request"); + } + + if (xrtnl_dump_filter(&rth, print_neigh, NULL) < 0) { + bb_error_msg_and_die("dump terminated"); + } + + return 0; +} + +/* Return value becomes exitcode. It's okay to not return at all */ +int FAST_FUNC do_ipneigh(char **argv) +{ + static const char ip_neigh_commands[] ALIGN1 = + /*0-1*/ "show\0" "flush\0"; + int command_num; + + if (!*argv) + return ipneigh_list_or_flush(argv, 0); + + command_num = index_in_substrings(ip_neigh_commands, *argv); + switch (command_num) { + case 0: /* show */ + return ipneigh_list_or_flush(argv + 1, 0); + case 1: /* flush */ + return ipneigh_list_or_flush(argv + 1, 1); + } + invarg(*argv, applet_name); + return 1; +} diff --git a/networking/libiproute/iproute.c b/networking/libiproute/iproute.c index 6ecd5f719..f7209b126 100644 --- a/networking/libiproute/iproute.c +++ b/networking/libiproute/iproute.c @@ -55,28 +55,6 @@ static int flush_update(void) return 0; } -static unsigned get_hz(void) -{ - static unsigned hz_internal; - FILE *fp; - - if (hz_internal) - return hz_internal; - - fp = fopen_for_read("/proc/net/psched"); - if (fp) { - unsigned nom, denom; - - if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2) - if (nom == 1000000) - hz_internal = denom; - fclose(fp); - } - if (!hz_internal) - hz_internal = bb_clk_tck(); - return hz_internal; -} - static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM, struct nlmsghdr *n, void *arg UNUSED_PARAM) { @@ -217,7 +195,7 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM, if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) { if (flush_update()) - bb_error_msg_and_die("flush"); + xfunc_die(); } fn = (void*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp)); memcpy(fn, n, n->nlmsg_len); @@ -954,7 +932,7 @@ int FAST_FUNC do_iproute(char **argv) case 11: /* flush */ return iproute_list_or_flush(argv+1, 1); default: - bb_error_msg_and_die("unknown command %s", *argv); + invarg(*argv, applet_name); } return iproute_modify(cmd, flags, argv+1); diff --git a/networking/libiproute/utils.c b/networking/libiproute/utils.c index d0fe30605..37b5311f0 100644 --- a/networking/libiproute/utils.c +++ b/networking/libiproute/utils.c @@ -13,6 +13,28 @@ #include "utils.h" #include "inet_common.h" +unsigned get_hz(void) +{ + static unsigned hz_internal; + FILE *fp; + + if (hz_internal) + return hz_internal; + + fp = fopen_for_read("/proc/net/psched"); + if (fp) { + unsigned nom, denom; + + if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2) + if (nom == 1000000) + hz_internal = denom; + fclose(fp); + } + if (!hz_internal) + hz_internal = bb_clk_tck(); + return hz_internal; +} + unsigned get_unsigned(char *arg, const char *errmsg) { unsigned long res; diff --git a/networking/libiproute/utils.h b/networking/libiproute/utils.h index 5fb4a862c..cd15b706b 100644 --- a/networking/libiproute/utils.h +++ b/networking/libiproute/utils.h @@ -85,6 +85,8 @@ int dnet_pton(int af, const char *src, void *addr); const char *ipx_ntop(int af, const void *addr, char *str, size_t len); int ipx_pton(int af, const char *src, void *addr); +unsigned get_hz(void); + POP_SAVED_FUNCTION_VISIBILITY #endif |