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