Add a heuristic to detect when server ignores dhcp renews.

If we get no response to three renews (unicast), switch to sending
rebinds (broadcast).  Servers are supposed to always reply with
a DHCPACK or DHCPNAK even if the server doesn't update its internal
lease duration database, so this behavior should be RFC compliant.
This commit is contained in:
Nicholas J. Kain 2020-10-19 07:03:03 -04:00
parent f4365897bc
commit 32bc422d0e
2 changed files with 8 additions and 2 deletions

View File

@ -48,7 +48,7 @@ struct client_state_t {
int epollFd, signalFd, listenFd, arpFd, nlFd, rfkillFd; int epollFd, signalFd, listenFd, arpFd, nlFd, rfkillFd;
int server_arp_sent, router_arp_sent; int server_arp_sent, router_arp_sent;
uint32_t nlPortId; uint32_t nlPortId;
unsigned int num_dhcp_requests; unsigned int num_dhcp_requests, num_dhcp_renews;
uint32_t clientAddr, serverAddr, srcAddr, routerAddr; uint32_t clientAddr, serverAddr, srcAddr, routerAddr;
uint32_t lease, xid; uint32_t lease, xid;
uint8_t routerArp[6], serverArp[6]; uint8_t routerArp[6], serverArp[6];

View File

@ -43,6 +43,8 @@
#include "netlink.h" #include "netlink.h"
#include "coroutine.h" #include "coroutine.h"
#define IGNORED_RENEWS_BEFORE_REBIND 3
#define SEL_SUCCESS 0 #define SEL_SUCCESS 0
#define SEL_FAIL -1 #define SEL_FAIL -1
@ -78,6 +80,7 @@ static void reinit_shared_deconfig(struct client_state_t cs[static 1])
cs->xid = nk_random_u32(&cs->rnd_state); cs->xid = nk_random_u32(&cs->rnd_state);
cs->clientAddr = 0; cs->clientAddr = 0;
cs->num_dhcp_requests = 0; cs->num_dhcp_requests = 0;
cs->num_dhcp_renews = 0;
cs->server_arp_sent = 0; cs->server_arp_sent = 0;
cs->router_arp_sent = 0; cs->router_arp_sent = 0;
cs->server_arp_state = ARP_QUERY; cs->server_arp_state = ARP_QUERY;
@ -150,7 +153,7 @@ static int renewing_timeout(struct client_state_t cs[static 1],
long long nowts) long long nowts)
{ {
long long rbt = cs->leaseStartTime + cs->rebindTime * 1000; long long rbt = cs->leaseStartTime + cs->rebindTime * 1000;
if (nowts >= rbt) if (nowts >= rbt || cs->num_dhcp_renews >= IGNORED_RENEWS_BEFORE_REBIND)
return rebinding_timeout(cs, nowts); return rebinding_timeout(cs, nowts);
start_dhcp_listen(cs); start_dhcp_listen(cs);
if (send_renew(cs) < 0) { if (send_renew(cs) < 0) {
@ -159,6 +162,7 @@ static int renewing_timeout(struct client_state_t cs[static 1],
return BTO_HARDFAIL; return BTO_HARDFAIL;
} }
cs->sent_renew_or_rebind = true; cs->sent_renew_or_rebind = true;
++cs->num_dhcp_renews;
long long ts0 = nowts + (50 + nk_random_u32(&cs->rnd_state) % 20) * 1000; long long ts0 = nowts + (50 + nk_random_u32(&cs->rnd_state) % 20) * 1000;
cs->dhcp_wake_ts = ts0 < rbt ? ts0 : rbt; cs->dhcp_wake_ts = ts0 < rbt ? ts0 : rbt;
return BTO_WAIT; return BTO_WAIT;
@ -230,6 +234,7 @@ static int extend_packet(struct client_state_t cs[static 1],
if (!validate_serverid(cs, packet, "a DHCP ACK")) if (!validate_serverid(cs, packet, "a DHCP ACK"))
return ANP_IGNORE; return ANP_IGNORE;
cs->sent_renew_or_rebind = false; cs->sent_renew_or_rebind = false;
cs->num_dhcp_renews = 0;
get_leasetime(cs, packet); get_leasetime(cs, packet);
// Did we receive a lease with a different IP than we had before? // Did we receive a lease with a different IP than we had before?
@ -253,6 +258,7 @@ static int extend_packet(struct client_state_t cs[static 1],
if (!validate_serverid(cs, packet, "a DHCP NAK")) if (!validate_serverid(cs, packet, "a DHCP NAK"))
return ANP_IGNORE; return ANP_IGNORE;
cs->sent_renew_or_rebind = false; cs->sent_renew_or_rebind = false;
cs->num_dhcp_renews = 0;
log_line("%s: Our request was rejected. Searching for a new lease...", log_line("%s: Our request was rejected. Searching for a new lease...",
client_config.interface); client_config.interface);
reinit_selecting(cs, 3000); reinit_selecting(cs, 3000);