From a0358e5ddbc1ef3dec791f11f95f5dbe56087a5e Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sat, 7 Jun 2014 13:38:48 +0100 Subject: [PATCH] Handle async notification of address changes using the event system. --- CHANGELOG | 4 ++++ src/bpf.c | 6 +++--- src/dhcp6.c | 10 ---------- src/dnsmasq.c | 13 +++++++++++-- src/dnsmasq.h | 6 ++++-- src/netlink.c | 39 ++++++++++----------------------------- src/network.c | 11 +++-------- 7 files changed, 35 insertions(+), 54 deletions(-) --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,10 @@ version 2.71 regression introduced in 2.69. Thanks to James Hunt and the Ubuntu crowd for assistance in fixing this. + Fix race condition which could lock up dnsmasq when an + interface goes down and up rapidly. Thanks to Conrad + Kostecki for helping to chase this down. + version 2.70 Fix crash, introduced in 2.69, on TCP request when dnsmasq --- a/src/bpf.c +++ b/src/bpf.c @@ -376,7 +376,7 @@ void route_init(void) die(_("cannot create PF_ROUTE socket: %s"), NULL, EC_BADNET); } -void route_sock(time_t now) +void route_sock(void) { struct if_msghdr *msg; int rc = recv(daemon->routefd, daemon->packet, daemon->packet_buff_sz, 0); @@ -401,7 +401,7 @@ void route_sock(time_t now) else if (msg->ifm_type == RTM_NEWADDR) { del_family = 0; - newaddress(now); + send_newaddr(); } else if (msg->ifm_type == RTM_DELADDR) { @@ -439,7 +439,7 @@ void route_sock(time_t now) of += sizeof(long) - (diff & (sizeof(long) - 1)); } - newaddress(now); + send_newaddr(); } } --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -917,10 +917,10 @@ int main (int argc, char **argv) #if defined(HAVE_LINUX_NETWORK) if (FD_ISSET(daemon->netlinkfd, &rset)) - netlink_multicast(now); + netlink_multicast(); #elif defined(HAVE_BSD_NETWORK) if (FD_ISSET(daemon->routefd, &rset)) - route_sock(now); + route_sock(); #endif /* Check for changes to resolv files once per second max. */ @@ -1037,6 +1037,11 @@ void send_alarm(time_t event, time_t now } } +void send_newaddr(void) +{ + send_event(pipewrite, EVENT_NEWADDR, 0, NULL); +} + void send_event(int fd, int event, int data, char *msg) { struct event_desc ev; @@ -1230,6 +1235,10 @@ static void async_event(int pipe, time_t if (daemon->log_file != NULL) log_reopen(daemon->log_file); break; + + case EVENT_NEWADDR: + newaddress(now); + break; case EVENT_TERM: /* Knock all our children on the head. */ --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -165,6 +165,7 @@ struct event_desc { #define EVENT_LUA_ERR 19 #define EVENT_TFTP_ERR 20 #define EVENT_INIT 21 +#define EVENT_NEWADDR 22 /* Exit codes. */ #define EC_GOOD 0 @@ -1289,6 +1290,7 @@ unsigned char *extended_hwaddr(int hwtyp int make_icmp_sock(void); int icmp_ping(struct in_addr addr); #endif +void send_newaddr(void); void send_alarm(time_t event, time_t now); void send_event(int fd, int event, int data, char *msg); void clear_cache_and_reload(time_t now); @@ -1297,7 +1299,7 @@ void poll_resolv(int force, int do_reloa /* netlink.c */ #ifdef HAVE_LINUX_NETWORK void netlink_init(void); -void netlink_multicast(time_t now); +void netlink_multicast(void); #endif /* bpf.c */ @@ -1306,7 +1308,7 @@ void init_bpf(void); void send_via_bpf(struct dhcp_packet *mess, size_t len, struct in_addr iface_addr, struct ifreq *ifr); void route_init(void); -void route_sock(time_t now); +void route_sock(void); #endif /* bpf.c or netlink.c */ --- a/src/netlink.c +++ b/src/netlink.c @@ -38,7 +38,7 @@ static struct iovec iov; static u32 netlink_pid; -static int nl_async(struct nlmsghdr *h); +static void nl_async(struct nlmsghdr *h); void netlink_init(void) { @@ -142,7 +142,7 @@ int iface_enumerate(int family, void *pa struct nlmsghdr *h; ssize_t len; static unsigned int seq = 0; - int callback_ok = 1, newaddr = 0; + int callback_ok = 1; struct { struct nlmsghdr nlh; @@ -191,21 +191,10 @@ int iface_enumerate(int family, void *pa if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR) { /* May be multicast arriving async */ - if (nl_async(h)) - { - newaddr = 1; - enumerate_interfaces(1); /* reset */ - } + nl_async(h); } else if (h->nlmsg_type == NLMSG_DONE) - { - /* handle async new interface address arrivals, these have to be done - after we complete as we're not re-entrant */ - if (newaddr) - newaddress(dnsmasq_time()); - - return callback_ok; - } + return callback_ok; else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL) { struct ifaddrmsg *ifa = NLMSG_DATA(h); @@ -330,11 +319,11 @@ int iface_enumerate(int family, void *pa } } -void netlink_multicast(time_t now) +void netlink_multicast(void) { ssize_t len; struct nlmsghdr *h; - int flags, newaddr = 0; + int flags; /* don't risk blocking reading netlink messages here. */ if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 || @@ -343,24 +332,19 @@ void netlink_multicast(time_t now) if ((len = netlink_recv()) != -1) for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) - if (nl_async(h)) - newaddr = 1; + nl_async(h); /* restore non-blocking status */ fcntl(daemon->netlinkfd, F_SETFL, flags); - - if (newaddr) - newaddress(now); } -static int nl_async(struct nlmsghdr *h) +static void nl_async(struct nlmsghdr *h) { if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = NLMSG_DATA(h); if (err->error != 0) my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error))); - return 0; } else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE) { @@ -385,18 +369,15 @@ static int nl_async(struct nlmsghdr *h) else if (daemon->rfd_save && daemon->rfd_save->refcount != 0) fd = daemon->rfd_save->fd; else - return 0; + return; while(sendto(fd, daemon->packet, daemon->packet_len, 0, &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send()); } } - return 0; } else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) - return 1; /* clever bind mode - rescan */ - - return 0; + send_newaddr(); } #endif --- a/src/network.c +++ b/src/network.c @@ -551,7 +551,7 @@ static int iface_allowed_v4(struct in_ad int enumerate_interfaces(int reset) { static struct addrlist *spare = NULL; - static int done = 0, active = 0; + static int done = 0; struct iface_param param; int errsave, ret = 1; struct addrlist *addr, *tmp; @@ -570,14 +570,11 @@ int enumerate_interfaces(int reset) return 1; } - if (done || active) + if (done) return 1; done = 1; - /* protect against recusive calls from iface_enumerate(); */ - active = 1; - if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return 0; @@ -677,10 +674,8 @@ int enumerate_interfaces(int reset) } errno = errsave; - spare = param.spare; - active = 0; - + return ret; }