diff --git a/include/libnetlink.h b/include/libnetlink.h index 6bff6bae..9e4cc101 100644 --- a/include/libnetlink.h +++ b/include/libnetlink.h @@ -109,6 +109,27 @@ struct rtnl_ctrl_data { typedef int (*rtnl_filter_t)(struct nlmsghdr *n, void *); +/** + * rtnl error handler called from + * rtnl_dump_done() + * rtnl_dump_error() + * + * Return value is a bitmask of the following values: + * RTNL_LET_NLERR + * error handled as usual + * RTNL_SUPPRESS_NLMSG_DONE_NLERR + * error in nlmsg_type == NLMSG_DONE will be suppressed + * RTNL_SUPPRESS_NLMSG_ERROR_NLERR + * error in nlmsg_type == NLMSG_ERROR will be suppressed + * and nlmsg will be skipped + * RTNL_SUPPRESS_NLERR - suppress error in both previous cases + */ +#define RTNL_LET_NLERR 0x01 +#define RTNL_SUPPRESS_NLMSG_DONE_NLERR 0x02 +#define RTNL_SUPPRESS_NLMSG_ERROR_NLERR 0x04 +#define RTNL_SUPPRESS_NLERR 0x06 +typedef int (*rtnl_err_hndlr_t)(struct nlmsghdr *n, void *); + typedef int (*rtnl_listen_filter_t)(struct rtnl_ctrl_data *, struct nlmsghdr *n, void *); @@ -118,6 +139,8 @@ typedef int (*nl_ext_ack_fn_t)(const char *errmsg, uint32_t off, struct rtnl_dump_filter_arg { rtnl_filter_t filter; void *arg1; + rtnl_err_hndlr_t errhndlr; + void *arg2; __u16 nc_flags; }; @@ -126,6 +149,15 @@ int rtnl_dump_filter_nc(struct rtnl_handle *rth, void *arg, __u16 nc_flags); #define rtnl_dump_filter(rth, filter, arg) \ rtnl_dump_filter_nc(rth, filter, arg, 0) +int rtnl_dump_filter_errhndlr_nc(struct rtnl_handle *rth, + rtnl_filter_t filter, + void *arg1, + rtnl_err_hndlr_t errhndlr, + void *arg2, + __u16 nc_flags); +#define rtnl_dump_filter_errhndlr(rth, filter, farg, errhndlr, earg) \ + rtnl_dump_filter_errhndlr_nc(rth, filter, farg, errhndlr, earg, 0) + int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, struct nlmsghdr **answer) __attribute__((warn_unused_result)); diff --git a/ip/iproute.c b/ip/iproute.c index bdeb9644..1ccf51a5 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -1734,6 +1734,18 @@ static int iproute_flush(int family, rtnl_filter_t filter_fn) } } +static int save_route_errhndlr(struct nlmsghdr *n, void *arg) +{ + int err = -*(int *)NLMSG_DATA(n); + + if (n->nlmsg_type == NLMSG_DONE && + filter.tb == RT_TABLE_MAIN && + err == ENOENT) + return RTNL_SUPPRESS_NLMSG_DONE_NLERR; + + return RTNL_LET_NLERR; +} + static int iproute_list_flush_or_save(int argc, char **argv, int action) { int dump_family = preferred_family; @@ -1946,7 +1958,8 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) new_json_obj(json); - if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) { + if (rtnl_dump_filter_errhndlr(&rth, filter_fn, stdout, + save_route_errhndlr, NULL) < 0) { fprintf(stderr, "Dump terminated\n"); return -2; } diff --git a/lib/libnetlink.c b/lib/libnetlink.c index 2f2cc1fe..b92f10e1 100644 --- a/lib/libnetlink.c +++ b/lib/libnetlink.c @@ -718,7 +718,8 @@ int rtnl_dump_request_n(struct rtnl_handle *rth, struct nlmsghdr *n) return sendmsg(rth->fd, &msg, 0); } -static int rtnl_dump_done(struct nlmsghdr *h) +static int rtnl_dump_done(struct nlmsghdr *h, + const struct rtnl_dump_filter_arg *a) { int len = *(int *)NLMSG_DATA(h); @@ -728,11 +729,15 @@ static int rtnl_dump_done(struct nlmsghdr *h) } if (len < 0) { + errno = -len; + + if (a->errhndlr(h, a->arg2) & RTNL_SUPPRESS_NLMSG_DONE_NLERR) + return 0; + /* check for any messages returned from kernel */ if (nl_dump_ext_ack_done(h, len)) return len; - errno = -len; switch (errno) { case ENOENT: case EOPNOTSUPP: @@ -753,8 +758,9 @@ static int rtnl_dump_done(struct nlmsghdr *h) return 0; } -static void rtnl_dump_error(const struct rtnl_handle *rth, - struct nlmsghdr *h) +static int rtnl_dump_error(const struct rtnl_handle *rth, + struct nlmsghdr *h, + const struct rtnl_dump_filter_arg *a) { if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { @@ -766,11 +772,16 @@ static void rtnl_dump_error(const struct rtnl_handle *rth, if (rth->proto == NETLINK_SOCK_DIAG && (errno == ENOENT || errno == EOPNOTSUPP)) - return; + return -1; + + if (a->errhndlr(h, a->arg2) & RTNL_SUPPRESS_NLMSG_ERROR_NLERR) + return 0; if (!(rth->flags & RTNL_HANDLE_F_SUPPRESS_NLERR)) perror("RTNETLINK answers"); } + + return -1; } static int __rtnl_recvmsg(int fd, struct msghdr *msg, int flags) @@ -879,7 +890,7 @@ static int rtnl_dump_filter_l(struct rtnl_handle *rth, dump_intr = 1; if (h->nlmsg_type == NLMSG_DONE) { - err = rtnl_dump_done(h); + err = rtnl_dump_done(h, a); if (err < 0) { free(buf); return -1; @@ -890,9 +901,13 @@ static int rtnl_dump_filter_l(struct rtnl_handle *rth, } if (h->nlmsg_type == NLMSG_ERROR) { - rtnl_dump_error(rth, h); - free(buf); - return -1; + err = rtnl_dump_error(rth, h, a); + if (err < 0) { + free(buf); + return -1; + } + + goto skip_it; } if (!rth->dump_fp) { @@ -932,8 +947,25 @@ int rtnl_dump_filter_nc(struct rtnl_handle *rth, void *arg1, __u16 nc_flags) { const struct rtnl_dump_filter_arg a[2] = { - { .filter = filter, .arg1 = arg1, .nc_flags = nc_flags, }, - { .filter = NULL, .arg1 = NULL, .nc_flags = 0, }, + { .filter = filter, .arg1 = arg1, + .errhndlr = NULL, .arg2 = NULL, .nc_flags = nc_flags, }, + { }, + }; + + return rtnl_dump_filter_l(rth, a); +} + +int rtnl_dump_filter_errhndlr_nc(struct rtnl_handle *rth, + rtnl_filter_t filter, + void *arg1, + rtnl_err_hndlr_t errhndlr, + void *arg2, + __u16 nc_flags) +{ + const struct rtnl_dump_filter_arg a[2] = { + { .filter = filter, .arg1 = arg1, + .errhndlr = errhndlr, .arg2 = arg2, .nc_flags = nc_flags, }, + { }, }; return rtnl_dump_filter_l(rth, a);