]> git.proxmox.com Git - mirror_iproute2.git/commitdiff
Merge branch 'strict-dumps' into iproute2-next
authorDavid Ahern <dsahern@gmail.com>
Thu, 27 Dec 2018 23:37:44 +0000 (15:37 -0800)
committerDavid Ahern <dsahern@gmail.com>
Thu, 27 Dec 2018 23:37:44 +0000 (15:37 -0800)
Signed-off-by: David Ahern <dsahern@gmail.com>
bridge/bridge.c
include/libnetlink.h
ip/ip.c
ip/ip_common.h
ip/ipaddress.c
ip/ipmroute.c
ip/ipneigh.c
ip/iproute.c
ip/ipvrf.c
lib/libnetlink.c

index a3d8154be898e33b6f48a0f2e12fc93f6bd272fb..a50d9d59b4c5a452bf2356f0419e4eee847c0946 100644 (file)
@@ -97,6 +97,8 @@ static int batch(const char *name)
                return EXIT_FAILURE;
        }
 
+       rtnl_set_strict_dump(&rth);
+
        cmdlineno = 0;
        while (getcmdline(&line, &len, stdin) != -1) {
                char *largv[100];
@@ -205,6 +207,8 @@ main(int argc, char **argv)
        if (rtnl_open(&rth, 0) < 0)
                exit(1);
 
+       rtnl_set_strict_dump(&rth);
+
        if (argc > 1)
                return do_cmd(argv[1], argc-1, argv+1);
 
index 138840d5c892e53492c4b993d3f62efdc01c54f0..dc0c9c4eb3f5218c33263dbd1bb1962125c1b533 100644 (file)
@@ -46,12 +46,17 @@ int rtnl_open_byproto(struct rtnl_handle *rth, unsigned int subscriptions,
        __attribute__((warn_unused_result));
 
 void rtnl_close(struct rtnl_handle *rth);
+void rtnl_set_strict_dump(struct rtnl_handle *rth);
 
-int rtnl_addrdump_req(struct rtnl_handle *rth, int family)
+typedef int (*req_filter_fn_t)(struct nlmsghdr *nlh, int reqlen);
+
+int rtnl_addrdump_req(struct rtnl_handle *rth, int family,
+                     req_filter_fn_t filter_fn)
        __attribute__((warn_unused_result));
 int rtnl_addrlbldump_req(struct rtnl_handle *rth, int family)
        __attribute__((warn_unused_result));
-int rtnl_routedump_req(struct rtnl_handle *rth, int family)
+int rtnl_routedump_req(struct rtnl_handle *rth, int family,
+                      req_filter_fn_t filter_fn)
        __attribute__((warn_unused_result));
 int rtnl_ruledump_req(struct rtnl_handle *rth, int family)
        __attribute__((warn_unused_result));
@@ -71,8 +76,6 @@ int rtnl_linkdump_req(struct rtnl_handle *rth, int fam)
 int rtnl_linkdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask)
        __attribute__((warn_unused_result));
 
-typedef int (*req_filter_fn_t)(struct nlmsghdr *nlh, int reqlen);
-
 int rtnl_linkdump_req_filter_fn(struct rtnl_handle *rth, int fam,
                                req_filter_fn_t fn)
        __attribute__((warn_unused_result));
diff --git a/ip/ip.c b/ip/ip.c
index a5bbacb4bb0f589a8305279b66a571c9d1bc1af4..e4131714018f9a843a02cbff2fa9054eae75e75d 100644 (file)
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -308,6 +308,8 @@ int main(int argc, char **argv)
        if (rtnl_open(&rth, 0) < 0)
                exit(1);
 
+       rtnl_set_strict_dump(&rth);
+
        if (strlen(basename) > 2)
                return do_cmd(basename+2, argc, argv);
 
index 53668f598cd28f3a6ef5e4c5ed9f43b9ae818b6f..d67575c63c24b93d268dea47c99fd467e3d7dd08 100644 (file)
@@ -84,8 +84,7 @@ int do_seg6(int argc, char **argv);
 int iplink_get(char *name, __u32 filt_mask);
 int iplink_ifla_xstats(int argc, char **argv);
 
-int ip_linkaddr_list(int family, req_filter_fn_t filter_fn,
-                    struct nlmsg_chain *linfo, struct nlmsg_chain *ainfo);
+int ip_link_list(req_filter_fn_t filter_fn, struct nlmsg_chain *linfo);
 void free_nlmsg_chain(struct nlmsg_chain *info);
 
 static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb)
index 016662e910e971d170b073bd829d319a701949f2..2bc33f3a3b3f2b1097a4e84b8dde4d99ec3bd206 100644 (file)
@@ -1679,6 +1679,15 @@ static void ipaddr_filter(struct nlmsg_chain *linfo, struct nlmsg_chain *ainfo)
        }
 }
 
+static int ipaddr_dump_filter(struct nlmsghdr *nlh, int reqlen)
+{
+       struct ifaddrmsg *ifa = NLMSG_DATA(nlh);
+
+       ifa->ifa_index = filter.ifindex;
+
+       return 0;
+}
+
 static int ipaddr_flush(void)
 {
        int round = 0;
@@ -1689,7 +1698,8 @@ static int ipaddr_flush(void)
        filter.flushe = sizeof(flushb);
 
        while ((max_flush_loops == 0) || (round < max_flush_loops)) {
-               if (rtnl_addrdump_req(&rth, filter.family) < 0) {
+               if (rtnl_addrdump_req(&rth, filter.family,
+                                     ipaddr_dump_filter) < 0) {
                        perror("Cannot send dump request");
                        exit(1);
                }
@@ -1762,12 +1772,41 @@ static int iplink_filter_req(struct nlmsghdr *nlh, int reqlen)
        return 0;
 }
 
+static int ipaddr_link_get(int index, struct nlmsg_chain *linfo)
+{
+       struct iplink_req req = {
+               .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+               .n.nlmsg_flags = NLM_F_REQUEST,
+               .n.nlmsg_type = RTM_GETLINK,
+               .i.ifi_family = filter.family,
+               .i.ifi_index = index,
+       };
+       __u32 filt_mask = RTEXT_FILTER_VF;
+       struct nlmsghdr *answer;
+
+       if (!show_stats)
+               filt_mask |= RTEXT_FILTER_SKIP_STATS;
+
+       addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask);
+
+       if (rtnl_talk(&rth, &req.n, &answer) < 0) {
+               perror("Cannot send link request");
+               return 1;
+       }
+
+       if (store_nlmsg(answer, linfo) < 0) {
+               fprintf(stderr, "Failed to process link information\n");
+               return 1;
+       }
+
+       return 0;
+}
+
 /* fills in linfo with link data and optionally ainfo with address info
  * caller can walk lists as desired and must call free_nlmsg_chain for
  * both when done
  */
-int ip_linkaddr_list(int family, req_filter_fn_t filter_fn,
-                    struct nlmsg_chain *linfo, struct nlmsg_chain *ainfo)
+int ip_link_list(req_filter_fn_t filter_fn, struct nlmsg_chain *linfo)
 {
        if (rtnl_linkdump_req_filter_fn(&rth, preferred_family,
                                        filter_fn) < 0) {
@@ -1780,16 +1819,19 @@ int ip_linkaddr_list(int family, req_filter_fn_t filter_fn,
                return 1;
        }
 
-       if (ainfo) {
-               if (rtnl_addrdump_req(&rth, family) < 0) {
-                       perror("Cannot send dump request");
-                       return 1;
-               }
+       return 0;
+}
 
-               if (rtnl_dump_filter(&rth, store_nlmsg, ainfo) < 0) {
-                       fprintf(stderr, "Dump terminated\n");
-                       return 1;
-               }
+static int ip_addr_list(struct nlmsg_chain *ainfo)
+{
+       if (rtnl_addrdump_req(&rth, filter.family, ipaddr_dump_filter) < 0) {
+               perror("Cannot send dump request");
+               return 1;
+       }
+
+       if (rtnl_dump_filter(&rth, store_nlmsg, ainfo) < 0) {
+               fprintf(stderr, "Dump terminated\n");
+               return 1;
        }
 
        return 0;
@@ -1798,7 +1840,7 @@ int ip_linkaddr_list(int family, req_filter_fn_t filter_fn,
 static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
 {
        struct nlmsg_chain linfo = { NULL, NULL};
-       struct nlmsg_chain _ainfo = { NULL, NULL}, *ainfo = NULL;
+       struct nlmsg_chain _ainfo = { NULL, NULL}, *ainfo = &_ainfo;
        struct nlmsg_list *l;
        char *filter_dev = NULL;
        int no_link = 0;
@@ -1906,7 +1948,8 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
                if (ipadd_save_prep())
                        exit(1);
 
-               if (rtnl_addrdump_req(&rth, preferred_family) < 0) {
+               if (rtnl_addrdump_req(&rth, preferred_family,
+                                     ipaddr_dump_filter) < 0) {
                        perror("Cannot send dump request");
                        exit(1);
                }
@@ -1940,19 +1983,23 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
                goto out;
        }
 
-       if (filter.family != AF_PACKET) {
-               ainfo = &_ainfo;
+       if (filter.ifindex) {
+               if (ipaddr_link_get(filter.ifindex, &linfo) != 0)
+                       goto out;
+       } else {
+               if (ip_link_list(iplink_filter_req, &linfo) != 0)
+                       goto out;
+       }
 
+       if (filter.family != AF_PACKET) {
                if (filter.oneline)
                        no_link = 1;
-       }
 
-       if (ip_linkaddr_list(filter.family, iplink_filter_req,
-                            &linfo, ainfo) != 0)
-               goto out;
+               if (ip_addr_list(ainfo) != 0)
+                       goto out;
 
-       if (filter.family != AF_PACKET)
                ipaddr_filter(&linfo, ainfo);
+       }
 
        for (l = linfo.head; l; l = l->next) {
                struct nlmsghdr *n = &l->h;
@@ -1971,8 +2018,7 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
        fflush(stdout);
 
 out:
-       if (ainfo)
-               free_nlmsg_chain(ainfo);
+       free_nlmsg_chain(ainfo);
        free_nlmsg_chain(&linfo);
        delete_json_obj();
        return 0;
index 4d8867d3219f6ec86290457ab2b6d4b4e7e930fc..b29c78e4cc86c96a5c562c9356c637a0212fa18c 100644 (file)
@@ -220,21 +220,36 @@ void ipmroute_reset_filter(int ifindex)
        filter.iif = ifindex;
 }
 
+static int iproute_dump_filter(struct nlmsghdr *nlh, int reqlen)
+{
+       int err;
+
+       if (filter.tb) {
+               err = addattr32(nlh, reqlen, RTA_TABLE, filter.tb);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
 static int mroute_list(int argc, char **argv)
 {
        char *id = NULL;
-       int family;
+       int family = preferred_family;
 
        ipmroute_reset_filter(0);
-       if (preferred_family == AF_UNSPEC)
-               family = AF_INET;
-       else
-               family = AF_INET6;
-       if (family == AF_INET) {
+       if (family == AF_INET || family == AF_UNSPEC) {
+               family = RTNL_FAMILY_IPMR;
                filter.af = RTNL_FAMILY_IPMR;
                filter.tb = RT_TABLE_DEFAULT;  /* for backward compatibility */
-       } else
+       } else if (family == AF_INET6) {
+               family = RTNL_FAMILY_IP6MR;
                filter.af = RTNL_FAMILY_IP6MR;
+       } else {
+               /* family does not have multicast routing */
+               return 0;
+       }
 
        filter.msrc.family = filter.mdst.family = family;
 
@@ -283,7 +298,7 @@ static int mroute_list(int argc, char **argv)
                filter.iif = idx;
        }
 
-       if (rtnl_routedump_req(&rth, filter.af) < 0) {
+       if (rtnl_routedump_req(&rth, filter.af, iproute_dump_filter) < 0) {
                perror("Cannot send dump request");
                return 1;
        }
index 6041c467749c35bc25794a9bdc20a409e8a4af74..26ac2d1be21f4af2c09dc62610f7df84c74f1fa2 100644 (file)
@@ -40,6 +40,7 @@ static struct
        int flushp;
        int flushe;
        int master;
+       int protocol;
 } filter;
 
 static void usage(void) __attribute__((noreturn));
@@ -48,7 +49,7 @@ static void usage(void)
 {
        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, "                [ 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"
@@ -148,6 +149,14 @@ static int ipneigh_modify(int cmd, int flags, int argc, char **argv)
                        NEXT_ARG();
                        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();
@@ -244,6 +253,7 @@ int print_neigh(struct nlmsghdr *n, void *arg)
        int len = n->nlmsg_len;
        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) {
@@ -285,6 +295,12 @@ int print_neigh(struct nlmsghdr *n, void *arg)
        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]);
 
@@ -371,6 +387,13 @@ int print_neigh(struct nlmsghdr *n, void *arg)
        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)));
+       }
+
        print_string(PRINT_FP, NULL, "\n", "");
        close_json_object();
        fflush(stdout);
@@ -458,9 +481,19 @@ 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)
+               } else if (strcmp(*argv, "proxy") == 0) {
                        req.ndm.ndm_flags = NTF_PROXY;
-               else {
+               } 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();
                        }
index d347246925531ead5e7a6eb40e9bdbbfcb9b4bc2..0440366ee26a17c2c35b63ecb46ec1a37e4fe817 100644 (file)
@@ -1535,24 +1535,6 @@ static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv)
        return 0;
 }
 
-static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
-{
-       struct {
-               struct nlmsghdr nlh;
-               struct rtmsg rtm;
-       } req = {
-               .nlh.nlmsg_len = sizeof(req),
-               .nlh.nlmsg_type = RTM_GETROUTE,
-               .nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST,
-               .nlh.nlmsg_seq = rth->dump = ++rth->seq,
-               .rtm.rtm_family = family,
-               .rtm.rtm_flags = RTM_F_CLONED,
-       };
-       struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
-
-       return sendto(rth->fd, (void *)&req, sizeof(req), 0, (struct sockaddr *)&nladdr, sizeof(nladdr));
-}
-
 static int iproute_flush_cache(void)
 {
 #define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush"
@@ -1622,7 +1604,7 @@ static int save_route_prep(void)
        return 0;
 }
 
-static int iproute_flush(int do_ipv6, rtnl_filter_t filter_fn)
+static int iproute_flush(int family, rtnl_filter_t filter_fn)
 {
        time_t start = time(0);
        char flushb[4096-512];
@@ -1630,12 +1612,12 @@ static int iproute_flush(int do_ipv6, rtnl_filter_t filter_fn)
        int ret;
 
        if (filter.cloned) {
-               if (do_ipv6 != AF_INET6) {
+               if (family != AF_INET6) {
                        iproute_flush_cache();
                        if (show_stats)
                                printf("*** IPv4 routing cache is flushed.\n");
                }
-               if (do_ipv6 == AF_INET)
+               if (family == AF_INET)
                        return 0;
        }
 
@@ -1644,7 +1626,7 @@ static int iproute_flush(int do_ipv6, rtnl_filter_t filter_fn)
        filter.flushe = sizeof(flushb);
 
        for (;;) {
-               if (rtnl_routedump_req(&rth, do_ipv6) < 0) {
+               if (rtnl_routedump_req(&rth, family, NULL) < 0) {
                        perror("Cannot send dump request");
                        return -2;
                }
@@ -1656,7 +1638,7 @@ static int iproute_flush(int do_ipv6, rtnl_filter_t filter_fn)
                if (filter.flushed == 0) {
                        if (show_stats) {
                                if (round == 0 &&
-                                   (!filter.cloned || do_ipv6 == AF_INET6))
+                                   (!filter.cloned || family == AF_INET6))
                                        printf("Nothing to flush.\n");
                                else
                                        printf("*** Flush is complete after %d round%s ***\n",
@@ -1684,9 +1666,33 @@ static int iproute_flush(int do_ipv6, rtnl_filter_t filter_fn)
        }
 }
 
+static int iproute_dump_filter(struct nlmsghdr *nlh, int reqlen)
+{
+       struct rtmsg *rtm = NLMSG_DATA(nlh);
+       int err;
+
+       rtm->rtm_protocol = filter.protocol;
+       if (filter.cloned)
+               rtm->rtm_flags |= RTM_F_CLONED;
+
+       if (filter.tb) {
+               err = addattr32(nlh, reqlen, RTA_TABLE, filter.tb);
+               if (err)
+                       return err;
+       }
+
+       if (filter.oif) {
+               err = addattr32(nlh, reqlen, RTA_OIF, filter.oif);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
 static int iproute_list_flush_or_save(int argc, char **argv, int action)
 {
-       int do_ipv6 = preferred_family;
+       int dump_family = preferred_family;
        char *id = NULL;
        char *od = NULL;
        unsigned int mark = 0;
@@ -1805,13 +1811,13 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action)
                        NEXT_ARG();
                        family = read_family(*argv);
                        if (family == AF_UNSPEC)
-                               family = do_ipv6;
+                               family = dump_family;
                        else
                                NEXT_ARG();
                        get_prefix(&filter.rvia, *argv, family);
                } else if (strcmp(*argv, "src") == 0) {
                        NEXT_ARG();
-                       get_prefix(&filter.rprefsrc, *argv, do_ipv6);
+                       get_prefix(&filter.rprefsrc, *argv, dump_family);
                } else if (matches(*argv, "realms") == 0) {
                        __u32 realm;
 
@@ -1831,15 +1837,15 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action)
                        NEXT_ARG();
                        if (matches(*argv, "root") == 0) {
                                NEXT_ARG();
-                               get_prefix(&filter.rsrc, *argv, do_ipv6);
+                               get_prefix(&filter.rsrc, *argv, dump_family);
                        } else if (matches(*argv, "match") == 0) {
                                NEXT_ARG();
-                               get_prefix(&filter.msrc, *argv, do_ipv6);
+                               get_prefix(&filter.msrc, *argv, dump_family);
                        } else {
                                if (matches(*argv, "exact") == 0) {
                                        NEXT_ARG();
                                }
-                               get_prefix(&filter.msrc, *argv, do_ipv6);
+                               get_prefix(&filter.msrc, *argv, dump_family);
                                filter.rsrc = filter.msrc;
                        }
                } else {
@@ -1848,23 +1854,23 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action)
                        }
                        if (matches(*argv, "root") == 0) {
                                NEXT_ARG();
-                               get_prefix(&filter.rdst, *argv, do_ipv6);
+                               get_prefix(&filter.rdst, *argv, dump_family);
                        } else if (matches(*argv, "match") == 0) {
                                NEXT_ARG();
-                               get_prefix(&filter.mdst, *argv, do_ipv6);
+                               get_prefix(&filter.mdst, *argv, dump_family);
                        } else {
                                if (matches(*argv, "exact") == 0) {
                                        NEXT_ARG();
                                }
-                               get_prefix(&filter.mdst, *argv, do_ipv6);
+                               get_prefix(&filter.mdst, *argv, dump_family);
                                filter.rdst = filter.mdst;
                        }
                }
                argc--; argv++;
        }
 
-       if (do_ipv6 == AF_UNSPEC && filter.tb)
-               do_ipv6 = AF_INET;
+       if (dump_family == AF_UNSPEC && filter.tb)
+               dump_family = AF_INET;
 
        if (id || od)  {
                int idx;
@@ -1887,18 +1893,11 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action)
        filter.mark = mark;
 
        if (action == IPROUTE_FLUSH)
-               return iproute_flush(do_ipv6, filter_fn);
+               return iproute_flush(dump_family, filter_fn);
 
-       if (!filter.cloned) {
-               if (rtnl_routedump_req(&rth, do_ipv6) < 0) {
-                       perror("Cannot send dump request");
-                       return -2;
-               }
-       } else {
-               if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
-                       perror("Cannot send dump request");
-                       return -2;
-               }
+       if (rtnl_routedump_req(&rth, dump_family, iproute_dump_filter) < 0) {
+               perror("Cannot send dump request");
+               return -2;
        }
 
        new_json_obj(json);
index 8a6b7f977b1423da8c1ee0df8379d90ba4716cc0..08a0d45b2570f47167b0e55bae55f1776334996a 100644 (file)
@@ -589,7 +589,7 @@ static int ipvrf_show(int argc, char **argv)
                return 0;
        }
 
-       if (ip_linkaddr_list(0, ipvrf_filter_req, &linfo, NULL) == 0) {
+       if (ip_link_list(ipvrf_filter_req, &linfo) == 0) {
                struct nlmsg_list *l;
                unsigned nvrf = 0;
                int n;
index 954571097ea8dd90a1ec3461da84865a4655f1be..4d7d081054fd324c8b92c8bf141b89ab8393db6c 100644 (file)
@@ -28,6 +28,8 @@
 
 #include "libnetlink.h"
 
+#define __aligned(x)           __attribute__((aligned(x)))
+
 #ifndef SOL_NETLINK
 #define SOL_NETLINK 270
 #endif
@@ -67,6 +69,14 @@ static int err_attr_cb(const struct nlattr *attr, void *data)
        return MNL_CB_OK;
 }
 
+static void print_ext_ack_msg(bool is_err, const char *msg)
+{
+       fprintf(stderr, "%s: %s", is_err ? "Error" : "Warning", msg);
+       if (msg[strlen(msg) - 1] != '.')
+               fprintf(stderr, ".");
+       fprintf(stderr, "\n");
+}
+
 /* dump netlink extended ack error message */
 int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
 {
@@ -108,12 +118,29 @@ int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
        if (msg && *msg != '\0') {
                bool is_err = !!err->error;
 
-               fprintf(stderr, "%s: %s",
-                       is_err ? "Error" : "Warning", msg);
-               if (msg[strlen(msg) - 1] != '.')
-                       fprintf(stderr, ".");
-               fprintf(stderr, "\n");
+               print_ext_ack_msg(is_err, msg);
+               return is_err ? 1 : 0;
+       }
+
+       return 0;
+}
+
+static int nl_dump_ext_ack_done(const struct nlmsghdr *nlh, int error)
+{
+       struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
+       unsigned int hlen = sizeof(int);
+       const char *msg = NULL;
+
+       if (mnl_attr_parse(nlh, hlen, err_attr_cb, tb) != MNL_CB_OK)
+               return 0;
+
+       if (tb[NLMSGERR_ATTR_MSG])
+               msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
+
+       if (msg && *msg != '\0') {
+               bool is_err = !!error;
 
+               print_ext_ack_msg(is_err, msg);
                return is_err ? 1 : 0;
        }
 
@@ -127,8 +154,22 @@ int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
 {
        return 0;
 }
+
+static int nl_dump_ext_ack_done(const struct nlmsghdr *nlh, int error)
+{
+       return 0;
+}
 #endif
 
+/* Older kernels may not support strict dump and filtering */
+void rtnl_set_strict_dump(struct rtnl_handle *rth)
+{
+       int one = 1;
+
+       setsockopt(rth->fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK,
+                  &one, sizeof(one));
+}
+
 void rtnl_close(struct rtnl_handle *rth)
 {
        if (rth->fd >= 0) {
@@ -202,19 +243,29 @@ int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions)
        return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
 }
 
-int rtnl_addrdump_req(struct rtnl_handle *rth, int family)
+int rtnl_addrdump_req(struct rtnl_handle *rth, int family,
+                     req_filter_fn_t filter_fn)
 {
        struct {
                struct nlmsghdr nlh;
                struct ifaddrmsg ifm;
+               char buf[128];
        } req = {
-               .nlh.nlmsg_len = sizeof(req),
+               .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
                .nlh.nlmsg_type = RTM_GETADDR,
                .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
                .nlh.nlmsg_seq = rth->dump = ++rth->seq,
                .ifm.ifa_family = family,
        };
 
+       if (filter_fn) {
+               int err;
+
+               err = filter_fn(&req.nlh, sizeof(req));
+               if (err)
+                       return err;
+       }
+
        return send(rth->fd, &req, sizeof(req), 0);
 }
 
@@ -224,7 +275,7 @@ int rtnl_addrlbldump_req(struct rtnl_handle *rth, int family)
                struct nlmsghdr nlh;
                struct ifaddrlblmsg ifal;
        } req = {
-               .nlh.nlmsg_len = sizeof(req),
+               .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrlblmsg)),
                .nlh.nlmsg_type = RTM_GETADDRLABEL,
                .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
                .nlh.nlmsg_seq = rth->dump = ++rth->seq,
@@ -234,19 +285,29 @@ int rtnl_addrlbldump_req(struct rtnl_handle *rth, int family)
        return send(rth->fd, &req, sizeof(req), 0);
 }
 
-int rtnl_routedump_req(struct rtnl_handle *rth, int family)
+int rtnl_routedump_req(struct rtnl_handle *rth, int family,
+                      req_filter_fn_t filter_fn)
 {
        struct {
                struct nlmsghdr nlh;
                struct rtmsg rtm;
+               char buf[128];
        } req = {
-               .nlh.nlmsg_len = sizeof(req),
+               .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
                .nlh.nlmsg_type = RTM_GETROUTE,
                .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
                .nlh.nlmsg_seq = rth->dump = ++rth->seq,
                .rtm.rtm_family = family,
        };
 
+       if (filter_fn) {
+               int err;
+
+               err = filter_fn(&req.nlh, sizeof(req));
+               if (err)
+                       return err;
+       }
+
        return send(rth->fd, &req, sizeof(req), 0);
 }
 
@@ -256,7 +317,7 @@ int rtnl_ruledump_req(struct rtnl_handle *rth, int family)
                struct nlmsghdr nlh;
                struct fib_rule_hdr frh;
        } req = {
-               .nlh.nlmsg_len = sizeof(req),
+               .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct fib_rule_hdr)),
                .nlh.nlmsg_type = RTM_GETRULE,
                .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
                .nlh.nlmsg_seq = rth->dump = ++rth->seq,
@@ -272,7 +333,7 @@ int rtnl_neighdump_req(struct rtnl_handle *rth, int family)
                struct nlmsghdr nlh;
                struct ndmsg ndm;
        } req = {
-               .nlh.nlmsg_len = sizeof(req),
+               .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
                .nlh.nlmsg_type = RTM_GETNEIGH,
                .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
                .nlh.nlmsg_seq = rth->dump = ++rth->seq,
@@ -288,7 +349,7 @@ int rtnl_neightbldump_req(struct rtnl_handle *rth, int family)
                struct nlmsghdr nlh;
                struct ndtmsg ndtmsg;
        } req = {
-               .nlh.nlmsg_len = sizeof(req),
+               .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndtmsg)),
                .nlh.nlmsg_type = RTM_GETNEIGHTBL,
                .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
                .nlh.nlmsg_seq = rth->dump = ++rth->seq,
@@ -304,7 +365,7 @@ int rtnl_mdbdump_req(struct rtnl_handle *rth, int family)
                struct nlmsghdr nlh;
                struct br_port_msg bpm;
        } req = {
-               .nlh.nlmsg_len = sizeof(req),
+               .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
                .nlh.nlmsg_type = RTM_GETMDB,
                .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
                .nlh.nlmsg_seq = rth->dump = ++rth->seq,
@@ -319,8 +380,9 @@ int rtnl_netconfdump_req(struct rtnl_handle *rth, int family)
        struct {
                struct nlmsghdr nlh;
                struct netconfmsg ncm;
+               char buf[0] __aligned(NLMSG_ALIGNTO);
        } req = {
-               .nlh.nlmsg_len = sizeof(req),
+               .nlh.nlmsg_len = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct netconfmsg))),
                .nlh.nlmsg_type = RTM_GETNETCONF,
                .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
                .nlh.nlmsg_seq = rth->dump = ++rth->seq,
@@ -335,8 +397,9 @@ int rtnl_nsiddump_req(struct rtnl_handle *rth, int family)
        struct {
                struct nlmsghdr nlh;
                struct rtgenmsg rtm;
+               char buf[0] __aligned(NLMSG_ALIGNTO);
        } req = {
-               .nlh.nlmsg_len = sizeof(req),
+               .nlh.nlmsg_len = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(struct rtgenmsg))),
                .nlh.nlmsg_type = RTM_GETNSID,
                .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
                .nlh.nlmsg_seq = rth->dump = ++rth->seq,
@@ -346,58 +409,85 @@ int rtnl_nsiddump_req(struct rtnl_handle *rth, int family)
        return send(rth->fd, &req, sizeof(req), 0);
 }
 
-int rtnl_linkdump_req(struct rtnl_handle *rth, int family)
-{
-       return rtnl_linkdump_req_filter(rth, family, RTEXT_FILTER_VF);
-}
-
-int rtnl_linkdump_req_filter(struct rtnl_handle *rth, int family,
-                           __u32 filt_mask)
+static int __rtnl_linkdump_req(struct rtnl_handle *rth, int family)
 {
        struct {
                struct nlmsghdr nlh;
                struct ifinfomsg ifm;
-               /* attribute has to be NLMSG aligned */
-               struct rtattr ext_req __attribute__ ((aligned(NLMSG_ALIGNTO)));
-               __u32 ext_filter_mask;
        } req = {
-               .nlh.nlmsg_len = sizeof(req),
+               .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
                .nlh.nlmsg_type = RTM_GETLINK,
                .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
                .nlh.nlmsg_seq = rth->dump = ++rth->seq,
                .ifm.ifi_family = family,
-               .ext_req.rta_type = IFLA_EXT_MASK,
-               .ext_req.rta_len = RTA_LENGTH(sizeof(__u32)),
-               .ext_filter_mask = filt_mask,
        };
 
        return send(rth->fd, &req, sizeof(req), 0);
 }
 
+int rtnl_linkdump_req(struct rtnl_handle *rth, int family)
+{
+       if (family == AF_UNSPEC)
+               return rtnl_linkdump_req_filter(rth, family, RTEXT_FILTER_VF);
+
+       return __rtnl_linkdump_req(rth, family);
+}
+
+int rtnl_linkdump_req_filter(struct rtnl_handle *rth, int family,
+                           __u32 filt_mask)
+{
+       if (family == AF_UNSPEC) {
+               struct {
+                       struct nlmsghdr nlh;
+                       struct ifinfomsg ifm;
+                       /* attribute has to be NLMSG aligned */
+                       struct rtattr ext_req __aligned(NLMSG_ALIGNTO);
+                       __u32 ext_filter_mask;
+               } req = {
+                       .nlh.nlmsg_len = sizeof(req),
+                       .nlh.nlmsg_type = RTM_GETLINK,
+                       .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+                       .nlh.nlmsg_seq = rth->dump = ++rth->seq,
+                       .ifm.ifi_family = family,
+                       .ext_req.rta_type = IFLA_EXT_MASK,
+                       .ext_req.rta_len = RTA_LENGTH(sizeof(__u32)),
+                       .ext_filter_mask = filt_mask,
+               };
+
+               return send(rth->fd, &req, sizeof(req), 0);
+       }
+
+       return __rtnl_linkdump_req(rth, family);
+}
+
 int rtnl_linkdump_req_filter_fn(struct rtnl_handle *rth, int family,
                                req_filter_fn_t filter_fn)
 {
-       struct {
-               struct nlmsghdr nlh;
-               struct ifinfomsg ifm;
-               char buf[1024];
-       } req = {
-               .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-               .nlh.nlmsg_type = RTM_GETLINK,
-               .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
-               .nlh.nlmsg_seq = rth->dump = ++rth->seq,
-               .ifm.ifi_family = family,
-       };
-       int err;
-
-       if (!filter_fn)
-               return -EINVAL;
+       if (family == AF_UNSPEC) {
+               struct {
+                       struct nlmsghdr nlh;
+                       struct ifinfomsg ifm;
+                       char buf[1024];
+               } req = {
+                       .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+                       .nlh.nlmsg_type = RTM_GETLINK,
+                       .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+                       .nlh.nlmsg_seq = rth->dump = ++rth->seq,
+                       .ifm.ifi_family = family,
+               };
+               int err;
+
+               if (!filter_fn)
+                       return -EINVAL;
+
+               err = filter_fn(&req.nlh, sizeof(req));
+               if (err)
+                       return err;
 
-       err = filter_fn(&req.nlh, sizeof(req));
-       if (err)
-               return err;
+               return send(rth->fd, &req, req.nlh.nlmsg_len, 0);
+       }
 
-       return send(rth->fd, &req, req.nlh.nlmsg_len, 0);
+       return __rtnl_linkdump_req(rth, family);
 }
 
 int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask)
@@ -512,6 +602,10 @@ static int rtnl_dump_done(struct nlmsghdr *h)
        }
 
        if (len < 0) {
+               /* check for any messages returned from kernel */
+               if (nl_dump_ext_ack_done(h, len))
+                       return len;
+
                errno = -len;
                switch (errno) {
                case ENOENT: