From: Simon Kelley Date: Sun, 5 Jul 2015 21:31:30 +0000 (+0100) Subject: Avoid hanngs in DHCP ping code when system time goes backwards. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5e95a552ee68a945fe2277f6e4ed857e6d8b8620;p=people%2Fms%2Fdnsmasq.git Avoid hanngs in DHCP ping code when system time goes backwards. --- diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 19a6428..5fb4281 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -1755,14 +1755,22 @@ int icmp_ping(struct in_addr addr) better not use any resources our caller has in use...) but we remain deaf to signals or further DHCP packets. */ - int fd; + /* There can be a problem using dnsmasq_time() to end the loop, since + it's not monotonic, and can go backwards if the system clock is + tweaked, leading to the code getting stuck in this loop and + ignoring DHCP requests. To fix this, we check to see if select returned + as a result of a timeout rather than a socket becoming available. We + only allow this to happen as many times as it takes to get to the wait time + in quarter-second chunks. This provides a fallback way to end loop. */ + + int fd, rc; struct sockaddr_in saddr; struct { struct ip ip; struct icmp icmp; } packet; unsigned short id = rand16(); - unsigned int i, j; + unsigned int i, j, timeout_count; int gotreply = 0; time_t start, now; @@ -1794,8 +1802,8 @@ int icmp_ping(struct in_addr addr) while (retry_send(sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0, (struct sockaddr *)&saddr, sizeof(saddr)))); - for (now = start = dnsmasq_time(); - difftime(now, start) < (float)PING_WAIT;) + for (now = start = dnsmasq_time(), timeout_count = 0; + (difftime(now, start) < (float)PING_WAIT) && (timeout_count < PING_WAIT * 4);) { struct timeval tv; fd_set rset, wset; @@ -1820,11 +1828,15 @@ int icmp_ping(struct in_addr addr) } #endif - if (select(maxfd+1, &rset, &wset, NULL, &tv) < 0) + rc = select(maxfd+1, &rset, &wset, NULL, &tv); + + if (rc < 0) { FD_ZERO(&rset); FD_ZERO(&wset); } + else if (rc == 0) + timeout_count++; now = dnsmasq_time();