[git commit] networking: add 'ip neigh' command

Denys Vlasenko vda.linux at googlemail.com
Wed Oct 14 10:53:47 UTC 2015


commit: http://git.busybox.net/busybox/commit/?id=69934701fd1b18327b3a779cb292a728834b2d0d
branch: http://git.busybox.net/busybox/commit/?id=refs/heads/master

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.

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 at cumulusnetworks.com>
Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 networking/Config.src             |   13 ++
 networking/ip.c                   |   19 ++-
 networking/libiproute/Kbuild.src  |    8 +
 networking/libiproute/ip_common.h |    2 +-
 networking/libiproute/ipneigh.c   |  354 +++++++++++++++++++++++++++++++++++++
 networking/libiproute/iproute.c   |   26 +---
 networking/libiproute/utils.c     |   22 +++
 networking/libiproute/utils.h     |    2 +
 8 files changed, 420 insertions(+), 26 deletions(-)

diff --git a/networking/Config.src b/networking/Config.src
index 43ccbf3..8c7417f 100644
--- a/networking/Config.src
+++ b/networking/Config.src
@@ -554,6 +554,13 @@ config FEATURE_IP_RULE
 	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
@@ -565,6 +572,7 @@ config FEATURE_IP_SHORT_FORMS
 	  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.
@@ -604,6 +612,11 @@ config IPRULE
 	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
diff --git a/networking/ip.c b/networking/ip.c
index d35345c..ddfe74e 100644
--- a/networking/ip.c
+++ b/networking/ip.c
@@ -16,6 +16,7 @@
 //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"
@@ -25,6 +26,7 @@
 //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] }"
@@ -80,6 +82,11 @@
 //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"
 
@@ -90,7 +97,8 @@
  || 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)
 {
@@ -140,6 +148,13 @@ 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;
@@ -153,6 +168,7 @@ int ip_main(int argc UNUSED_PARAM, char **argv)
 		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,
@@ -163,6 +179,7 @@ int ip_main(int argc UNUSED_PARAM, char **argv)
 		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;
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
@@ -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 30c7e59..40171be 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 0000000..03a15d8
--- /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 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;
+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 6ecd5f7..f7209b1 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 d0fe306..37b5311 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 5fb4a86..cd15b70 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


More information about the busybox-cvs mailing list