#include "rt_names.h"
#include "utils.h"
#include "ip_common.h"
+#include "json_print.h"
#define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY)
#define MAX_ROUNDS 10
{
fprintf(stderr, "Usage: ip neigh { add | del | change | replace }\n"
" { ADDR [ lladdr LLADDR ] [ nud STATE ] | proxy ADDR } [ dev DEV ]\n");
+ fprintf(stderr, " [ router ] [ extern_learn ]\n\n");
fprintf(stderr, " ip neigh { show | flush } [ proxy ] [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n");
fprintf(stderr, " [ vrf NAME ]\n\n");
fprintf(stderr, "STATE := { permanent | noarp | stale | reachable | none |\n"
dst_ok = 1;
dev_ok = 1;
req.ndm.ndm_flags |= NTF_PROXY;
+ } else if (strcmp(*argv, "router") == 0) {
+ req.ndm.ndm_flags |= NTF_ROUTER;
+ } else if (matches(*argv, "extern_learn") == 0) {
+ req.ndm.ndm_flags |= NTF_EXT_LEARNED;
} else if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
dev = *argv;
ll_init_map(&rth);
- if (dev && (req.ndm.ndm_ifindex = ll_name_to_index(dev)) == 0) {
- fprintf(stderr, "Cannot find device \"%s\"\n", dev);
- return -1;
+ if (dev) {
+ req.ndm.ndm_ifindex = ll_name_to_index(dev);
+ if (!req.ndm.ndm_ifindex)
+ return nodev(dev);
}
if (rtnl_talk(&rth, &req.n, NULL) < 0)
return 0;
}
+static void print_cacheinfo(const struct nda_cacheinfo *ci)
+{
+ static int hz;
+
+ if (!hz)
+ hz = get_user_hz();
+
+ if (ci->ndm_refcnt)
+ print_uint(PRINT_ANY, "refcnt",
+ " ref %u", ci->ndm_refcnt);
+
+ print_uint(PRINT_ANY, "used", " used %u", ci->ndm_used / hz);
+ print_uint(PRINT_ANY, "confirmed", "/%u", ci->ndm_confirmed / hz);
+ print_uint(PRINT_ANY, "updated", "/%u", ci->ndm_updated / hz);
+}
+
+static void print_neigh_state(unsigned int nud)
+{
+
+ open_json_array(PRINT_JSON,
+ is_json_context() ? "state" : "");
-int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+#define PRINT_FLAG(f) \
+ if (nud & NUD_##f) { \
+ nud &= ~NUD_##f; \
+ print_string(PRINT_ANY, NULL, " %s", #f); \
+ }
+
+ PRINT_FLAG(INCOMPLETE);
+ PRINT_FLAG(REACHABLE);
+ PRINT_FLAG(STALE);
+ PRINT_FLAG(DELAY);
+ PRINT_FLAG(PROBE);
+ PRINT_FLAG(FAILED);
+ PRINT_FLAG(NOARP);
+ PRINT_FLAG(PERMANENT);
+#undef PRINT_FLAG
+
+ close_json_array(PRINT_JSON, NULL);
+}
+
+int print_neigh(struct nlmsghdr *n, void *arg)
{
FILE *fp = (FILE *)arg;
struct ndmsg *r = NLMSG_DATA(n);
return 0;
if (!(filter.state&r->ndm_state) &&
!(r->ndm_flags & NTF_PROXY) &&
+ !(r->ndm_flags & NTF_EXT_LEARNED) &&
(r->ndm_state || !(filter.state&0x100)) &&
- (r->ndm_family != AF_DECnet))
+ (r->ndm_family != AF_DECnet))
return 0;
if (filter.master && !(n->nlmsg_flags & NLM_F_DUMP_FILTERED)) {
return 0;
}
+ open_json_object(NULL);
if (n->nlmsg_type == RTM_DELNEIGH)
- fprintf(fp, "Deleted ");
+ print_bool(PRINT_ANY, "deleted", "Deleted ", true);
else if (n->nlmsg_type == RTM_GETNEIGH)
- fprintf(fp, "miss ");
+ print_null(PRINT_ANY, "miss", "%s ", "miss");
+
if (tb[NDA_DST]) {
- fprintf(fp, "%s ",
- format_host_rta(r->ndm_family, tb[NDA_DST]));
+ const char *dst;
+
+ dst = format_host_rta(r->ndm_family, tb[NDA_DST]);
+ print_color_string(PRINT_ANY,
+ ifa_family_color(r->ndm_family),
+ "dst", "%s ", dst);
+ }
+
+ if (!filter.index && r->ndm_ifindex) {
+ if (!is_json_context())
+ fprintf(fp, "dev ");
+
+ print_color_string(PRINT_ANY, COLOR_IFNAME,
+ "dev", "%s ",
+ ll_index_to_name(r->ndm_ifindex));
}
- if (!filter.index && r->ndm_ifindex)
- fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex));
+
if (tb[NDA_LLADDR]) {
+ const char *lladdr;
SPRINT_BUF(b1);
- fprintf(fp, "lladdr %s", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
- RTA_PAYLOAD(tb[NDA_LLADDR]),
- ll_index_to_type(r->ndm_ifindex),
- b1, sizeof(b1)));
- }
- if (r->ndm_flags & NTF_ROUTER) {
- fprintf(fp, " router");
- }
- if (r->ndm_flags & NTF_PROXY) {
- fprintf(fp, " proxy");
- }
- if (tb[NDA_CACHEINFO] && show_stats) {
- struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
- int hz = get_user_hz();
- if (ci->ndm_refcnt)
- printf(" ref %d", ci->ndm_refcnt);
- fprintf(fp, " used %d/%d/%d", ci->ndm_used/hz,
- ci->ndm_confirmed/hz, ci->ndm_updated/hz);
- }
+ lladdr = ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
+ RTA_PAYLOAD(tb[NDA_LLADDR]),
+ ll_index_to_type(r->ndm_ifindex),
+ b1, sizeof(b1));
- if (tb[NDA_PROBES] && show_stats) {
- __u32 p = rta_getattr_u32(tb[NDA_PROBES]);
+ if (!is_json_context())
+ fprintf(fp, "lladdr ");
- fprintf(fp, " probes %u", p);
+ print_color_string(PRINT_ANY, COLOR_MAC,
+ "lladdr", "%s", lladdr);
}
- if (r->ndm_state) {
- int nud = r->ndm_state;
-
- fprintf(fp, " ");
-
-#define PRINT_FLAG(f) if (nud & NUD_##f) { \
- nud &= ~NUD_##f; fprintf(fp, #f "%s", nud ? "," : ""); }
- PRINT_FLAG(INCOMPLETE);
- PRINT_FLAG(REACHABLE);
- PRINT_FLAG(STALE);
- PRINT_FLAG(DELAY);
- PRINT_FLAG(PROBE);
- PRINT_FLAG(FAILED);
- PRINT_FLAG(NOARP);
- PRINT_FLAG(PERMANENT);
-#undef PRINT_FLAG
+ if (r->ndm_flags & NTF_ROUTER)
+ print_null(PRINT_ANY, "router", " %s", "router");
+
+ if (r->ndm_flags & NTF_PROXY)
+ print_null(PRINT_ANY, "proxy", " %s", "proxy");
+
+ if (r->ndm_flags & NTF_EXT_LEARNED)
+ print_null(PRINT_ANY, "extern_learn", " %s ", "extern_learn");
+
+ if (show_stats) {
+ if (tb[NDA_CACHEINFO])
+ print_cacheinfo(RTA_DATA(tb[NDA_CACHEINFO]));
+
+ if (tb[NDA_PROBES])
+ print_uint(PRINT_ANY, "probes", " probes %u",
+ rta_getattr_u32(tb[NDA_PROBES]));
}
- fprintf(fp, "\n");
- fflush(fp);
+ if (r->ndm_state)
+ print_neigh_state(r->ndm_state);
+
+ print_string(PRINT_FP, NULL, "\n", "");
+ close_json_object();
+ fflush(stdout);
+
return 0;
}
ll_init_map(&rth);
if (filter_dev) {
- if ((filter.index = ll_name_to_index(filter_dev)) == 0) {
- fprintf(stderr, "Cannot find device \"%s\"\n", filter_dev);
- return -1;
- }
+ filter.index = ll_name_to_index(filter_dev);
+ if (!filter.index)
+ return nodev(filter_dev);
addattr32(&req.n, sizeof(req), NDA_IFINDEX, filter.index);
}
exit(1);
}
+ new_json_obj(json);
if (rtnl_dump_filter(&rth, print_neigh, stdout) < 0) {
fprintf(stderr, "Dump terminated\n");
exit(1);
}
+ delete_json_obj();
return 0;
}