Start 1.33.0 development cycle
[oweals/busybox.git] / networking / libiproute / ipneigh.c
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
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
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
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
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
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 */
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 */
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 }