]> git.proxmox.com Git - mirror_iproute2.git/blobdiff - lib/libnetlink.c
vdpa: add .gitignore
[mirror_iproute2.git] / lib / libnetlink.c
index 4618dc0e3247051c3701eae28d4a2a69541295e9..c958aa57d0cd3c5d5beffa882631ac622dec4317 100644 (file)
@@ -14,7 +14,6 @@
 #include <stdlib.h>
 #include <stdbool.h>
 #include <unistd.h>
-#include <syslog.h>
 #include <fcntl.h>
 #include <net/if_arp.h>
 #include <sys/socket.h>
 #include <errno.h>
 #include <time.h>
 #include <sys/uio.h>
+#include <linux/fib_rules.h>
+#include <linux/if_addrlabel.h>
+#include <linux/if_bridge.h>
+#include <linux/nexthop.h>
 
 #include "libnetlink.h"
+#include "utils.h"
+
+#ifndef __aligned
+#define __aligned(x)           __attribute__((aligned(x)))
+#endif
 
 #ifndef SOL_NETLINK
 #define SOL_NETLINK 270
@@ -65,14 +73,22 @@ 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 */
-static int nl_dump_ext_err(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
+int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
 {
        struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
        const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
        const struct nlmsghdr *err_nlh = NULL;
        unsigned int hlen = sizeof(*err);
-       const char *errmsg = NULL;
+       const char *msg = NULL;
        uint32_t off = 0;
 
        /* no TLVs, nothing to do here */
@@ -87,7 +103,7 @@ static int nl_dump_ext_err(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
                return 0;
 
        if (tb[NLMSGERR_ATTR_MSG])
-               errmsg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
+               msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
 
        if (tb[NLMSGERR_ATTR_OFFS]) {
                off = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
@@ -101,15 +117,35 @@ static int nl_dump_ext_err(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
        }
 
        if (errfn)
-               return errfn(errmsg, off, err_nlh);
+               return errfn(msg, off, err_nlh);
+
+       if (msg && *msg != '\0') {
+               bool is_err = !!err->error;
+
+               print_ext_ack_msg(is_err, msg);
+               return is_err ? 1 : 0;
+       }
+
+       return 0;
+}
+
+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 (errmsg && *errmsg != '\0') {
-               fprintf(stderr, "Error: %s", errmsg);
-               if (errmsg[strlen(errmsg) - 1] != '.')
-                       fprintf(stderr, ".");
-               fprintf(stderr, "\n");
+       if (tb[NLMSGERR_ATTR_MSG])
+               msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
+
+       if (msg && *msg != '\0') {
+               bool is_err = !!error;
 
-               return 1;
+               print_ext_ack_msg(is_err, msg);
+               return is_err ? 1 : 0;
        }
 
        return 0;
@@ -118,12 +154,35 @@ static int nl_dump_ext_err(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
 #warning "libmnl required for error support"
 
 /* No extended error ack without libmnl */
-static int nl_dump_ext_err(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
+int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
+{
+       return 0;
+}
+
+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;
+
+       if (setsockopt(rth->fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK,
+                      &one, sizeof(one)) < 0)
+               return;
+
+       rth->flags |= RTNL_HANDLE_F_STRICT_CHK;
+}
+
+int rtnl_add_nl_group(struct rtnl_handle *rth, unsigned int group)
+{
+       return setsockopt(rth->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
+                         &group, sizeof(group));
+}
+
 void rtnl_close(struct rtnl_handle *rth)
 {
        if (rth->fd >= 0) {
@@ -197,47 +256,204 @@ int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions)
        return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
 }
 
-int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+int rtnl_nexthopdump_req(struct rtnl_handle *rth, int family,
+                        req_filter_fn_t filter_fn)
 {
-       return rtnl_wilddump_req_filter(rth, family, type, RTEXT_FILTER_VF);
+       struct {
+               struct nlmsghdr nlh;
+               struct nhmsg nhm;
+               char buf[128];
+       } req = {
+               .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
+               .nlh.nlmsg_type = RTM_GETNEXTHOP,
+               .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+               .nlh.nlmsg_seq = rth->dump = ++rth->seq,
+               .nhm.nh_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);
 }
 
-int rtnl_wilddump_req_filter(struct rtnl_handle *rth, int family, int type,
-                           __u32 filt_mask)
+int rtnl_addrdump_req(struct rtnl_handle *rth, int family,
+                     req_filter_fn_t filter_fn)
 {
        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;
+               struct ifaddrmsg ifm;
+               char buf[128];
        } req = {
-               .nlh.nlmsg_len = sizeof(req),
-               .nlh.nlmsg_type = type,
+               .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.ifi_family = family,
-               .ext_req.rta_type = IFLA_EXT_MASK,
-               .ext_req.rta_len = RTA_LENGTH(sizeof(__u32)),
-               .ext_filter_mask = filt_mask,
+               .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);
+}
+
+int rtnl_addrlbldump_req(struct rtnl_handle *rth, int family)
+{
+       struct {
+               struct nlmsghdr nlh;
+               struct ifaddrlblmsg ifal;
+       } 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,
+               .ifal.ifal_family = family,
+       };
+
+       return send(rth->fd, &req, sizeof(req), 0);
+}
+
+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 = 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);
+}
+
+int rtnl_ruledump_req(struct rtnl_handle *rth, int family)
+{
+       struct {
+               struct nlmsghdr nlh;
+               struct fib_rule_hdr frh;
+       } 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,
+               .frh.family = family
+       };
+
+       return send(rth->fd, &req, sizeof(req), 0);
+}
+
+int rtnl_neighdump_req(struct rtnl_handle *rth, int family,
+                      req_filter_fn_t filter_fn)
+{
+       struct {
+               struct nlmsghdr nlh;
+               struct ndmsg ndm;
+               char buf[256];
+       } 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,
+               .ndm.ndm_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);
+}
+
+int rtnl_neightbldump_req(struct rtnl_handle *rth, int family)
+{
+       struct {
+               struct nlmsghdr nlh;
+               struct ndtmsg ndtmsg;
+       } 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,
+               .ndtmsg.ndtm_family = family,
+       };
+
+       return send(rth->fd, &req, sizeof(req), 0);
+}
+
+int rtnl_mdbdump_req(struct rtnl_handle *rth, int family)
+{
+       struct {
+               struct nlmsghdr nlh;
+               struct br_port_msg bpm;
+       } 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,
+               .bpm.family = family,
+       };
+
+       return send(rth->fd, &req, sizeof(req), 0);
+}
+
+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 = 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,
+               .ncm.ncm_family = family,
        };
 
        return send(rth->fd, &req, sizeof(req), 0);
 }
 
-int rtnl_wilddump_req_filter_fn(struct rtnl_handle *rth, int family, int type,
+int rtnl_nsiddump_req_filter_fn(struct rtnl_handle *rth, int family,
                                req_filter_fn_t filter_fn)
 {
        struct {
                struct nlmsghdr nlh;
-               struct ifinfomsg ifm;
+               struct rtgenmsg rtm;
                char buf[1024];
        } req = {
-               .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
-               .nlh.nlmsg_type = type,
+               .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,
-               .ifm.ifi_family = family,
+               .rtm.rtgen_family = family,
        };
        int err;
 
@@ -251,8 +467,111 @@ int rtnl_wilddump_req_filter_fn(struct rtnl_handle *rth, int family, int type,
        return send(rth->fd, &req, req.nlh.nlmsg_len, 0);
 }
 
-int rtnl_wilddump_stats_req_filter(struct rtnl_handle *rth, int fam, int type,
-                                  __u32 filt_mask)
+static int __rtnl_linkdump_req(struct rtnl_handle *rth, int family)
+{
+       struct {
+               struct nlmsghdr nlh;
+               struct ifinfomsg ifm;
+       } 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,
+       };
+
+       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 || family == AF_BRIDGE) {
+               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)
+{
+       if (family == AF_UNSPEC || family == AF_PACKET) {
+               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;
+
+               return send(rth->fd, &req, req.nlh.nlmsg_len, 0);
+       }
+
+       return __rtnl_linkdump_req(rth, family);
+}
+
+int rtnl_fdb_linkdump_req_filter_fn(struct rtnl_handle *rth,
+                                   req_filter_fn_t filter_fn)
+{
+       struct {
+               struct nlmsghdr nlh;
+               struct ifinfomsg ifm;
+               char buf[128];
+       } req = {
+               .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+               .nlh.nlmsg_type = RTM_GETNEIGH,
+               .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+               .nlh.nlmsg_seq = rth->dump = ++rth->seq,
+               .ifm.ifi_family = PF_BRIDGE,
+       };
+       int err;
+
+       err = filter_fn(&req.nlh, sizeof(req));
+       if (err)
+               return err;
+
+       return send(rth->fd, &req, sizeof(req), 0);
+}
+
+int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask)
 {
        struct {
                struct nlmsghdr nlh;
@@ -261,7 +580,7 @@ int rtnl_wilddump_stats_req_filter(struct rtnl_handle *rth, int fam, int type,
 
        memset(&req, 0, sizeof(req));
        req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct if_stats_msg));
-       req.nlh.nlmsg_type = type;
+       req.nlh.nlmsg_type = RTM_GETSTATS;
        req.nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST;
        req.nlh.nlmsg_pid = 0;
        req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
@@ -364,6 +683,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:
@@ -379,6 +702,9 @@ static int rtnl_dump_done(struct nlmsghdr *h)
                return len;
        }
 
+       /* check for any messages returned from kernel */
+       nl_dump_ext_ack(h, NULL);
+
        return 0;
 }
 
@@ -437,6 +763,8 @@ static int rtnl_recvmsg(int fd, struct msghdr *msg, char **answer)
        if (len < 0)
                return len;
 
+       if (len < 32768)
+               len = 32768;
        buf = malloc(len);
        if (!buf) {
                fprintf(stderr, "malloc error: not enough buffer\n");
@@ -460,8 +788,8 @@ static int rtnl_recvmsg(int fd, struct msghdr *msg, char **answer)
        return len;
 }
 
-int rtnl_dump_filter_l(struct rtnl_handle *rth,
-                      const struct rtnl_dump_filter_arg *arg)
+static int rtnl_dump_filter_l(struct rtnl_handle *rth,
+                             const struct rtnl_dump_filter_arg *arg)
 {
        struct sockaddr_nl nladdr;
        struct iovec iov;
@@ -523,7 +851,7 @@ int rtnl_dump_filter_l(struct rtnl_handle *rth,
                                }
 
                                if (!rth->dump_fp) {
-                                       err = a->filter(&nladdr, h, a->arg1);
+                                       err = a->filter(h, a->arg1);
                                        if (err < 0) {
                                                free(buf);
                                                return err;
@@ -569,37 +897,37 @@ int rtnl_dump_filter_nc(struct rtnl_handle *rth,
 static void rtnl_talk_error(struct nlmsghdr *h, struct nlmsgerr *err,
                            nl_ext_ack_fn_t errfn)
 {
-       if (nl_dump_ext_err(h, errfn))
+       if (nl_dump_ext_ack(h, errfn))
                return;
 
        fprintf(stderr, "RTNETLINK answers: %s\n",
                strerror(-err->error));
 }
 
-static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
-                      struct nlmsghdr **answer,
-                      bool show_rtnl_err, nl_ext_ack_fn_t errfn)
+
+static int __rtnl_talk_iov(struct rtnl_handle *rtnl, struct iovec *iov,
+                          size_t iovlen, struct nlmsghdr **answer,
+                          bool show_rtnl_err, nl_ext_ack_fn_t errfn)
 {
-       int status;
-       unsigned int seq;
-       struct nlmsghdr *h;
        struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
-       struct iovec iov = {
-               .iov_base = n,
-               .iov_len = n->nlmsg_len
-       };
+       struct iovec riov;
        struct msghdr msg = {
                .msg_name = &nladdr,
                .msg_namelen = sizeof(nladdr),
-               .msg_iov = &iov,
-               .msg_iovlen = 1,
+               .msg_iov = iov,
+               .msg_iovlen = iovlen,
        };
+       unsigned int seq = 0;
+       struct nlmsghdr *h;
+       int i, status;
        char *buf;
 
-       n->nlmsg_seq = seq = ++rtnl->seq;
-
-       if (answer == NULL)
-               n->nlmsg_flags |= NLM_F_ACK;
+       for (i = 0; i < iovlen; i++) {
+               h = iov[i].iov_base;
+               h->nlmsg_seq = seq = ++rtnl->seq;
+               if (answer == NULL)
+                       h->nlmsg_flags |= NLM_F_ACK;
+       }
 
        status = sendmsg(rtnl->fd, &msg, 0);
        if (status < 0) {
@@ -607,8 +935,14 @@ static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
                return -1;
        }
 
+       /* change msg to use the response iov */
+       msg.msg_iov = &riov;
+       msg.msg_iovlen = 1;
+       i = 0;
        while (1) {
+next:
                status = rtnl_recvmsg(rtnl->fd, &msg, &buf);
+               ++i;
 
                if (status < 0)
                        return status;
@@ -637,7 +971,7 @@ static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
 
                        if (nladdr.nl_pid != 0 ||
                            h->nlmsg_pid != rtnl->local.nl_pid ||
-                           h->nlmsg_seq != seq) {
+                           h->nlmsg_seq > seq || h->nlmsg_seq < seq - iovlen) {
                                /* Don't forget to skip that message. */
                                status -= NLMSG_ALIGN(len);
                                h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
@@ -646,24 +980,33 @@ static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
 
                        if (h->nlmsg_type == NLMSG_ERROR) {
                                struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
+                               int error = err->error;
 
                                if (l < sizeof(struct nlmsgerr)) {
                                        fprintf(stderr, "ERROR truncated\n");
-                               } else if (!err->error) {
-                                       if (answer)
-                                               *answer = (struct nlmsghdr *)buf;
-                                       else
-                                               free(buf);
-                                       return 0;
+                                       free(buf);
+                                       return -1;
                                }
 
-                               if (rtnl->proto != NETLINK_SOCK_DIAG &&
-                                   show_rtnl_err)
-                                       rtnl_talk_error(h, err, errfn);
+                               if (!error) {
+                                       /* check messages from kernel */
+                                       nl_dump_ext_ack(h, errfn);
+                               } else {
+                                       errno = -error;
 
-                               errno = -err->error;
-                               free(buf);
-                               return -1;
+                                       if (rtnl->proto != NETLINK_SOCK_DIAG &&
+                                           show_rtnl_err)
+                                               rtnl_talk_error(h, err, errfn);
+                               }
+
+                               if (answer)
+                                       *answer = (struct nlmsghdr *)buf;
+                               else
+                                       free(buf);
+
+                               if (i < iovlen)
+                                       goto next;
+                               return error ? -i : 0;
                        }
 
                        if (answer) {
@@ -690,17 +1033,28 @@ static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
        }
 }
 
+static int __rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
+                      struct nlmsghdr **answer,
+                      bool show_rtnl_err, nl_ext_ack_fn_t errfn)
+{
+       struct iovec iov = {
+               .iov_base = n,
+               .iov_len = n->nlmsg_len
+       };
+
+       return __rtnl_talk_iov(rtnl, &iov, 1, answer, show_rtnl_err, errfn);
+}
+
 int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
              struct nlmsghdr **answer)
 {
        return __rtnl_talk(rtnl, n, answer, true, NULL);
 }
 
-int rtnl_talk_extack(struct rtnl_handle *rtnl, struct nlmsghdr *n,
-                    struct nlmsghdr **answer,
-                    nl_ext_ack_fn_t errfn)
+int rtnl_talk_iov(struct rtnl_handle *rtnl, struct iovec *iovec, size_t iovlen,
+                 struct nlmsghdr **answer)
 {
-       return __rtnl_talk(rtnl, n, answer, true, errfn);
+       return __rtnl_talk_iov(rtnl, iovec, iovlen, answer, true, NULL);
 }
 
 int rtnl_talk_suppress_rtnl_errmsg(struct rtnl_handle *rtnl, struct nlmsghdr *n,
@@ -802,7 +1156,7 @@ int rtnl_listen(struct rtnl_handle *rtnl,
                                exit(1);
                        }
 
-                       err = handler(&nladdr, &ctrl, h, jarg);
+                       err = handler(&ctrl, h, jarg);
                        if (err < 0)
                                return err;
 
@@ -823,8 +1177,7 @@ int rtnl_listen(struct rtnl_handle *rtnl,
 int rtnl_from_file(FILE *rtnl, rtnl_listen_filter_t handler,
                   void *jarg)
 {
-       int status;
-       struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
+       size_t status;
        char buf[16384];
        struct nlmsghdr *h = (struct nlmsghdr *)buf;
 
@@ -834,14 +1187,15 @@ int rtnl_from_file(FILE *rtnl, rtnl_listen_filter_t handler,
 
                status = fread(&buf, 1, sizeof(*h), rtnl);
 
-               if (status < 0) {
-                       if (errno == EINTR)
-                               continue;
-                       perror("rtnl_from_file: fread");
+               if (status == 0 && feof(rtnl))
+                       return 0;
+               if (status != sizeof(*h)) {
+                       if (ferror(rtnl))
+                               perror("rtnl_from_file: fread");
+                       if (feof(rtnl))
+                               fprintf(stderr, "rtnl-from_file: truncated message\n");
                        return -1;
                }
-               if (status == 0)
-                       return 0;
 
                len = h->nlmsg_len;
                l = len - sizeof(*h);
@@ -854,16 +1208,15 @@ int rtnl_from_file(FILE *rtnl, rtnl_listen_filter_t handler,
 
                status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
 
-               if (status < 0) {
-                       perror("rtnl_from_file: fread");
-                       return -1;
-               }
-               if (status < l) {
-                       fprintf(stderr, "rtnl-from_file: truncated message\n");
+               if (status != NLMSG_ALIGN(l)) {
+                       if (ferror(rtnl))
+                               perror("rtnl_from_file: fread");
+                       if (feof(rtnl))
+                               fprintf(stderr, "rtnl-from_file: truncated message\n");
                        return -1;
                }
 
-               err = handler(&nladdr, NULL, h, jarg);
+               err = handler(NULL, h, jarg);
                if (err < 0)
                        return err;
        }
@@ -1028,6 +1381,7 @@ struct rtattr *rta_nest(struct rtattr *rta, int maxlen, int type)
        struct rtattr *nest = RTA_TAIL(rta);
 
        rta_addattr_l(rta, maxlen, type, NULL, 0);
+       nest->rta_type |= NLA_F_NESTED;
 
        return nest;
 }
@@ -1062,23 +1416,6 @@ int parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta,
        return 0;
 }
 
-int parse_rtattr_byindex(struct rtattr *tb[], int max,
-                        struct rtattr *rta, int len)
-{
-       int i = 0;
-
-       memset(tb, 0, sizeof(struct rtattr *) * max);
-       while (RTA_OK(rta, len)) {
-               if (rta->rta_type <= max && i < max)
-                       tb[i++] = rta;
-               rta = RTA_NEXT(rta, len);
-       }
-       if (len)
-               fprintf(stderr, "!!!Deficit %d, rta_len=%d\n",
-                       len, rta->rta_len);
-       return i;
-}
-
 struct rtattr *parse_rtattr_one(int type, struct rtattr *rta, int len)
 {
        while (RTA_OK(rta, len)) {
@@ -1106,3 +1443,75 @@ int __parse_rtattr_nested_compat(struct rtattr *tb[], int max,
        memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
        return 0;
 }
+
+static const char *get_nla_type_str(unsigned int attr)
+{
+       switch (attr) {
+#define C(x) case NL_ATTR_TYPE_ ## x: return #x
+       C(U8);
+       C(U16);
+       C(U32);
+       C(U64);
+       C(STRING);
+       C(FLAG);
+       C(NESTED);
+       C(NESTED_ARRAY);
+       C(NUL_STRING);
+       C(BINARY);
+       C(S8);
+       C(S16);
+       C(S32);
+       C(S64);
+       C(BITFIELD32);
+       default:
+               return "unknown";
+       }
+}
+
+void nl_print_policy(const struct rtattr *attr, FILE *fp)
+{
+       const struct rtattr *pos;
+
+       rtattr_for_each_nested(pos, attr) {
+               const struct rtattr *attr;
+
+               fprintf(fp, " policy[%u]:", pos->rta_type & ~NLA_F_NESTED);
+
+               rtattr_for_each_nested(attr, pos) {
+                       struct rtattr *tp[NL_POLICY_TYPE_ATTR_MAX + 1];
+
+                       parse_rtattr_nested(tp, ARRAY_SIZE(tp) - 1, attr);
+
+                       if (tp[NL_POLICY_TYPE_ATTR_TYPE])
+                               fprintf(fp, "attr[%u]: type=%s",
+                                       attr->rta_type & ~NLA_F_NESTED,
+                                       get_nla_type_str(rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_TYPE])));
+
+                       if (tp[NL_POLICY_TYPE_ATTR_POLICY_IDX])
+                               fprintf(fp, " policy:%u",
+                                       rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_POLICY_IDX]));
+
+                       if (tp[NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE])
+                               fprintf(fp, " maxattr:%u",
+                                       rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE]));
+
+                       if (tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_S] && tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_S])
+                               fprintf(fp, " range:[%lld,%lld]",
+                                       (signed long long)rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_S]),
+                                       (signed long long)rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_S]));
+
+                       if (tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_U] && tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_U])
+                               fprintf(fp, " range:[%llu,%llu]",
+                                       (unsigned long long)rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_U]),
+                                       (unsigned long long)rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_U]));
+
+                       if (tp[NL_POLICY_TYPE_ATTR_MIN_LENGTH])
+                               fprintf(fp, " min len:%u",
+                                       rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_MIN_LENGTH]));
+
+                       if (tp[NL_POLICY_TYPE_ATTR_MAX_LENGTH])
+                               fprintf(fp, " max len:%u",
+                                       rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_MAX_LENGTH]));
+               }
+       }
+}