[PATCH 1/1] networking: add 'ip neigh' command
Curt Brune
curt at cumulusnetworks.com
Fri Oct 9 15:22:29 UTC 2015
This patch ports the 'ip neigh' command, originally written by Alexey
Kuznetsov, <kuznet at 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.
Signed-off-by: Curt Brune <curt at cumulusnetworks.com>
---
networking/Config.src | 13 ++
networking/ip.c | 19 +-
networking/libiproute/Kbuild.src | 8 +
networking/libiproute/ip_common.h | 2 +-
networking/libiproute/ipneigh.c | 393 ++++++++++++++++++++++++++++++++++++++
5 files changed, 433 insertions(+), 2 deletions(-)
create mode 100644 networking/libiproute/ipneigh.c
diff --git a/networking/Config.src b/networking/Config.src
index 43ccbf3..8c7417f 100644
--- a/networking/Config.src
+++ b/networking/Config.src
@@ -552,10 +552,17 @@ config FEATURE_IP_RULE
default y
depends on IP
help
Add support for rule commands to "ip".
+config FEATURE_IP_NEIGH
+ bool "ip neighbor"
+ default y
+ depends on IP
+ help
+ Add support for neighbor commands to "ip".
+
config FEATURE_IP_SHORT_FORMS
bool "Support short forms of ip commands"
default y
depends on IP
help
@@ -563,10 +570,11 @@ config FEATURE_IP_SHORT_FORMS
ip addr -> ipaddr
ip link -> iplink
ip route -> iproute
ip tunnel -> iptunnel
ip rule -> iprule
+ ip neigh -> ipneigh
Say N unless you desparately need the short form of the ip
object commands.
config FEATURE_IP_RARE_PROTOCOLS
@@ -602,10 +610,15 @@ config IPTUNNEL
config IPRULE
bool
default y
depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_RULE
+config IPNEIGH
+ bool
+ default y
+ depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_NEIGH
+
config IPCALC
bool "ipcalc"
default y
help
ipcalc takes an IP address and netmask and calculates the
diff --git a/networking/ip.c b/networking/ip.c
index d35345c..ddfe74e 100644
--- a/networking/ip.c
+++ b/networking/ip.c
@@ -14,19 +14,21 @@
//usage: "[OPTIONS] {"
//usage: IF_FEATURE_IP_ADDRESS("address | ")
//usage: IF_FEATURE_IP_ROUTE("route | ")
//usage: IF_FEATURE_IP_LINK("link | ")
//usage: IF_FEATURE_IP_TUNNEL("tunnel | ")
+//usage: IF_FEATURE_IP_NEIGH("neigh | ")
//usage: IF_FEATURE_IP_RULE("rule")
//usage: "} {COMMAND}"
//usage:#define ip_full_usage "\n\n"
//usage: "ip [OPTIONS] OBJECT {COMMAND}\n"
//usage: "where OBJECT := {"
//usage: IF_FEATURE_IP_ADDRESS("address | ")
//usage: IF_FEATURE_IP_ROUTE("route | ")
//usage: IF_FEATURE_IP_LINK("link | ")
//usage: IF_FEATURE_IP_TUNNEL("tunnel | ")
+//usage: IF_FEATURE_IP_NEIGH("neigh | ")
//usage: IF_FEATURE_IP_RULE("rule")
//usage: "}\n"
//usage: "OPTIONS := { -f[amily] { inet | inet6 | link } | -o[neline] }"
//usage:
//usage:#define ipaddr_trivial_usage
@@ -78,21 +80,27 @@
//usage:#define iptunnel_full_usage "\n\n"
//usage: "iptunnel { add | change | del | show } [NAME]\n"
//usage: " [mode { ipip | gre | sit }] [remote ADDR] [local ADDR]\n"
//usage: " [[i|o]seq] [[i|o]key KEY] [[i|o]csum]\n"
//usage: " [ttl TTL] [tos TOS] [[no]pmtudisc] [dev PHYS_DEV]"
+//usage:
+//usage:#define ipneigh_trivial_usage
+//usage: "{ show | flush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]"
+//usage:#define ipneigh_full_usage "\n\n"
+//usage: "ipneigh { show | flush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]"
#include "libbb.h"
#include "libiproute/utils.h"
#include "libiproute/ip_common.h"
#if ENABLE_FEATURE_IP_ADDRESS \
|| ENABLE_FEATURE_IP_ROUTE \
|| ENABLE_FEATURE_IP_LINK \
|| ENABLE_FEATURE_IP_TUNNEL \
- || ENABLE_FEATURE_IP_RULE
+ || ENABLE_FEATURE_IP_RULE \
+ || ENABLE_FEATURE_IP_NEIGH
static int FAST_FUNC ip_print_help(char **argv UNUSED_PARAM)
{
bb_show_usage();
}
@@ -138,10 +146,17 @@ int iptunnel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int iptunnel_main(int argc UNUSED_PARAM, char **argv)
{
return ip_do(do_iptunnel, argv);
}
#endif
+#if ENABLE_FEATURE_IP_NEIGH
+int ipneigh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ipneigh_main(int argc UNUSED_PARAM, char **argv)
+{
+ return ip_do(do_ipneigh, argv);
+}
+#endif
int ip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int ip_main(int argc UNUSED_PARAM, char **argv)
{
@@ -151,20 +166,22 @@ int ip_main(int argc UNUSED_PARAM, char **argv)
IF_FEATURE_IP_ROUTE("r\0")
IF_FEATURE_IP_LINK("link\0")
IF_FEATURE_IP_TUNNEL("tunnel\0")
IF_FEATURE_IP_TUNNEL("tunl\0")
IF_FEATURE_IP_RULE("rule\0")
+ IF_FEATURE_IP_NEIGH("neigh\0")
;
static const ip_func_ptr_t ip_func_ptrs[] = {
ip_print_help,
IF_FEATURE_IP_ADDRESS(do_ipaddr,)
IF_FEATURE_IP_ROUTE(do_iproute,)
IF_FEATURE_IP_ROUTE(do_iproute,)
IF_FEATURE_IP_LINK(do_iplink,)
IF_FEATURE_IP_TUNNEL(do_iptunnel,)
IF_FEATURE_IP_TUNNEL(do_iptunnel,)
IF_FEATURE_IP_RULE(do_iprule,)
+ IF_FEATURE_IP_NEIGH(do_ipneigh,)
};
ip_func_ptr_t ip_func;
int key;
argv = ip_parse_common_args(argv + 1);
diff --git a/networking/libiproute/Kbuild.src b/networking/libiproute/Kbuild.src
index 7c78f3c..c20e2fe 100644
--- a/networking/libiproute/Kbuild.src
+++ b/networking/libiproute/Kbuild.src
@@ -62,5 +62,13 @@ lib-$(CONFIG_FEATURE_IP_TUNNEL) += \
lib-$(CONFIG_FEATURE_IP_RULE) += \
ip_parse_common_args.o \
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 30c7e59..40171be 100644
--- a/networking/libiproute/ip_common.h
+++ b/networking/libiproute/ip_common.h
@@ -22,11 +22,11 @@ int FAST_FUNC ipaddr_list_or_flush(char **argv, int flush);
//void FAST_FUNC ipneigh_reset_filter(void);
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);
//int FAST_FUNC do_multiaddr(char **argv);
//int FAST_FUNC do_multiroute(char **argv);
diff --git a/networking/libiproute/ipneigh.c b/networking/libiproute/ipneigh.c
new file mode 100644
index 0000000..68b129d
--- /dev/null
+++ b/networking/libiproute/ipneigh.c
@@ -0,0 +1,393 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet at ms2.inr.ac.ru>
+ *
+ * Ported to Busybox by: Curt Brune <curt at 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;
+
+static inline __u32 rta_getattr_u32(const struct rtattr *rta)
+{
+ return *(__u32 *)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 get_user_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 = sysconf(_SC_CLK_TCK);
+ return hz_internal;
+}
+
+static int nud_state_a2n(unsigned *state, 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"
+ ;
+ enum {
+ KW_permanent, KW_reachable, KW_noarp, KW_none,
+ KW_stale, KW_incomplete, KW_delay, KW_probe,
+ KW_failed,
+ };
+ int id = index_in_substrings(keywords, arg);
+
+ if (id == KW_permanent)
+ *state = NUD_PERMANENT;
+ else if (id == KW_reachable)
+ *state = NUD_REACHABLE;
+ else if (id == KW_noarp)
+ *state = NUD_NOARP;
+ else if (id == KW_none)
+ *state = NUD_NONE;
+ else if (id == KW_stale)
+ *state = NUD_STALE;
+ else if (id == KW_incomplete)
+ *state = NUD_INCOMPLETE;
+ else if (id == KW_delay)
+ *state = NUD_DELAY;
+ else if (id == KW_probe)
+ *state = NUD_PROBE;
+ else if (id == KW_failed)
+ *state = NUD_FAILED;
+ else {
+ return -1;
+ }
+ return 0;
+}
+
+#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",
+ 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\n", 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_user_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) {
+ __u32 p = rta_getattr_u32(tb[NDA_PROBES]);
+ printf(" probes %u", p);
+ }
+
+ if (r->ndm_state) {
+ int nud = r->ndm_state;
+ printf(" ");
+
+#define PRINT_FLAG(f) if (nud & NUD_##f) { \
+ nud &= ~NUD_##f; printf( #f "%s", nud ? "," : ""); }
+ 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
+ }
+ printf("\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 (nud_state_a2n(&state, *argv)) {
+ if (strcmp(*argv, "all") != 0) {
+ bb_error_msg_and_die(bb_msg_invalid_arg,
+ *argv, "nud state is bad");
+ }
+ state = ~0;
+ if (flush)
+ state &= ~NUD_NOARP;
+ }
+ 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("Cannot 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)
+ printf("Nothing to flush.\n");
+ else
+ printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
+ return 0;
+ }
+ round++;
+ if (flush_update() < 0)
+ exit(1);
+ 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\n", 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("Cannot send dump request");
+ }
+
+ if (xrtnl_dump_filter(&rth, print_neigh, NULL) < 0) {
+ bb_error_msg_and_die("Dump terminated\n");
+ }
+
+ 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);
+
+ /* "Standard" 'ip r a' treats 'a' as 'add', not 'append' */
+ /* It probably means that it is using "first match" rule */
+ 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);
+ default:
+ bb_error_msg_and_die("unknown command %s", *argv);
+ }
+
+ return 1;
+}
--
1.9.1
More information about the busybox
mailing list