aboutsummaryrefslogtreecommitdiff
path: root/networking/libiproute
diff options
context:
space:
mode:
authorCurt Brune <curt@cumulusnetworks.com>2015-10-14 12:53:47 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2015-10-14 12:53:47 +0200
commit69934701fd1b18327b3a779cb292a728834b2d0d (patch)
treecdae967517ee981a7e7f07bddfec7047c1f51221 /networking/libiproute
parent7b85ec30b5941f0b90c48a990f2f6840aca87bce (diff)
downloadbusybox-69934701fd1b18327b3a779cb292a728834b2d0d.tar.gz
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.src8
-rw-r--r--networking/libiproute/ip_common.h2
-rw-r--r--networking/libiproute/ipneigh.c354
-rw-r--r--networking/libiproute/iproute.c26
-rw-r--r--networking/libiproute/utils.c22
-rw-r--r--networking/libiproute/utils.h2
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