1
2
3
4
5
6
7
8
9
10
11
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <fcntl.h>
17#include <string.h>
18#include <sys/time.h>
19#include <sys/socket.h>
20#include <netinet/in.h>
21#include <netinet/ip.h>
22
23#include "rt_names.h"
24#include "utils.h"
25#include "ip_common.h"
26#include "json_print.h"
27
28#define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY)
29#define MAX_ROUNDS 10
30
31static struct
32{
33 int family;
34 int index;
35 int state;
36 int unused_only;
37 inet_prefix pfx;
38 int flushed;
39 char *flushb;
40 int flushp;
41 int flushe;
42 int master;
43 int protocol;
44 __u8 ndm_flags;
45} filter;
46
47static void usage(void) __attribute__((noreturn));
48
49static void usage(void)
50{
51 fprintf(stderr,
52 "Usage: ip neigh { add | del | change | replace }\n"
53 " { ADDR [ lladdr LLADDR ] [ nud STATE ] proxy ADDR }\n"
54 " [ dev DEV ] [ router ] [ extern_learn ] [ protocol PROTO ]\n"
55 "\n"
56 " ip neigh { show | flush } [ proxy ] [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n"
57 " [ vrf NAME ]\n"
58 "\n"
59 " ip neigh get { ADDR | proxy ADDR } dev DEV\n"
60 "\n"
61 "STATE := { delay | failed | incomplete | noarp | none |\n"
62 " permanent | probe | reachable | stale }\n");
63 exit(-1);
64}
65
66static int nud_state_a2n(unsigned int *state, const char *arg)
67{
68 if (matches(arg, "permanent") == 0)
69 *state = NUD_PERMANENT;
70 else if (matches(arg, "reachable") == 0)
71 *state = NUD_REACHABLE;
72 else if (strcmp(arg, "noarp") == 0)
73 *state = NUD_NOARP;
74 else if (strcmp(arg, "none") == 0)
75 *state = NUD_NONE;
76 else if (strcmp(arg, "stale") == 0)
77 *state = NUD_STALE;
78 else if (strcmp(arg, "incomplete") == 0)
79 *state = NUD_INCOMPLETE;
80 else if (strcmp(arg, "delay") == 0)
81 *state = NUD_DELAY;
82 else if (strcmp(arg, "probe") == 0)
83 *state = NUD_PROBE;
84 else if (matches(arg, "failed") == 0)
85 *state = NUD_FAILED;
86 else {
87 if (get_unsigned(state, arg, 0))
88 return -1;
89 if (*state >= 0x100 || (*state&((*state)-1)))
90 return -1;
91 }
92 return 0;
93}
94
95static int flush_update(void)
96{
97 if (rtnl_send_check(&rth, filter.flushb, filter.flushp) < 0) {
98 perror("Failed to send flush request");
99 return -1;
100 }
101 filter.flushp = 0;
102 return 0;
103}
104
105
106static int ipneigh_modify(int cmd, int flags, int argc, char **argv)
107{
108 struct {
109 struct nlmsghdr n;
110 struct ndmsg ndm;
111 char buf[256];
112 } req = {
113 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
114 .n.nlmsg_flags = NLM_F_REQUEST | flags,
115 .n.nlmsg_type = cmd,
116 .ndm.ndm_family = preferred_family,
117 .ndm.ndm_state = NUD_PERMANENT,
118 };
119 char *dev = NULL;
120 int dst_ok = 0;
121 int dev_ok = 0;
122 int lladdr_ok = 0;
123 char *lla = NULL;
124 inet_prefix dst;
125
126 while (argc > 0) {
127 if (matches(*argv, "lladdr") == 0) {
128 NEXT_ARG();
129 if (lladdr_ok)
130 duparg("lladdr", *argv);
131 lla = *argv;
132 lladdr_ok = 1;
133 } else if (strcmp(*argv, "nud") == 0) {
134 unsigned int state;
135
136 NEXT_ARG();
137 if (nud_state_a2n(&state, *argv))
138 invarg("nud state is bad", *argv);
139 req.ndm.ndm_state = state;
140 } else if (matches(*argv, "proxy") == 0) {
141 NEXT_ARG();
142 if (matches(*argv, "help") == 0)
143 usage();
144 if (dst_ok)
145 duparg("address", *argv);
146 get_addr(&dst, *argv, preferred_family);
147 dst_ok = 1;
148 dev_ok = 1;
149 req.ndm.ndm_flags |= NTF_PROXY;
150 } else if (strcmp(*argv, "router") == 0) {
151 req.ndm.ndm_flags |= NTF_ROUTER;
152 } else if (matches(*argv, "extern_learn") == 0) {
153 req.ndm.ndm_flags |= NTF_EXT_LEARNED;
154 } else if (strcmp(*argv, "dev") == 0) {
155 NEXT_ARG();
156 dev = *argv;
157 dev_ok = 1;
158 } else if (matches(*argv, "protocol") == 0) {
159 __u32 proto;
160
161 NEXT_ARG();
162 if (rtnl_rtprot_a2n(&proto, *argv))
163 invarg("\"protocol\" value is invalid\n", *argv);
164 if (addattr8(&req.n, sizeof(req), NDA_PROTOCOL, proto))
165 return -1;
166 } else {
167 if (strcmp(*argv, "to") == 0) {
168 NEXT_ARG();
169 }
170 if (matches(*argv, "help") == 0) {
171 NEXT_ARG();
172 }
173 if (dst_ok)
174 duparg2("to", *argv);
175 get_addr(&dst, *argv, preferred_family);
176 dst_ok = 1;
177 }
178 argc--; argv++;
179 }
180 if (!dev_ok || !dst_ok || dst.family == AF_UNSPEC) {
181 fprintf(stderr, "Device and destination are required arguments.\n");
182 exit(-1);
183 }
184 req.ndm.ndm_family = dst.family;
185 if (addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen) < 0)
186 return -1;
187
188 if (lla && strcmp(lla, "null")) {
189 char llabuf[20];
190 int l;
191
192 l = ll_addr_a2n(llabuf, sizeof(llabuf), lla);
193 if (l < 0)
194 return -1;
195
196 if (addattr_l(&req.n, sizeof(req), NDA_LLADDR, llabuf, l) < 0)
197 return -1;
198 }
199
200 ll_init_map(&rth);
201
202 if (dev) {
203 req.ndm.ndm_ifindex = ll_name_to_index(dev);
204 if (!req.ndm.ndm_ifindex)
205 return nodev(dev);
206 }
207
208 if (rtnl_talk(&rth, &req.n, NULL) < 0)
209 exit(2);
210
211 return 0;
212}
213
214static void print_cacheinfo(const struct nda_cacheinfo *ci)
215{
216 static int hz;
217
218 if (!hz)
219 hz = get_user_hz();
220
221 if (ci->ndm_refcnt)
222 print_uint(PRINT_ANY, "refcnt",
223 " ref %u", ci->ndm_refcnt);
224
225 print_uint(PRINT_ANY, "used", " used %u", ci->ndm_used / hz);
226 print_uint(PRINT_ANY, "confirmed", "/%u", ci->ndm_confirmed / hz);
227 print_uint(PRINT_ANY, "updated", "/%u", ci->ndm_updated / hz);
228}
229
230static void print_neigh_state(unsigned int nud)
231{
232
233 open_json_array(PRINT_JSON,
234 is_json_context() ? "state" : "");
235
236#define PRINT_FLAG(f) \
237 if (nud & NUD_##f) { \
238 nud &= ~NUD_##f; \
239 print_string(PRINT_ANY, NULL, " %s", #f); \
240 }
241
242 PRINT_FLAG(INCOMPLETE);
243 PRINT_FLAG(REACHABLE);
244 PRINT_FLAG(STALE);
245 PRINT_FLAG(DELAY);
246 PRINT_FLAG(PROBE);
247 PRINT_FLAG(FAILED);
248 PRINT_FLAG(NOARP);
249 PRINT_FLAG(PERMANENT);
250#undef PRINT_FLAG
251
252 close_json_array(PRINT_JSON, NULL);
253}
254
255static int print_neigh_brief(FILE *fp, struct ndmsg *r, struct rtattr *tb[])
256{
257 if (tb[NDA_DST]) {
258 const char *dst;
259 int family = r->ndm_family;
260
261 if (family == AF_BRIDGE) {
262 if (RTA_PAYLOAD(tb[NDA_DST]) == sizeof(struct in6_addr))
263 family = AF_INET6;
264 else
265 family = AF_INET;
266 }
267
268 dst = format_host_rta(family, tb[NDA_DST]);
269 print_color_string(PRINT_ANY, ifa_family_color(family),
270 "dst", "%-39s ", dst);
271 }
272
273 if (!filter.index && r->ndm_ifindex) {
274 print_color_string(PRINT_ANY, COLOR_IFNAME,
275 "dev", "%-16s ",
276 ll_index_to_name(r->ndm_ifindex));
277 }
278
279 if (tb[NDA_LLADDR]) {
280 const char *lladdr;
281
282 SPRINT_BUF(b1);
283
284 lladdr = ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
285 RTA_PAYLOAD(tb[NDA_LLADDR]),
286 ll_index_to_type(r->ndm_ifindex),
287 b1, sizeof(b1));
288
289 print_color_string(PRINT_ANY, COLOR_MAC,
290 "lladdr", "%s", lladdr);
291 }
292
293 print_string(PRINT_FP, NULL, "%s", "\n");
294 close_json_object();
295 fflush(fp);
296
297 return 0;
298}
299
300int print_neigh(struct nlmsghdr *n, void *arg)
301{
302 FILE *fp = (FILE *)arg;
303 struct ndmsg *r = NLMSG_DATA(n);
304 int len = n->nlmsg_len;
305 struct rtattr *tb[NDA_MAX+1];
306 static int logit = 1;
307 __u8 protocol = 0;
308
309 if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH &&
310 n->nlmsg_type != RTM_GETNEIGH) {
311 fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n",
312 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
313
314 return 0;
315 }
316 len -= NLMSG_LENGTH(sizeof(*r));
317 if (len < 0) {
318 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
319 return -1;
320 }
321
322 if (filter.flushb && n->nlmsg_type != RTM_NEWNEIGH)
323 return 0;
324
325 if (filter.family && filter.family != r->ndm_family)
326 return 0;
327 if (filter.index && filter.index != r->ndm_ifindex)
328 return 0;
329 if (!(filter.state&r->ndm_state) &&
330 !(r->ndm_flags & NTF_PROXY) &&
331 !(r->ndm_flags & NTF_EXT_LEARNED) &&
332 (r->ndm_state || !(filter.state&0x100)))
333 return 0;
334
335 if (filter.master && !(n->nlmsg_flags & NLM_F_DUMP_FILTERED)) {
336 if (logit) {
337 logit = 0;
338 fprintf(fp,
339 "\nWARNING: Kernel does not support filtering by master device\n\n");
340 }
341 }
342
343 parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
344
345 if (inet_addr_match_rta(&filter.pfx, tb[NDA_DST]))
346 return 0;
347
348 if (tb[NDA_PROTOCOL])
349 protocol = rta_getattr_u8(tb[NDA_PROTOCOL]);
350
351 if (filter.protocol && filter.protocol != protocol)
352 return 0;
353
354 if (filter.unused_only && tb[NDA_CACHEINFO]) {
355 struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
356
357 if (ci->ndm_refcnt)
358 return 0;
359 }
360
361 if (filter.flushb) {
362 struct nlmsghdr *fn;
363
364 if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
365 if (flush_update())
366 return -1;
367 }
368 fn = (struct nlmsghdr *)(filter.flushb + NLMSG_ALIGN(filter.flushp));
369 memcpy(fn, n, n->nlmsg_len);
370 fn->nlmsg_type = RTM_DELNEIGH;
371 fn->nlmsg_flags = NLM_F_REQUEST;
372 fn->nlmsg_seq = ++rth.seq;
373 filter.flushp = (((char *)fn) + n->nlmsg_len) - filter.flushb;
374 filter.flushed++;
375 if (show_stats < 2)
376 return 0;
377 }
378
379 open_json_object(NULL);
380 if (n->nlmsg_type == RTM_DELNEIGH)
381 print_bool(PRINT_ANY, "deleted", "Deleted ", true);
382 else if (n->nlmsg_type == RTM_GETNEIGH)
383 print_null(PRINT_ANY, "miss", "%s ", "miss");
384
385 if (brief)
386 return print_neigh_brief(fp, r, tb);
387
388 if (tb[NDA_DST]) {
389 const char *dst;
390 int family = r->ndm_family;
391
392 if (family == AF_BRIDGE) {
393 if (RTA_PAYLOAD(tb[NDA_DST]) == sizeof(struct in6_addr))
394 family = AF_INET6;
395 else
396 family = AF_INET;
397 }
398
399 dst = format_host_rta(family, tb[NDA_DST]);
400 print_color_string(PRINT_ANY,
401 ifa_family_color(family),
402 "dst", "%s ", dst);
403 }
404
405 if (!filter.index && r->ndm_ifindex) {
406 if (!is_json_context())
407 fprintf(fp, "dev ");
408
409 print_color_string(PRINT_ANY, COLOR_IFNAME,
410 "dev", "%s ",
411 ll_index_to_name(r->ndm_ifindex));
412 }
413
414 if (tb[NDA_LLADDR]) {
415 const char *lladdr;
416 SPRINT_BUF(b1);
417
418 lladdr = ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
419 RTA_PAYLOAD(tb[NDA_LLADDR]),
420 ll_index_to_type(r->ndm_ifindex),
421 b1, sizeof(b1));
422
423 if (!is_json_context())
424 fprintf(fp, "lladdr ");
425
426 print_color_string(PRINT_ANY, COLOR_MAC,
427 "lladdr", "%s", lladdr);
428 }
429
430 if (r->ndm_flags & NTF_ROUTER)
431 print_null(PRINT_ANY, "router", " %s", "router");
432
433 if (r->ndm_flags & NTF_PROXY)
434 print_null(PRINT_ANY, "proxy", " %s", "proxy");
435
436 if (r->ndm_flags & NTF_EXT_LEARNED)
437 print_null(PRINT_ANY, "extern_learn", " %s ", "extern_learn");
438
439 if (r->ndm_flags & NTF_OFFLOADED)
440 print_null(PRINT_ANY, "offload", " %s", "offload");
441
442 if (show_stats) {
443 if (tb[NDA_CACHEINFO])
444 print_cacheinfo(RTA_DATA(tb[NDA_CACHEINFO]));
445
446 if (tb[NDA_PROBES])
447 print_uint(PRINT_ANY, "probes", " probes %u",
448 rta_getattr_u32(tb[NDA_PROBES]));
449 }
450
451 if (r->ndm_state)
452 print_neigh_state(r->ndm_state);
453
454 if (protocol) {
455 SPRINT_BUF(b1);
456
457 print_string(PRINT_ANY, "protocol", " proto %s ",
458 rtnl_rtprot_n2a(protocol, b1, sizeof(b1)));
459 }
460
461 print_string(PRINT_FP, NULL, "\n", "");
462 close_json_object();
463 fflush(fp);
464
465 return 0;
466}
467
468void ipneigh_reset_filter(int ifindex)
469{
470 memset(&filter, 0, sizeof(filter));
471 filter.state = ~0;
472 filter.index = ifindex;
473}
474
475static int ipneigh_dump_filter(struct nlmsghdr *nlh, int reqlen)
476{
477 struct ndmsg *ndm = NLMSG_DATA(nlh);
478 int err;
479
480 ndm->ndm_flags = filter.ndm_flags;
481
482 if (filter.index) {
483 err = addattr32(nlh, reqlen, NDA_IFINDEX, filter.index);
484 if (err)
485 return err;
486 }
487 if (filter.master) {
488 err = addattr32(nlh, reqlen, NDA_MASTER, filter.master);
489 if (err)
490 return err;
491 }
492
493 return 0;
494}
495
496static int do_show_or_flush(int argc, char **argv, int flush)
497{
498 char *filter_dev = NULL;
499 int state_given = 0;
500
501 ipneigh_reset_filter(0);
502
503 if (!filter.family)
504 filter.family = preferred_family;
505
506 if (flush) {
507 if (argc <= 0) {
508 fprintf(stderr, "Flush requires arguments.\n");
509 return -1;
510 }
511 filter.state = ~(NUD_PERMANENT|NUD_NOARP);
512 } else
513 filter.state = 0xFF & ~NUD_NOARP;
514
515 while (argc > 0) {
516 if (strcmp(*argv, "dev") == 0) {
517 NEXT_ARG();
518 if (filter_dev)
519 duparg("dev", *argv);
520 filter_dev = *argv;
521 } else if (strcmp(*argv, "master") == 0) {
522 int ifindex;
523
524 NEXT_ARG();
525 ifindex = ll_name_to_index(*argv);
526 if (!ifindex)
527 invarg("Device does not exist\n", *argv);
528 filter.master = ifindex;
529 } else if (strcmp(*argv, "vrf") == 0) {
530 int ifindex;
531
532 NEXT_ARG();
533 ifindex = ll_name_to_index(*argv);
534 if (!ifindex)
535 invarg("Not a valid VRF name\n", *argv);
536 if (!name_is_vrf(*argv))
537 invarg("Not a valid VRF name\n", *argv);
538 filter.master = ifindex;
539 } else if (strcmp(*argv, "unused") == 0) {
540 filter.unused_only = 1;
541 } else if (strcmp(*argv, "nud") == 0) {
542 unsigned int state;
543
544 NEXT_ARG();
545 if (!state_given) {
546 state_given = 1;
547 filter.state = 0;
548 }
549 if (nud_state_a2n(&state, *argv)) {
550 if (strcmp(*argv, "all") != 0)
551 invarg("nud state is bad", *argv);
552 state = ~0;
553 if (flush)
554 state &= ~NUD_NOARP;
555 }
556 if (state == 0)
557 state = 0x100;
558 filter.state |= state;
559 } else if (strcmp(*argv, "proxy") == 0) {
560 filter.ndm_flags = NTF_PROXY;
561 } else if (matches(*argv, "protocol") == 0) {
562 __u32 prot;
563
564 NEXT_ARG();
565 if (rtnl_rtprot_a2n(&prot, *argv)) {
566 if (strcmp(*argv, "all"))
567 invarg("invalid \"protocol\"\n", *argv);
568 prot = 0;
569 }
570 filter.protocol = prot;
571 } else {
572 if (strcmp(*argv, "to") == 0) {
573 NEXT_ARG();
574 }
575 if (matches(*argv, "help") == 0)
576 usage();
577 if (get_prefix(&filter.pfx, *argv, filter.family))
578 invarg("to value is invalid\n", *argv);
579 if (filter.family == AF_UNSPEC)
580 filter.family = filter.pfx.family;
581 }
582 argc--; argv++;
583 }
584
585 ll_init_map(&rth);
586
587 if (filter_dev) {
588 filter.index = ll_name_to_index(filter_dev);
589 if (!filter.index)
590 return nodev(filter_dev);
591 }
592
593 if (flush) {
594 int round = 0;
595 char flushb[4096-512];
596
597 filter.flushb = flushb;
598 filter.flushp = 0;
599 filter.flushe = sizeof(flushb);
600
601 while (round < MAX_ROUNDS) {
602 if (rtnl_neighdump_req(&rth, filter.family,
603 ipneigh_dump_filter) < 0) {
604 perror("Cannot send dump request");
605 exit(1);
606 }
607 filter.flushed = 0;
608 if (rtnl_dump_filter(&rth, print_neigh, stdout) < 0) {
609 fprintf(stderr, "Flush terminated\n");
610 exit(1);
611 }
612 if (filter.flushed == 0) {
613 if (show_stats) {
614 if (round == 0)
615 printf("Nothing to flush.\n");
616 else
617 printf("*** Flush is complete after %d round%s ***\n", round, round > 1?"s":"");
618 }
619 fflush(stdout);
620 return 0;
621 }
622 round++;
623 if (flush_update() < 0)
624 exit(1);
625 if (show_stats) {
626 printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed);
627 fflush(stdout);
628 }
629 filter.state &= ~NUD_FAILED;
630 }
631 printf("*** Flush not complete bailing out after %d rounds\n",
632 MAX_ROUNDS);
633 return 1;
634 }
635
636 if (rtnl_neighdump_req(&rth, filter.family, ipneigh_dump_filter) < 0) {
637 perror("Cannot send dump request");
638 exit(1);
639 }
640
641 new_json_obj(json);
642 if (rtnl_dump_filter(&rth, print_neigh, stdout) < 0) {
643 fprintf(stderr, "Dump terminated\n");
644 exit(1);
645 }
646 delete_json_obj();
647
648 return 0;
649}
650
651static int ipneigh_get(int argc, char **argv)
652{
653 struct {
654 struct nlmsghdr n;
655 struct ndmsg ndm;
656 char buf[1024];
657 } req = {
658 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
659 .n.nlmsg_flags = NLM_F_REQUEST,
660 .n.nlmsg_type = RTM_GETNEIGH,
661 .ndm.ndm_family = preferred_family,
662 };
663 struct nlmsghdr *answer;
664 char *d = NULL;
665 int dst_ok = 0;
666 int dev_ok = 0;
667 inet_prefix dst;
668
669 while (argc > 0) {
670 if (strcmp(*argv, "dev") == 0) {
671 NEXT_ARG();
672 d = *argv;
673 dev_ok = 1;
674 } else if (matches(*argv, "proxy") == 0) {
675 NEXT_ARG();
676 if (matches(*argv, "help") == 0)
677 usage();
678 if (dst_ok)
679 duparg("address", *argv);
680 get_addr(&dst, *argv, preferred_family);
681 dst_ok = 1;
682 dev_ok = 1;
683 req.ndm.ndm_flags |= NTF_PROXY;
684 } else {
685 if (strcmp(*argv, "to") == 0)
686 NEXT_ARG();
687
688 if (matches(*argv, "help") == 0)
689 usage();
690 if (dst_ok)
691 duparg2("to", *argv);
692 get_addr(&dst, *argv, preferred_family);
693 dst_ok = 1;
694 }
695 argc--; argv++;
696 }
697
698 if (!dev_ok || !dst_ok || dst.family == AF_UNSPEC) {
699 fprintf(stderr, "Device and address are required arguments.\n");
700 return -1;
701 }
702
703 req.ndm.ndm_family = dst.family;
704 if (addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen) < 0)
705 return -1;
706
707 if (d) {
708 req.ndm.ndm_ifindex = ll_name_to_index(d);
709 if (!req.ndm.ndm_ifindex) {
710 fprintf(stderr, "Cannot find device \"%s\"\n", d);
711 return -1;
712 }
713 }
714
715 if (rtnl_talk(&rth, &req.n, &answer) < 0)
716 return -2;
717
718 ipneigh_reset_filter(0);
719 if (print_neigh(answer, stdout) < 0) {
720 fprintf(stderr, "An error :-)\n");
721 return -1;
722 }
723
724 return 0;
725}
726
727int do_ipneigh(int argc, char **argv)
728{
729 if (argc > 0) {
730 if (matches(*argv, "add") == 0)
731 return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
732 if (matches(*argv, "change") == 0 ||
733 strcmp(*argv, "chg") == 0)
734 return ipneigh_modify(RTM_NEWNEIGH, NLM_F_REPLACE, argc-1, argv+1);
735 if (matches(*argv, "replace") == 0)
736 return ipneigh_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
737 if (matches(*argv, "delete") == 0)
738 return ipneigh_modify(RTM_DELNEIGH, 0, argc-1, argv+1);
739 if (matches(*argv, "get") == 0)
740 return ipneigh_get(argc-1, argv+1);
741 if (matches(*argv, "show") == 0 ||
742 matches(*argv, "lst") == 0 ||
743 matches(*argv, "list") == 0)
744 return do_show_or_flush(argc-1, argv+1, 0);
745 if (matches(*argv, "flush") == 0)
746 return do_show_or_flush(argc-1, argv+1, 1);
747 if (matches(*argv, "help") == 0)
748 usage();
749 } else
750 return do_show_or_flush(0, NULL, 0);
751
752 fprintf(stderr, "Command \"%s\" is unknown, try \"ip neigh help\".\n", *argv);
753 exit(-1);
754}
755