From 08619211f81fcc43b67a35ead1f06a181f5ac21f Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 2 Dec 2013 14:43:48 +0000 Subject: [PATCH] Garbage collect listening sockets when their address is deleted. In --bind-dynamic mode, stop listening on an address when it's removed from an interface. 6rd and 6to4 tunnels can go through lots of addresses. --- src/dnsmasq.h | 2 +- src/network.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 5efa7ad..0b9c7dd 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -459,7 +459,7 @@ struct ipsets { struct irec { union mysockaddr addr; struct in_addr netmask; /* only valid for IPv4 */ - int tftp_ok, dhcp_ok, mtu, done, warned, dad, dns_auth, index, multicast_done; + int tftp_ok, dhcp_ok, mtu, done, warned, dad, dns_auth, index, multicast_done, found; char *name; struct irec *next; }; diff --git a/src/network.c b/src/network.c index 2ebcbcb..5225ec5 100644 --- a/src/network.c +++ b/src/network.c @@ -371,6 +371,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label, if (sockaddr_isequal(&iface->addr, addr)) { iface->dad = dad; + iface->found = 1; /* for garbage collection */ return 1; } @@ -445,6 +446,7 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label, iface->dns_auth = auth_dns; iface->mtu = mtu; iface->dad = dad; + iface->found = 1; iface->done = iface->multicast_done = iface->warned = 0; iface->index = if_index; if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1))) @@ -517,6 +519,7 @@ int enumerate_interfaces(int reset) int errsave, ret = 1; struct addrlist *addr, *tmp; struct interface_name *intname; + struct irec *iface; #ifdef HAVE_AUTH struct auth_zone *zone; #endif @@ -541,6 +544,10 @@ int enumerate_interfaces(int reset) if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return 0; + /* Mark interfaces for garbage collection */ + for (iface = daemon->interfaces; iface; iface = iface->next) + iface->found = 0; + /* remove addresses stored against interface_names */ for (intname = daemon->int_names; intname; intname = intname->next) { @@ -587,11 +594,47 @@ int enumerate_interfaces(int reset) errsave = errno; close(param.fd); + + if (option_bool(OPT_CLEVERBIND)) + { + /* Garbage-collect listeners listening on addresses that no longer exist. + Does nothing when not binding interfaces or for listeners on localhost, + since the ->iface field is NULL. Note that this needs the protections + against re-entrancy, hence it's here. It also means there's a possibility, + in OPT_CLEVERBIND mode, that at listener will just disappear after + a call to enumerate_interfaces, this is checked OK on all calls. */ + struct listener *l, *tmp, **up; + + for (up = &daemon->listeners, l = daemon->listeners; l; l = tmp) + { + tmp = l->next; + + if (!l->iface || l->iface->found) + up = &l->next; + else + { + *up = l->next; + + /* In case it ever returns */ + l->iface->done = 0; + + if (l->fd != -1) + close(l->fd); + if (l->tcpfd != -1) + close(l->tcpfd); + if (l->tftpfd != -1) + close(l->tftpfd); + + free(l); + } + } + } + errno = errsave; - + spare = param.spare; active = 0; - + return ret; } @@ -830,7 +873,8 @@ static struct listener *create_listeners(union mysockaddr *addr, int do_tftp, in l->family = addr->sa.sa_family; l->fd = fd; l->tcpfd = tcpfd; - l->tftpfd = tftpfd; + l->tftpfd = tftpfd; + l->iface = NULL; } return l; @@ -877,7 +921,7 @@ void create_bound_listeners(int dienow) struct iname *if_tmp; for (iface = daemon->interfaces; iface; iface = iface->next) - if (!iface->done && !iface->dad && + if (!iface->done && !iface->dad && iface->found && (new = create_listeners(&iface->addr, iface->tftp_ok, dienow))) { new->iface = iface; @@ -901,7 +945,6 @@ void create_bound_listeners(int dienow) if (!if_tmp->used && (new = create_listeners(&if_tmp->addr, !!option_bool(OPT_TFTP), dienow))) { - new->iface = NULL; new->next = daemon->listeners; daemon->listeners = new; }