source: MondoRescue/branches/3.3/mindi-busybox/networking/libiproute/ipneigh.c@ 3734

Last change on this file since 3734 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

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