Commit 8d07e5f7 authored by Stephen Hemminger's avatar Stephen Hemminger

Refactor ipaddr_list_or_flush

Alternative solution to problem reported by Pravin B Shelar <pshelar@nicira.com>
Split large function ipaddr_list_or_flush into components.
Fix memory leak of address and link nlmsg info.
Avoid fetching address info if only flushing.
parent 524de027
......@@ -768,11 +768,145 @@ static int store_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n,
return 0;
}
static void free_nlmsg_chain(struct nlmsg_chain *info)
{
struct nlmsg_list *l, *n;
for (l = info->head; l; l = n) {
n = l->next;
free(l);
}
}
static void ipaddr_filter(struct nlmsg_chain *linfo, struct nlmsg_chain *ainfo)
{
struct nlmsg_list *l, **lp;
lp = &linfo->head;
while ( (l = *lp) != NULL) {
int ok = 0;
struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
struct nlmsg_list *a;
for (a = ainfo->head; a; a = a->next) {
struct nlmsghdr *n = &a->h;
struct ifaddrmsg *ifa = NLMSG_DATA(n);
if (ifa->ifa_index != ifi->ifi_index ||
(filter.family && filter.family != ifa->ifa_family))
continue;
if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
continue;
if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
continue;
if (filter.pfx.family || filter.label) {
struct rtattr *tb[IFA_MAX+1];
parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
if (!tb[IFA_LOCAL])
tb[IFA_LOCAL] = tb[IFA_ADDRESS];
if (filter.pfx.family && tb[IFA_LOCAL]) {
inet_prefix dst;
memset(&dst, 0, sizeof(dst));
dst.family = ifa->ifa_family;
memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
continue;
}
if (filter.label) {
SPRINT_BUF(b1);
const char *label;
if (tb[IFA_LABEL])
label = RTA_DATA(tb[IFA_LABEL]);
else
label = ll_idx_n2a(ifa->ifa_index, b1);
if (fnmatch(filter.label, label, 0) != 0)
continue;
}
}
ok = 1;
break;
}
if (!ok) {
*lp = l->next;
free(l);
} else
lp = &l->next;
}
}
static int ipaddr_flush(void)
{
int round = 0;
char flushb[4096-512];
filter.flushb = flushb;
filter.flushp = 0;
filter.flushe = sizeof(flushb);
while ((max_flush_loops == 0) || (round < max_flush_loops)) {
const struct rtnl_dump_filter_arg a[3] = {
{
.filter = print_addrinfo_secondary,
.arg1 = stdout,
},
{
.filter = print_addrinfo_primary,
.arg1 = stdout,
},
{
.filter = NULL,
.arg1 = NULL,
},
};
if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
perror("Cannot send dump request");
exit(1);
}
filter.flushed = 0;
if (rtnl_dump_filter_l(&rth, a) < 0) {
fprintf(stderr, "Flush terminated\n");
exit(1);
}
if (filter.flushed == 0) {
flush_done:
if (show_stats) {
if (round == 0)
printf("Nothing to flush.\n");
else
printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
}
fflush(stdout);
return 0;
}
round++;
if (flush_update() < 0)
return 1;
if (show_stats) {
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_flush_loops);
fflush(stderr);
return 1;
}
static int ipaddr_list_or_flush(int argc, char **argv, int flush)
{
struct nlmsg_chain linfo = { NULL, NULL};
struct nlmsg_chain ainfo = { NULL, NULL};
struct nlmsg_list *l, *n;
struct nlmsg_list *l;
char *filter_dev = NULL;
int no_link = 0;
......@@ -863,16 +997,6 @@ static int ipaddr_list_or_flush(int argc, char **argv, int flush)
argv++; argc--;
}
if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
perror("Cannot send dump request");
exit(1);
}
if (rtnl_dump_filter(&rth, store_nlmsg, &linfo) < 0) {
fprintf(stderr, "Dump terminated\n");
exit(1);
}
if (filter_dev) {
filter.ifindex = ll_name_to_index(filter_dev);
if (filter.ifindex <= 0) {
......@@ -881,72 +1005,23 @@ static int ipaddr_list_or_flush(int argc, char **argv, int flush)
}
}
if (flush) {
int round = 0;
char flushb[4096-512];
filter.flushb = flushb;
filter.flushp = 0;
filter.flushe = sizeof(flushb);
while ((max_flush_loops == 0) || (round < max_flush_loops)) {
const struct rtnl_dump_filter_arg a[3] = {
{
.filter = print_addrinfo_secondary,
.arg1 = stdout,
},
{
.filter = print_addrinfo_primary,
.arg1 = stdout,
},
{
.filter = NULL,
.arg1 = NULL,
},
};
if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
perror("Cannot send dump request");
exit(1);
}
filter.flushed = 0;
if (rtnl_dump_filter_l(&rth, a) < 0) {
fprintf(stderr, "Flush terminated\n");
exit(1);
}
if (filter.flushed == 0) {
flush_done:
if (show_stats) {
if (round == 0)
printf("Nothing to flush.\n");
else
printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":"");
}
fflush(stdout);
return 0;
}
round++;
if (flush_update() < 0)
return 1;
if (flush)
return ipaddr_flush();
if (show_stats) {
printf("\n*** Round %d, deleting %d addresses ***\n", round, filter.flushed);
fflush(stdout);
}
if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
perror("Cannot send dump request");
exit(1);
}
/* 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_flush_loops);
fflush(stderr);
return 1;
if (rtnl_dump_filter(&rth, store_nlmsg, &linfo) < 0) {
fprintf(stderr, "Dump terminated\n");
exit(1);
}
if (filter.family != AF_PACKET) {
if (filter.family && filter.family != AF_PACKET) {
if (filter.oneline)
no_link = 1;
if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
perror("Cannot send dump request");
exit(1);
......@@ -956,78 +1031,22 @@ flush_done:
fprintf(stderr, "Dump terminated\n");
exit(1);
}
}
if (filter.family && filter.family != AF_PACKET) {
struct nlmsg_list **lp;
lp = &linfo.head;
if (filter.oneline)
no_link = 1;
while ((l=*lp)!=NULL) {
int ok = 0;
struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
struct nlmsg_list *a;
for (a = ainfo.head; a; a = a->next) {
struct nlmsghdr *n = &a->h;
struct ifaddrmsg *ifa = NLMSG_DATA(n);
if (ifa->ifa_index != ifi->ifi_index ||
(filter.family && filter.family != ifa->ifa_family))
continue;
if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
continue;
if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
continue;
if (filter.pfx.family || filter.label) {
struct rtattr *tb[IFA_MAX+1];
parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
if (!tb[IFA_LOCAL])
tb[IFA_LOCAL] = tb[IFA_ADDRESS];
if (filter.pfx.family && tb[IFA_LOCAL]) {
inet_prefix dst;
memset(&dst, 0, sizeof(dst));
dst.family = ifa->ifa_family;
memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
continue;
}
if (filter.label) {
SPRINT_BUF(b1);
const char *label;
if (tb[IFA_LABEL])
label = RTA_DATA(tb[IFA_LABEL]);
else
label = ll_idx_n2a(ifa->ifa_index, b1);
if (fnmatch(filter.label, label, 0) != 0)
continue;
}
}
ok = 1;
break;
}
if (!ok)
*lp = l->next;
else
lp = &l->next;
}
ipaddr_filter(&linfo, &ainfo);
}
for (l = linfo.head; l; l = n) {
n = l->next;
for (l = linfo.head; l; l = l->next) {
if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
if (filter.family != AF_PACKET)
print_selected_addrinfo(ifi->ifi_index, ainfo.head, stdout);
print_selected_addrinfo(ifi->ifi_index,
ainfo.head, stdout);
}
fflush(stdout);
free(l);
}
fflush(stdout);
free_nlmsg_chain(&ainfo);
free_nlmsg_chain(&linfo);
return 0;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment