From 3bc1c4f29777171b484d36abf673667e3729202b Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Mon, 16 Aug 2010 10:00:08 -0700 Subject: [PATCH 1/3] iproute2: Fix filtering related to flushing IP addresses. The old 'ip addr flush' logic had several flaws: * It reversed logic for primary v/s secondary flags (though, it sort of worked right anyway) * The code tried to remove secondaries and then primaries, but in practice, it always removed one primary per loop, which not at all efficient. * The filter logic in the core would run only the first filter in most cases. * If you used '-s -s', the ifa_flags member would be modified, which could make future filters fail to function fine. This patch attempts to fix all of these issues. Tested-by: Brian Haley Signed-off-by: Ben Greear --- ip/ipaddress.c | 34 +++++++++++++++++++++++----------- lib/libnetlink.c | 23 ++++++++++++++++------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/ip/ipaddress.c b/ip/ipaddress.c index 3a411b1..19b3d6e 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -453,6 +453,8 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, struct ifaddrmsg *ifa = NLMSG_DATA(n); int len = n->nlmsg_len; int deprecated = 0; + /* Use local copy of ifa_flags to not interfere with filtering code */ + unsigned int ifa_flags; struct rtattr * rta_tb[IFA_MAX+1]; char abuf[256]; SPRINT_BUF(b1); @@ -572,40 +574,41 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, abuf, sizeof(abuf))); } fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1))); + ifa_flags = ifa->ifa_flags; if (ifa->ifa_flags&IFA_F_SECONDARY) { - ifa->ifa_flags &= ~IFA_F_SECONDARY; + ifa_flags &= ~IFA_F_SECONDARY; if (ifa->ifa_family == AF_INET6) fprintf(fp, "temporary "); else fprintf(fp, "secondary "); } if (ifa->ifa_flags&IFA_F_TENTATIVE) { - ifa->ifa_flags &= ~IFA_F_TENTATIVE; + ifa_flags &= ~IFA_F_TENTATIVE; fprintf(fp, "tentative "); } if (ifa->ifa_flags&IFA_F_DEPRECATED) { - ifa->ifa_flags &= ~IFA_F_DEPRECATED; + ifa_flags &= ~IFA_F_DEPRECATED; deprecated = 1; fprintf(fp, "deprecated "); } if (ifa->ifa_flags&IFA_F_HOMEADDRESS) { - ifa->ifa_flags &= ~IFA_F_HOMEADDRESS; + ifa_flags &= ~IFA_F_HOMEADDRESS; fprintf(fp, "home "); } if (ifa->ifa_flags&IFA_F_NODAD) { - ifa->ifa_flags &= ~IFA_F_NODAD; + ifa_flags &= ~IFA_F_NODAD; fprintf(fp, "nodad "); } if (!(ifa->ifa_flags&IFA_F_PERMANENT)) { fprintf(fp, "dynamic "); } else - ifa->ifa_flags &= ~IFA_F_PERMANENT; + ifa_flags &= ~IFA_F_PERMANENT; if (ifa->ifa_flags&IFA_F_DADFAILED) { - ifa->ifa_flags &= ~IFA_F_DADFAILED; + ifa_flags &= ~IFA_F_DADFAILED; fprintf(fp, "dadfailed "); } - if (ifa->ifa_flags) - fprintf(fp, "flags %02x ", ifa->ifa_flags); + if (ifa_flags) + fprintf(fp, "flags %02x ", ifa_flags); if (rta_tb[IFA_LABEL]) fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL])); if (rta_tb[IFA_CACHEINFO]) { @@ -638,7 +641,7 @@ int print_addrinfo_primary(const struct sockaddr_nl *who, struct nlmsghdr *n, { struct ifaddrmsg *ifa = NLMSG_DATA(n); - if (!ifa->ifa_flags & IFA_F_SECONDARY) + if (ifa->ifa_flags & IFA_F_SECONDARY) return 0; return print_addrinfo(who, n, arg); @@ -649,7 +652,7 @@ int print_addrinfo_secondary(const struct sockaddr_nl *who, struct nlmsghdr *n, { struct ifaddrmsg *ifa = NLMSG_DATA(n); - if (ifa->ifa_flags & IFA_F_SECONDARY) + if (!(ifa->ifa_flags & IFA_F_SECONDARY)) return 0; return print_addrinfo(who, n, arg); @@ -849,6 +852,7 @@ static int ipaddr_list_or_flush(int argc, char **argv, int flush) exit(1); } if (filter.flushed == 0) { +flush_done: if (show_stats) { if (round == 0) printf("Nothing to flush.\n"); @@ -866,6 +870,14 @@ static int ipaddr_list_or_flush(int argc, char **argv, int flush) printf("\n*** Round %d, deleting %d addresses ***\n", round, filter.flushed); fflush(stdout); } + + /* If we are flushing, and specifying primary, then we + * want to flush only a single round. Otherwise, we'll + * start flushing secondaries that were promoted to + * primaries. + */ + if (!(filter.flags & IFA_F_SECONDARY) && (filter.flagmask & IFA_F_SECONDARY)) + goto flush_done; } fprintf(stderr, "*** Flush remains incomplete after %d rounds. ***\n", MAX_ROUNDS); fflush(stderr); return 1; diff --git a/lib/libnetlink.c b/lib/libnetlink.c index cfeb894..ee4f045 100644 --- a/lib/libnetlink.c +++ b/lib/libnetlink.c @@ -189,6 +189,8 @@ int rtnl_dump_filter_l(struct rtnl_handle *rth, while (1) { int status; const struct rtnl_dump_filter_arg *a; + int found_done = 0; + int msglen = 0; iov.iov_len = sizeof(buf); status = recvmsg(rth->fd, &msg, 0); @@ -208,8 +210,9 @@ int rtnl_dump_filter_l(struct rtnl_handle *rth, for (a = arg; a->filter; a++) { struct nlmsghdr *h = (struct nlmsghdr*)buf; + msglen = status; - while (NLMSG_OK(h, status)) { + while (NLMSG_OK(h, msglen)) { int err; if (nladdr.nl_pid != 0 || @@ -224,8 +227,10 @@ int rtnl_dump_filter_l(struct rtnl_handle *rth, goto skip_it; } - if (h->nlmsg_type == NLMSG_DONE) - return 0; + if (h->nlmsg_type == NLMSG_DONE) { + found_done = 1; + break; /* process next filter */ + } if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { @@ -242,15 +247,19 @@ int rtnl_dump_filter_l(struct rtnl_handle *rth, return err; skip_it: - h = NLMSG_NEXT(h, status); + h = NLMSG_NEXT(h, msglen); } - } while (0); + } + + if (found_done) + return 0; + if (msg.msg_flags & MSG_TRUNC) { fprintf(stderr, "Message truncated\n"); continue; } - if (status) { - fprintf(stderr, "!!!Remnant of size %d\n", status); + if (msglen) { + fprintf(stderr, "!!!Remnant of size %d\n", msglen); exit(1); } } -- 1.7.1