]> git.proxmox.com Git - mirror_iproute2.git/blobdiff - ip/ipneigh.c
ip route: get: allow zero-length subnet mask
[mirror_iproute2.git] / ip / ipneigh.c
index 71a4100f0b595f82b1641960b1f7ce42ea81fc2a..88596245a2eda88333dd42d58f24a169c4493743 100644 (file)
@@ -13,7 +13,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <syslog.h>
 #include <fcntl.h>
 #include <string.h>
 #include <sys/time.h>
@@ -24,6 +23,7 @@
 #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
@@ -31,7 +31,7 @@
 static struct
 {
        int family;
-        int index;
+       int index;
        int state;
        int unused_only;
        inet_prefix pfx;
@@ -39,20 +39,26 @@ static struct
        char *flushb;
        int flushp;
        int flushe;
+       int master;
+       int protocol;
+       __u8 ndm_flags;
 } filter;
 
 static void usage(void) __attribute__((noreturn));
 
 static void usage(void)
 {
-       fprintf(stderr, "Usage: ip neigh { add | del | change | replace } { ADDR [ lladdr LLADDR ]\n"
-                       "          [ nud { permanent | noarp | stale | reachable } ]\n"
-                       "          | proxy ADDR } [ dev DEV ]\n");
-       fprintf(stderr, "       ip neigh {show|flush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n");
+       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 ] [ protocol PROTO ]\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"
+                       "           incomplete | delay | probe | failed }\n");
        exit(-1);
 }
 
-static int nud_state_a2n(unsigned *state, const char *arg)
+static int nud_state_a2n(unsigned int *state, const char *arg)
 {
        if (matches(arg, "permanent") == 0)
                *state = NUD_PERMANENT;
@@ -75,7 +81,7 @@ static int nud_state_a2n(unsigned *state, const char *arg)
        else {
                if (get_unsigned(state, arg, 0))
                        return -1;
-               if (*state>=0x100 || (*state&((*state)-1)))
+               if (*state >= 0x100 || (*state&((*state)-1)))
                        return -1;
        }
        return 0;
@@ -97,22 +103,21 @@ static int ipneigh_modify(int cmd, int flags, int argc, char **argv)
        struct {
                struct nlmsghdr n;
                struct ndmsg            ndm;
-               char                    buf[256];
-       } req;
-       char  *d = NULL;
+               char                    buf[256];
+       } req = {
+               .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
+               .n.nlmsg_flags = NLM_F_REQUEST | flags,
+               .n.nlmsg_type = cmd,
+               .ndm.ndm_family = preferred_family,
+               .ndm.ndm_state = NUD_PERMANENT,
+       };
+       char  *dev = NULL;
        int dst_ok = 0;
+       int dev_ok = 0;
        int lladdr_ok = 0;
-       char * lla = NULL;
+       char *lla = NULL;
        inet_prefix dst;
 
-       memset(&req, 0, sizeof(req));
-
-       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
-       req.n.nlmsg_flags = NLM_F_REQUEST|flags;
-       req.n.nlmsg_type = cmd;
-       req.ndm.ndm_family = preferred_family;
-       req.ndm.ndm_state = NUD_PERMANENT;
-
        while (argc > 0) {
                if (matches(*argv, "lladdr") == 0) {
                        NEXT_ARG();
@@ -121,7 +126,8 @@ static int ipneigh_modify(int cmd, int flags, int argc, char **argv)
                        lla = *argv;
                        lladdr_ok = 1;
                } else if (strcmp(*argv, "nud") == 0) {
-                       unsigned state;
+                       unsigned int state;
+
                        NEXT_ARG();
                        if (nud_state_a2n(&state, *argv))
                                invarg("nud state is bad", *argv);
@@ -134,10 +140,24 @@ static int ipneigh_modify(int cmd, int flags, int argc, char **argv)
                                duparg("address", *argv);
                        get_addr(&dst, *argv, preferred_family);
                        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();
-                       d = *argv;
+                       dev = *argv;
+                       dev_ok = 1;
+               } else if (matches(*argv, "protocol") == 0) {
+                       __u32 proto;
+
+                       NEXT_ARG();
+                       if (rtnl_rtprot_a2n(&proto, *argv))
+                               invarg("\"protocol\" value is invalid\n", *argv);
+                       if (addattr8(&req.n, sizeof(req), NDA_PROTOCOL, proto))
+                               return -1;
                } else {
                        if (strcmp(*argv, "to") == 0) {
                                NEXT_ARG();
@@ -152,42 +172,89 @@ static int ipneigh_modify(int cmd, int flags, int argc, char **argv)
                }
                argc--; argv++;
        }
-       if (d == NULL || !dst_ok || dst.family == AF_UNSPEC) {
+       if (!dev_ok || !dst_ok || dst.family == AF_UNSPEC) {
                fprintf(stderr, "Device and destination are required arguments.\n");
                exit(-1);
        }
        req.ndm.ndm_family = dst.family;
-       addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen);
+       if (addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen) < 0)
+               return -1;
 
        if (lla && strcmp(lla, "null")) {
                char llabuf[20];
                int l;
 
                l = ll_addr_a2n(llabuf, sizeof(llabuf), lla);
-               addattr_l(&req.n, sizeof(req), NDA_LLADDR, llabuf, l);
+               if (l < 0)
+                       return -1;
+
+               if (addattr_l(&req.n, sizeof(req), NDA_LLADDR, llabuf, l) < 0)
+                       return -1;
        }
 
        ll_init_map(&rth);
 
-       if ((req.ndm.ndm_ifindex = ll_name_to_index(d)) == 0) {
-               fprintf(stderr, "Cannot find device \"%s\"\n", d);
-               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, 0, 0, NULL) < 0)
+       if (rtnl_talk(&rth, &req.n, NULL) < 0)
                exit(2);
 
        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" : "");
+
+#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(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+int print_neigh(struct nlmsghdr *n, void *arg)
 {
-       FILE *fp = (FILE*)arg;
+       FILE *fp = (FILE *)arg;
        struct ndmsg *r = NLMSG_DATA(n);
        int len = n->nlmsg_len;
-       struct rtattr * tb[NDA_MAX+1];
-       char abuf[256];
+       struct rtattr *tb[NDA_MAX+1];
+       static int logit = 1;
+       __u8 protocol = 0;
 
        if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH &&
            n->nlmsg_type != RTM_GETNEIGH) {
@@ -211,121 +278,172 @@ int print_neigh(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
                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;
 
-       parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
-
-       if (tb[NDA_DST]) {
-               if (filter.pfx.family) {
-                       inet_prefix dst;
-                       memset(&dst, 0, sizeof(dst));
-                       dst.family = r->ndm_family;
-                       memcpy(&dst.data, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
-                       if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
-                               return 0;
+       if (filter.master && !(n->nlmsg_flags & NLM_F_DUMP_FILTERED)) {
+               if (logit) {
+                       logit = 0;
+                       fprintf(fp,
+                               "\nWARNING: Kernel does not support filtering by master device\n\n");
                }
        }
+
+       parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
+
+       if (inet_addr_match_rta(&filter.pfx, tb[NDA_DST]))
+               return 0;
+
+       if (tb[NDA_PROTOCOL])
+               protocol = rta_getattr_u8(tb[NDA_PROTOCOL]);
+
+       if (filter.protocol && filter.protocol != protocol)
+               return 0;
+
        if (filter.unused_only && tb[NDA_CACHEINFO]) {
                struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
+
                if (ci->ndm_refcnt)
                        return 0;
        }
 
        if (filter.flushb) {
                struct nlmsghdr *fn;
+
                if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
                        if (flush_update())
                                return -1;
                }
-               fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
+               fn = (struct nlmsghdr *)(filter.flushb + NLMSG_ALIGN(filter.flushp));
                memcpy(fn, n, n->nlmsg_len);
                fn->nlmsg_type = RTM_DELNEIGH;
                fn->nlmsg_flags = NLM_F_REQUEST;
                fn->nlmsg_seq = ++rth.seq;
-               filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
+               filter.flushp = (((char *)fn) + n->nlmsg_len) - filter.flushb;
                filter.flushed++;
                if (show_stats < 2)
                        return 0;
        }
 
+       open_json_object(NULL);
        if (n->nlmsg_type == RTM_DELNEIGH)
-               fprintf(fp, "delete ");
+               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(r->ndm_family,
-                                   RTA_PAYLOAD(tb[NDA_DST]),
-                                   RTA_DATA(tb[NDA_DST]),
-                                   abuf, sizeof(abuf)));
+               const char *dst;
+               int family = r->ndm_family;
+
+               if (family == AF_BRIDGE) {
+                       if (RTA_PAYLOAD(tb[NDA_DST]) == sizeof(struct in6_addr))
+                               family = AF_INET6;
+                       else
+                               family = AF_INET;
+               }
+
+               dst = format_host_rta(family, tb[NDA_DST]);
+               print_color_string(PRINT_ANY,
+                                  ifa_family_color(family),
+                                  "dst", "%s ", dst);
        }
-       if (!filter.index && r->ndm_ifindex)
-               fprintf(fp, "dev %s ", ll_index_to_name(r->ndm_ifindex));
+
+       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 (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 (!is_json_context())
+                       fprintf(fp, "lladdr ");
+
+               print_color_string(PRINT_ANY, COLOR_MAC,
+                                  "lladdr", "%s", lladdr);
        }
 
-       if (tb[NDA_PROBES] && show_stats) {
-               __u32 p = rta_getattr_u32(tb[NDA_PROBES]);
-               fprintf(fp, " probes %u", p);
+       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]));
        }
 
-       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_state)
+               print_neigh_state(r->ndm_state);
+
+       if (protocol) {
+               SPRINT_BUF(b1);
+
+               print_string(PRINT_ANY, "protocol", " proto %s ",
+                            rtnl_rtprot_n2a(protocol, b1, sizeof(b1)));
        }
-       fprintf(fp, "\n");
 
-       fflush(fp);
+       print_string(PRINT_FP, NULL, "\n", "");
+       close_json_object();
+       fflush(stdout);
+
        return 0;
 }
 
-void ipneigh_reset_filter(void)
+void ipneigh_reset_filter(int ifindex)
 {
        memset(&filter, 0, sizeof(filter));
        filter.state = ~0;
+       filter.index = ifindex;
+}
+
+static int ipneigh_dump_filter(struct nlmsghdr *nlh, int reqlen)
+{
+       struct ndmsg *ndm = NLMSG_DATA(nlh);
+       int err;
+
+       ndm->ndm_flags = filter.ndm_flags;
+
+       if (filter.index) {
+               err = addattr32(nlh, reqlen, NDA_IFINDEX, filter.index);
+               if (err)
+                       return err;
+       }
+       if (filter.master) {
+               err = addattr32(nlh, reqlen, NDA_MASTER, filter.master);
+               if (err)
+                       return err;
+       }
+
+       return 0;
 }
 
 static int do_show_or_flush(int argc, char **argv, int flush)
 {
        char *filter_dev = NULL;
        int state_given = 0;
-       struct ndmsg ndm = { 0 };
 
-       ipneigh_reset_filter();
+       ipneigh_reset_filter(0);
 
        if (!filter.family)
                filter.family = preferred_family;
@@ -345,10 +463,29 @@ static int do_show_or_flush(int argc, char **argv, int flush)
                        if (filter_dev)
                                duparg("dev", *argv);
                        filter_dev = *argv;
+               } else if (strcmp(*argv, "master") == 0) {
+                       int ifindex;
+
+                       NEXT_ARG();
+                       ifindex = ll_name_to_index(*argv);
+                       if (!ifindex)
+                               invarg("Device does not exist\n", *argv);
+                       filter.master = ifindex;
+               } else if (strcmp(*argv, "vrf") == 0) {
+                       int ifindex;
+
+                       NEXT_ARG();
+                       ifindex = ll_name_to_index(*argv);
+                       if (!ifindex)
+                               invarg("Not a valid VRF name\n", *argv);
+                       if (!name_is_vrf(*argv))
+                               invarg("Not a valid VRF name\n", *argv);
+                       filter.master = ifindex;
                } else if (strcmp(*argv, "unused") == 0) {
                        filter.unused_only = 1;
                } else if (strcmp(*argv, "nud") == 0) {
-                       unsigned state;
+                       unsigned int state;
+
                        NEXT_ARG();
                        if (!state_given) {
                                state_given = 1;
@@ -364,15 +501,26 @@ static int do_show_or_flush(int argc, char **argv, int flush)
                        if (state == 0)
                                state = 0x100;
                        filter.state |= state;
-               } else if (strcmp(*argv, "proxy") == 0)
-                       ndm.ndm_flags = NTF_PROXY;
-               else {
+               } else if (strcmp(*argv, "proxy") == 0) {
+                       filter.ndm_flags = NTF_PROXY;
+               } else if (matches(*argv, "protocol") == 0) {
+                       __u32 prot;
+
+                       NEXT_ARG();
+                       if (rtnl_rtprot_a2n(&prot, *argv)) {
+                               if (strcmp(*argv, "all"))
+                                       invarg("invalid \"protocol\"\n", *argv);
+                               prot = 0;
+                       }
+                       filter.protocol = prot;
+               } else {
                        if (strcmp(*argv, "to") == 0) {
                                NEXT_ARG();
                        }
                        if (matches(*argv, "help") == 0)
                                usage();
-                       get_prefix(&filter.pfx, *argv, filter.family);
+                       if (get_prefix(&filter.pfx, *argv, filter.family))
+                               invarg("to value is invalid\n", *argv);
                        if (filter.family == AF_UNSPEC)
                                filter.family = filter.pfx.family;
                }
@@ -382,10 +530,9 @@ static int do_show_or_flush(int argc, char **argv, int flush)
        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);
        }
 
        if (flush) {
@@ -395,10 +542,10 @@ static int do_show_or_flush(int argc, char **argv, int flush)
                filter.flushb = flushb;
                filter.flushp = 0;
                filter.flushe = sizeof(flushb);
-               filter.state &= ~NUD_FAILED;
 
                while (round < MAX_ROUNDS) {
-                       if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) {
+                       if (rtnl_neighdump_req(&rth, filter.family,
+                                              ipneigh_dump_filter) < 0) {
                                perror("Cannot send dump request");
                                exit(1);
                        }
@@ -412,7 +559,7 @@ static int do_show_or_flush(int argc, char **argv, int flush)
                                        if (round == 0)
                                                printf("Nothing to flush.\n");
                                        else
-                                               printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
+                                               printf("*** Flush is complete after %d round%s ***\n", round, round > 1?"s":"");
                                }
                                fflush(stdout);
                                return 0;
@@ -424,23 +571,24 @@ static int do_show_or_flush(int argc, char **argv, int flush)
                                printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed);
                                fflush(stdout);
                        }
+                       filter.state &= ~NUD_FAILED;
                }
                printf("*** Flush not complete bailing out after %d rounds\n",
                        MAX_ROUNDS);
                return 1;
        }
 
-       ndm.ndm_family = filter.family;
-
-       if (rtnl_dump_request(&rth, RTM_GETNEIGH, &ndm, sizeof(struct ndmsg)) < 0) {
+       if (rtnl_neighdump_req(&rth, filter.family, ipneigh_dump_filter) < 0) {
                perror("Cannot send dump request");
                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;
 }