diff --git a/bridge/bridge.c b/bridge/bridge.c index a3d8154b..a50d9d59 100644 --- a/bridge/bridge.c +++ b/bridge/bridge.c @@ -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); diff --git a/include/libnetlink.h b/include/libnetlink.h index 138840d5..dc0c9c4e 100644 --- a/include/libnetlink.h +++ b/include/libnetlink.h @@ -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 a5bbacb4..e4131714 100644 --- 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); diff --git a/ip/ip_common.h b/ip/ip_common.h index 53668f59..d67575c6 100644 --- a/ip/ip_common.h +++ b/ip/ip_common.h @@ -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) diff --git a/ip/ipaddress.c b/ip/ipaddress.c index 016662e9..2bc33f3a 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -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.oneline) - no_link = 1; + 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 (ip_linkaddr_list(filter.family, iplink_filter_req, - &linfo, ainfo) != 0) - goto out; + if (filter.family != AF_PACKET) { + if (filter.oneline) + no_link = 1; + + 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; diff --git a/ip/ipmroute.c b/ip/ipmroute.c index 4d8867d3..b29c78e4 100644 --- a/ip/ipmroute.c +++ b/ip/ipmroute.c @@ -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; } diff --git a/ip/ipneigh.c b/ip/ipneigh.c index 6041c467..26ac2d1b 100644 --- a/ip/ipneigh.c +++ b/ip/ipneigh.c @@ -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(); } diff --git a/ip/iproute.c b/ip/iproute.c index d3472469..0440366e 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -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); diff --git a/ip/ipvrf.c b/ip/ipvrf.c index 8a6b7f97..08a0d45b 100644 --- a/ip/ipvrf.c +++ b/ip/ipvrf.c @@ -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; diff --git a/lib/libnetlink.c b/lib/libnetlink.c index 95457109..4d7d0810 100644 --- a/lib/libnetlink.c +++ b/lib/libnetlink.c @@ -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,41 +409,11 @@ 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_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_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, @@ -388,16 +421,73 @@ int rtnl_linkdump_req_filter_fn(struct rtnl_handle *rth, int family, .nlh.nlmsg_seq = rth->dump = ++rth->seq, .ifm.ifi_family = family, }; - int err; - if (!filter_fn) - return -EINVAL; + return send(rth->fd, &req, sizeof(req), 0); +} - err = filter_fn(&req.nlh, sizeof(req)); - if (err) - return err; +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 send(rth->fd, &req, req.nlh.nlmsg_len, 0); + 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) +{ + 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; + + 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: