This patch adds a `nl_collect` function to store all datagram chunks of a multipart route dump in one single interim buffer for further processing and dependency resolution. --- netlink.c | 79 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 23 deletions(-) diff --git a/netlink.c b/netlink.c index 225b666..650a6fd 100644 --- a/netlink.c +++ b/netlink.c @@ -229,6 +229,51 @@ static struct nlmsghdr *nl_next(int s, char *buf, struct nlmsghdr *nh, ssize_t * /* NOLINTNEXTLINE(readability-inconsistent-ifelse-braces) */\ } else +/** + * nl_collect() - Collect all respose Datagrams of a netlink multi part dump + * @s: Netlink socket + * @buf: Buffer for responses (at least NLBUFSIZ long) + * @buflen: Buffer size. + * @seq: Sequence number of request we're getting responses for + * + * Return: Length of collected responses in bytes or error code < 0 + */ +int nl_collect(int s, char *buf, size_t buflen, uint32_t seq) +{ + size_t collected = 0; + struct nlmsghdr *nh; + ssize_t n; + int res; + const struct nlmsghdr* tail; + + #define NLMSG_DONE_SIZE NLMSG_LENGTH(4) + + while(collected + NLBUFSIZ < buflen){ + nh = nl_next(s, &buf[collected], NULL, &n); + debug("collect: add chunk of size n=%ld at offset=%ld, seq=%d nlmsg_len=%d", + n, collected, seq, ((const struct nlmsghdr *)&buf[collected])->nlmsg_len); + res = nl_status(nh, n, seq); + if (res < 0) /* error */ + return res; + collected += n; + + /* look for NLMSG_DONE -- it's ussualy transmitted in a seperate datagram, + * but in some rare cases also placed at the end of a multipart chunk.*/ + tail = (const struct nlmsghdr*)&buf[collected - NLMSG_DONE_SIZE]; + if (res == 0 || ( + collected >= NLMSG_DONE_SIZE + && (tail = (const struct nlmsghdr*)&buf[collected - NLMSG_DONE_SIZE]) + && NLMSG_OK(tail, NLMSG_DONE_SIZE) + && (nl_status(tail, NLMSG_DONE_SIZE, seq) == 0)) + ) { + debug("collected: %ld", collected); + return collected; + } + } + err("netlink: Too many routes to duplicate"); + return -E2BIG; +} + /** * nl_do() - Send netlink "do" request, and wait for acknowledgement * @s: Netlink socket @@ -553,21 +598,21 @@ int nl_route_dup(int s_src, unsigned int ifi_src, .rta.rta_len = RTA_LENGTH(sizeof(unsigned int)), .ifi = ifi_src, }; - ssize_t nlmsgs_size, left, status; + ssize_t nlmsgs_size, left, status = 0; unsigned dup_routes = 0; struct nlmsghdr *nh; - char buf[NLBUFSIZ]; + char buf[NLBUFSIZ * 8]; uint32_t seq; unsigned i; seq = nl_send(s_src, &req, RTM_GETROUTE, NLM_F_DUMP, sizeof(req)); - /* nl_foreach() will step through multiple response datagrams, - * which we don't want here because we need to have all the - * routes in the buffer at once. - */ - nh = nl_next(s_src, buf, NULL, &nlmsgs_size); - for (left = nlmsgs_size; + /* collect all the chunks of a multi part dump in one buffer.*/ + nlmsgs_size = nl_collect(s_src, buf, sizeof(buf), seq); + if (nlmsgs_size < 0) + return nlmsgs_size; + + for (nh = (struct nlmsghdr *)buf, left = nlmsgs_size; NLMSG_OK(nh, left) && (status = nl_status(nh, left, seq)) > 0; nh = NLMSG_NEXT(nh, left)) { struct rtmsg *rtm = (struct rtmsg *)NLMSG_DATA(nh); @@ -648,24 +693,12 @@ int nl_route_dup(int s_src, unsigned int ifi_src, dup_routes++; } - if (!NLMSG_OK(nh, left)) { - /* Process any remaining datagrams in a different - * buffer so we don't overwrite the first one. - */ - char tail[NLBUFSIZ]; - unsigned extra = 0; - - nl_foreach_oftype(nh, status, s_src, tail, seq, RTM_NEWROUTE) - extra++; - - if (extra) { - err("netlink: Too many routes to duplicate"); - return -E2BIG; - } - } + /* status should be 0 (=NLMSG_DONE) */ if (status < 0) return status; + debug("found %d routes in dump", dup_routes); + /* Routes might have dependencies between each other, and the kernel * processes RTM_NEWROUTE messages sequentially. For n routes, we might * need to send the requests up to n times to get all of them inserted. -- 2.53.0