e8ab29121bfd5d61304ce78a9c8ca6660a07a28a
[iproute2.git] / ip / ipneigh.c
1 /*
2  * ipneigh.c            "ip neigh".
3  *
4  *              This program is free software; you can redistribute it and/or
5  *              modify it under the terms of the GNU General Public License
6  *              as published by the Free Software Foundation; either version
7  *              2 of the License, or (at your option) any later version.
8  *
9  * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10  *
11  *
12  * Changes:
13  *
14  * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
15  */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <syslog.h>
21 #include <fcntl.h>
22 #include <string.h>
23 #include <sys/time.h>
24 #include <net/if.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <netinet/ip.h>
28
29 #include "rt_names.h"
30 #include "utils.h"
31 #include "ip_common.h"
32
33 #define NUD_VALID       (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY)
34
35 static struct
36 {
37         int family;
38         int index;
39         int state;
40         int unused_only;
41         inet_prefix pfx;
42         int flushed;
43         char *flushb;
44         int flushp;
45         int flushe;
46         struct rtnl_handle *rth;
47 } filter;
48
49 static void usage(void) __attribute__((noreturn));
50
51 static void usage(void)
52 {
53         fprintf(stderr, "Usage: ip neigh { add | del | change | replace } { ADDR [ lladdr LLADDR ]\n"
54                         "          [ nud { permanent | noarp | stale | reachable } ]\n"
55                         "          | proxy ADDR } [ dev DEV ]\n");
56         fprintf(stderr, "       ip neigh {show|flush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n");
57         exit(-1);
58 }
59
60 int nud_state_a2n(unsigned *state, char *arg)
61 {
62         if (matches(arg, "permanent") == 0)
63                 *state = NUD_PERMANENT;
64         else if (matches(arg, "reachable") == 0)
65                 *state = NUD_REACHABLE;
66         else if (strcmp(arg, "noarp") == 0)
67                 *state = NUD_NOARP;
68         else if (strcmp(arg, "none") == 0)
69                 *state = NUD_NONE;
70         else if (strcmp(arg, "stale") == 0)
71                 *state = NUD_STALE;
72         else if (strcmp(arg, "incomplete") == 0)
73                 *state = NUD_INCOMPLETE;
74         else if (strcmp(arg, "delay") == 0)
75                 *state = NUD_DELAY;
76         else if (strcmp(arg, "probe") == 0)
77                 *state = NUD_PROBE;
78         else if (matches(arg, "failed") == 0)
79                 *state = NUD_FAILED;
80         else {
81                 if (get_unsigned(state, arg, 0))
82                         return -1;
83                 if (*state>=0x100 || (*state&((*state)-1)))
84                         return -1;
85         }
86         return 0;
87 }
88
89 static int flush_update(void)
90 {
91         if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
92                 perror("Failed to send flush request\n");
93                 return -1;
94         }
95         filter.flushp = 0;
96         return 0;
97 }
98
99
100 static int ipneigh_modify(int cmd, int flags, int argc, char **argv)
101 {
102         struct rtnl_handle rth;
103         struct {
104                 struct nlmsghdr         n;
105                 struct ndmsg            ndm;
106                 char                    buf[256];
107         } req;
108         char  *d = NULL;
109         int dst_ok = 0;
110         int lladdr_ok = 0;
111         char * lla = NULL;
112         inet_prefix dst;
113
114         memset(&req, 0, sizeof(req));
115
116         req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
117         req.n.nlmsg_flags = NLM_F_REQUEST|flags;
118         req.n.nlmsg_type = cmd;
119         req.ndm.ndm_family = preferred_family;
120         req.ndm.ndm_state = NUD_PERMANENT;
121
122         while (argc > 0) {
123                 if (matches(*argv, "lladdr") == 0) {
124                         NEXT_ARG();
125                         if (lladdr_ok)
126                                 duparg("lladdr", *argv);
127                         lla = *argv;
128                         lladdr_ok = 1;
129                 } else if (strcmp(*argv, "nud") == 0) {
130                         unsigned state;
131                         NEXT_ARG();
132                         if (nud_state_a2n(&state, *argv))
133                                 invarg("nud state is bad", *argv);
134                         req.ndm.ndm_state = state;
135                 } else if (matches(*argv, "proxy") == 0) {
136                         NEXT_ARG();
137                         if (matches(*argv, "help") == 0)
138                                 usage();
139                         if (dst_ok)
140                                 duparg("address", *argv);
141                         get_addr(&dst, *argv, preferred_family);
142                         dst_ok = 1;
143                         req.ndm.ndm_flags |= NTF_PROXY;
144                 } else if (strcmp(*argv, "dev") == 0) {
145                         NEXT_ARG();
146                         d = *argv;
147                 } else {
148                         if (strcmp(*argv, "to") == 0) {
149                                 NEXT_ARG();
150                         }
151                         if (matches(*argv, "help") == 0) {
152                                 NEXT_ARG();
153                         }
154                         if (dst_ok)
155                                 duparg2("to", *argv);
156                         get_addr(&dst, *argv, preferred_family);
157                         dst_ok = 1;
158                 }
159                 argc--; argv++;
160         }
161         if (d == NULL || !dst_ok || dst.family == AF_UNSPEC) {
162                 fprintf(stderr, "Device and destination are required arguments.\n");
163                 exit(-1);
164         }
165         req.ndm.ndm_family = dst.family;
166         addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen);
167
168         if (lla && strcmp(lla, "null")) {
169                 __u8 llabuf[16];
170                 int l;
171
172                 l = ll_addr_a2n(llabuf, sizeof(llabuf), lla);
173                 addattr_l(&req.n, sizeof(req), NDA_LLADDR, llabuf, l);
174         }
175
176         if (rtnl_open(&rth, 0) < 0)
177                 exit(1);
178
179         ll_init_map(&rth);
180
181         if ((req.ndm.ndm_ifindex = ll_name_to_index(d)) == 0) {
182                 fprintf(stderr, "Cannot find device \"%s\"\n", d);
183                 return -1;
184         }
185
186         if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
187                 exit(2);
188
189         exit(0);
190 }
191
192
193 int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
194 {
195         FILE *fp = (FILE*)arg;
196         struct ndmsg *r = NLMSG_DATA(n);
197         int len = n->nlmsg_len;
198         struct rtattr * tb[NDA_MAX+1];
199         char abuf[256];
200
201         if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) {
202                 fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n",
203                         n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
204                 
205                 return 0;
206         }
207         len -= NLMSG_LENGTH(sizeof(*r));
208         if (len < 0) {
209                 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
210                 return -1;
211         }
212
213         if (filter.flushb && n->nlmsg_type != RTM_NEWNEIGH)
214                 return 0;
215
216         if (filter.family && filter.family != r->ndm_family)
217                 return 0;
218         if (filter.index && filter.index != r->ndm_ifindex)
219                 return 0;
220         if (!(filter.state&r->ndm_state) &&
221             (r->ndm_state || !(filter.state&0x100)) &&
222              (r->ndm_family != AF_DECnet))
223                 return 0;
224
225         parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
226
227         if (tb[NDA_DST]) {
228                 if (filter.pfx.family) {
229                         inet_prefix dst;
230                         memset(&dst, 0, sizeof(dst));
231                         dst.family = r->ndm_family;
232                         memcpy(&dst.data, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
233                         if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
234                                 return 0;
235                 }
236         }
237         if (filter.unused_only && tb[NDA_CACHEINFO]) {
238                 struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
239                 if (ci->ndm_refcnt)
240                         return 0;
241         }
242
243         if (filter.flushb) {
244                 struct nlmsghdr *fn;
245                 if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
246                         if (flush_update())
247                                 return -1;
248                 }
249                 fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
250                 memcpy(fn, n, n->nlmsg_len);
251                 fn->nlmsg_type = RTM_DELNEIGH;
252                 fn->nlmsg_flags = NLM_F_REQUEST;
253                 fn->nlmsg_seq = ++filter.rth->seq;
254                 filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
255                 filter.flushed++;
256                 if (show_stats < 2)
257                         return 0;
258         }
259
260         if (tb[NDA_DST]) {
261                 fprintf(fp, "%s ", 
262                         format_host(r->ndm_family,
263                                     RTA_PAYLOAD(tb[NDA_DST]),
264                                     RTA_DATA(tb[NDA_DST]),
265                                     abuf, sizeof(abuf)));
266         }
267         if (!filter.index && r->ndm_ifindex)
268                 fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex));
269         if (tb[NDA_LLADDR]) {
270                 SPRINT_BUF(b1);
271                 fprintf(fp, "lladdr %s", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
272                                               RTA_PAYLOAD(tb[NDA_LLADDR]),
273                                               ll_index_to_type(r->ndm_ifindex),
274                                               b1, sizeof(b1)));
275         }
276         if (r->ndm_flags & NTF_ROUTER) {
277                 fprintf(fp, " router");
278         }
279         if (tb[NDA_CACHEINFO] && show_stats) {
280                 static int hz;
281                 struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
282                 if (!hz)
283                         hz = get_hz();
284                 if (ci->ndm_refcnt)
285                         printf(" ref %d", ci->ndm_refcnt);
286                 fprintf(fp, " used %d/%d/%d", ci->ndm_used/hz,
287                        ci->ndm_confirmed/hz, ci->ndm_updated/hz);
288         }
289
290 #ifdef NDA_PROBES
291         if (tb[NDA_PROBES] && show_stats) {
292                 __u32 p = *(__u32 *) RTA_DATA(tb[NDA_PROBES]);
293                 fprintf(fp, " probes %u", p);
294         }
295 #endif
296
297         if (r->ndm_state) {
298                 int nud = r->ndm_state;
299                 fprintf(fp, " ");
300
301 #define PRINT_FLAG(f) if (nud & NUD_##f) { \
302         nud &= ~NUD_##f; fprintf(fp, #f "%s", nud ? "," : ""); }
303                 PRINT_FLAG(INCOMPLETE);
304                 PRINT_FLAG(REACHABLE);
305                 PRINT_FLAG(STALE);
306                 PRINT_FLAG(DELAY);
307                 PRINT_FLAG(PROBE);
308                 PRINT_FLAG(FAILED);
309                 PRINT_FLAG(NOARP);
310                 PRINT_FLAG(PERMANENT);
311 #undef PRINT_FLAG
312         }
313         fprintf(fp, "\n");
314
315         fflush(fp);
316         return 0;
317 }
318
319 void ipneigh_reset_filter()
320 {
321         memset(&filter, 0, sizeof(filter));
322         filter.state = ~0;
323 }
324
325 int do_show_or_flush(int argc, char **argv, int flush)
326 {
327         char *filter_dev = NULL;
328         struct rtnl_handle rth;
329         int state_given = 0;
330
331         ipneigh_reset_filter();
332
333         if (!filter.family)
334                 filter.family = preferred_family;
335
336         if (flush) {
337                 if (argc <= 0) {
338                         fprintf(stderr, "Flush requires arguments.\n");
339                         return -1;
340                 }
341                 filter.state = ~(NUD_PERMANENT|NUD_NOARP);
342         } else
343                 filter.state = 0xFF & ~NUD_NOARP;
344
345         while (argc > 0) {
346                 if (strcmp(*argv, "dev") == 0) {
347                         NEXT_ARG();
348                         if (filter_dev)
349                                 duparg("dev", *argv);
350                         filter_dev = *argv;
351                 } else if (strcmp(*argv, "unused") == 0) {
352                         filter.unused_only = 1;
353                 } else if (strcmp(*argv, "nud") == 0) {
354                         unsigned state;
355                         NEXT_ARG();
356                         if (!state_given) {
357                                 state_given = 1;
358                                 filter.state = 0;
359                         }
360                         if (nud_state_a2n(&state, *argv)) {
361                                 if (strcmp(*argv, "all") != 0)
362                                         invarg("nud state is bad", *argv);
363                                 state = ~0;
364                                 if (flush)
365                                         state &= ~NUD_NOARP;
366                         }
367                         if (state == 0)
368                                 state = 0x100;
369                         filter.state |= state;
370                 } else {
371                         if (strcmp(*argv, "to") == 0) {
372                                 NEXT_ARG();
373                         }
374                         if (matches(*argv, "help") == 0)
375                                 usage();
376                         get_prefix(&filter.pfx, *argv, filter.family);
377                         if (filter.family == AF_UNSPEC)
378                                 filter.family = filter.pfx.family;
379                 }
380                 argc--; argv++;
381         }
382
383         if (rtnl_open(&rth, 0) < 0)
384                 exit(1);
385
386         ll_init_map(&rth);
387
388         if (filter_dev) {
389                 if ((filter.index = ll_name_to_index(filter_dev)) == 0) {
390                         fprintf(stderr, "Cannot find device \"%s\"\n", filter_dev);
391                         return -1;
392                 }
393         }
394
395         if (flush) {
396                 int round = 0;
397                 char flushb[4096-512];
398
399                 filter.flushb = flushb;
400                 filter.flushp = 0;
401                 filter.flushe = sizeof(flushb);
402                 filter.rth = &rth;
403                 filter.state &= ~NUD_FAILED;
404
405                 for (;;) {
406                         if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) {
407                                 perror("Cannot send dump request");
408                                 exit(1);
409                         }
410                         filter.flushed = 0;
411                         if (rtnl_dump_filter(&rth, print_neigh, stdout, NULL, NULL) < 0) {
412                                 fprintf(stderr, "Flush terminated\n");
413                                 exit(1);
414                         }
415                         if (filter.flushed == 0) {
416                                 if (round == 0) {
417                                         fprintf(stderr, "Nothing to flush.\n");
418                                 } else if (show_stats)
419                                         printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
420                                 fflush(stdout);
421                                 return 0;
422                         }
423                         round++;
424                         if (flush_update() < 0)
425                                 exit(1);
426                         if (show_stats) {
427                                 printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed);
428                                 fflush(stdout);
429                         }
430                 }
431         }
432
433         if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) {
434                 perror("Cannot send dump request");
435                 exit(1);
436         }
437
438         if (rtnl_dump_filter(&rth, print_neigh, stdout, NULL, NULL) < 0) {
439                 fprintf(stderr, "Dump terminated\n");
440                 exit(1);
441         }
442
443         return 0;
444 }
445
446 int do_ipneigh(int argc, char **argv)
447 {
448         if (argc > 0) {
449                 if (matches(*argv, "add") == 0)
450                         return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
451                 if (matches(*argv, "change") == 0 ||
452                     strcmp(*argv, "chg") == 0)
453                         return ipneigh_modify(RTM_NEWNEIGH, NLM_F_REPLACE, argc-1, argv+1);
454                 if (matches(*argv, "replace") == 0)
455                         return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
456                 if (matches(*argv, "delete") == 0)
457                         return ipneigh_modify(RTM_DELNEIGH, 0, argc-1, argv+1);
458                 if (matches(*argv, "get") == 0) {
459                         fprintf(stderr, "Sorry, \"neigh get\" is not implemented :-(\n");
460                         return -1;
461                 }
462                 if (matches(*argv, "show") == 0 ||
463                     matches(*argv, "lst") == 0 ||
464                     matches(*argv, "list") == 0)
465                         return do_show_or_flush(argc-1, argv+1, 0);
466                 if (matches(*argv, "flush") == 0)
467                         return do_show_or_flush(argc-1, argv+1, 1);
468                 if (matches(*argv, "help") == 0)
469                         usage();
470         } else
471                 return do_show_or_flush(0, NULL, 0);
472
473         fprintf(stderr, "Command \"%s\" is unknown, try \"ip neigh help\".\n", *argv);
474         exit(-1);
475 }