Tolerate DHCP ACKs in REQUESTING state that don't match the serverid.
Some networks have multiple DHCP servers that don't respect the serverid that is specified in DHCPREQUESTs. Instead we simply check to see that the yiaddr matches. While we're at it, ignore DHCP NAK during REQUESTING state. It doesn't really make sense. Instead we should just wait for timeout.
This commit is contained in:
parent
f28495a58b
commit
29da5d21a4
110
src/state.c
110
src/state.c
@ -216,41 +216,47 @@ static int validate_serverid(struct client_state_t cs[static 1],
|
|||||||
svrbuf, sizeof svrbuf);
|
svrbuf, sizeof svrbuf);
|
||||||
log_line("%s: Received %s with an unexpected server id: %s. Ignoring it.",
|
log_line("%s: Received %s with an unexpected server id: %s. Ignoring it.",
|
||||||
client_config.interface, typemsg, svrbuf);
|
client_config.interface, typemsg, svrbuf);
|
||||||
return 1; // XXX! HACK! Change back to 0!
|
return 0;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int an_packet(struct client_state_t cs[static 1],
|
static void get_leasetime(struct client_state_t cs[static 1],
|
||||||
struct dhcpmsg packet[static 1], uint8_t msgtype,
|
struct dhcpmsg packet[static 1])
|
||||||
uint32_t srcaddr, bool is_requesting)
|
{
|
||||||
|
cs->lease = get_option_leasetime(packet);
|
||||||
|
cs->leaseStartTime = curms();
|
||||||
|
if (!cs->lease) {
|
||||||
|
log_line("%s: No lease time received; assuming 1h.",
|
||||||
|
client_config.interface);
|
||||||
|
cs->lease = 60 * 60;
|
||||||
|
} else {
|
||||||
|
if (cs->lease < 60) {
|
||||||
|
log_warning("Server sent lease of <1m. Forcing lease to 1m.",
|
||||||
|
client_config.interface);
|
||||||
|
cs->lease = 60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Always use RFC2131 'default' values. It's not worth validating
|
||||||
|
// the remote server values, if they even exist, for sanity.
|
||||||
|
cs->renewTime = cs->lease >> 1;
|
||||||
|
cs->rebindTime = (cs->lease >> 3) * 0x7; // * 0.875
|
||||||
|
cs->dhcp_wake_ts = cs->leaseStartTime + cs->renewTime * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int extend_packet(struct client_state_t cs[static 1],
|
||||||
|
struct dhcpmsg packet[static 1], uint8_t msgtype,
|
||||||
|
uint32_t srcaddr)
|
||||||
{
|
{
|
||||||
(void)srcaddr;
|
(void)srcaddr;
|
||||||
if (msgtype == DHCPACK) {
|
if (msgtype == DHCPACK) {
|
||||||
if (!validate_serverid(cs, packet, "a DHCP ACK"))
|
if (!validate_serverid(cs, packet, "a DHCP ACK"))
|
||||||
return ANP_IGNORE;
|
return ANP_IGNORE;
|
||||||
cs->lease = get_option_leasetime(packet);
|
get_leasetime(cs, packet);
|
||||||
cs->leaseStartTime = curms();
|
|
||||||
if (!cs->lease) {
|
|
||||||
log_line("%s: No lease time received; assuming 1h.",
|
|
||||||
client_config.interface);
|
|
||||||
cs->lease = 60 * 60;
|
|
||||||
} else {
|
|
||||||
if (cs->lease < 60) {
|
|
||||||
log_warning("Server sent lease of <1m. Forcing lease to 1m.",
|
|
||||||
client_config.interface);
|
|
||||||
cs->lease = 60;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Always use RFC2131 'default' values. It's not worth validating
|
|
||||||
// the remote server values, if they even exist, for sanity.
|
|
||||||
cs->renewTime = cs->lease >> 1;
|
|
||||||
cs->rebindTime = (cs->lease >> 3) * 0x7; // * 0.875
|
|
||||||
cs->dhcp_wake_ts = cs->leaseStartTime + cs->renewTime * 1000;
|
|
||||||
|
|
||||||
// Only check if we are either in the REQUESTING state, or if we
|
// Only check if we are either in the REQUESTING state, or if we
|
||||||
// have received a lease with a different IP than what we had before.
|
// have received a lease with a different IP than what we had before.
|
||||||
if (is_requesting || memcmp(&packet->yiaddr, &cs->clientAddr, 4)) {
|
if (memcmp(&packet->yiaddr, &cs->clientAddr, 4)) {
|
||||||
char clibuf[INET_ADDRSTRLEN];
|
char clibuf[INET_ADDRSTRLEN];
|
||||||
inet_ntop(AF_INET, &(struct in_addr){.s_addr=cs->clientAddr},
|
inet_ntop(AF_INET, &(struct in_addr){.s_addr=cs->clientAddr},
|
||||||
clibuf, sizeof clibuf);
|
clibuf, sizeof clibuf);
|
||||||
@ -276,14 +282,11 @@ static int an_packet(struct client_state_t cs[static 1],
|
|||||||
return ANP_IGNORE;
|
return ANP_IGNORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: This should also handle the REQUESTING part of an_packet and
|
|
||||||
// be used during REQUESTING; that will allow ndhc to track multiple
|
|
||||||
// DHCPOFFERs.
|
|
||||||
static int selecting_packet(struct client_state_t cs[static 1],
|
static int selecting_packet(struct client_state_t cs[static 1],
|
||||||
struct dhcpmsg packet[static 1], uint8_t msgtype,
|
struct dhcpmsg packet[static 1], uint8_t msgtype,
|
||||||
uint32_t srcaddr)
|
uint32_t srcaddr, bool is_requesting)
|
||||||
{
|
{
|
||||||
if (msgtype == DHCPOFFER) {
|
if (!is_requesting && msgtype == DHCPOFFER) {
|
||||||
int found;
|
int found;
|
||||||
uint32_t sid = get_option_serverid(packet, &found);
|
uint32_t sid = get_option_serverid(packet, &found);
|
||||||
if (!found) {
|
if (!found) {
|
||||||
@ -294,8 +297,8 @@ static int selecting_packet(struct client_state_t cs[static 1],
|
|||||||
char clibuf[INET_ADDRSTRLEN];
|
char clibuf[INET_ADDRSTRLEN];
|
||||||
char svrbuf[INET_ADDRSTRLEN];
|
char svrbuf[INET_ADDRSTRLEN];
|
||||||
char srcbuf[INET_ADDRSTRLEN];
|
char srcbuf[INET_ADDRSTRLEN];
|
||||||
cs->serverAddr = sid;
|
|
||||||
cs->clientAddr = packet->yiaddr;
|
cs->clientAddr = packet->yiaddr;
|
||||||
|
cs->serverAddr = sid;
|
||||||
cs->srcAddr = srcaddr;
|
cs->srcAddr = srcaddr;
|
||||||
cs->dhcp_wake_ts = curms();
|
cs->dhcp_wake_ts = curms();
|
||||||
cs->num_dhcp_requests = 0;
|
cs->num_dhcp_requests = 0;
|
||||||
@ -308,6 +311,38 @@ static int selecting_packet(struct client_state_t cs[static 1],
|
|||||||
log_line("%s: Received IP offer: %s from server %s via %s.",
|
log_line("%s: Received IP offer: %s from server %s via %s.",
|
||||||
client_config.interface, clibuf, svrbuf, srcbuf);
|
client_config.interface, clibuf, svrbuf, srcbuf);
|
||||||
return ANP_SUCCESS;
|
return ANP_SUCCESS;
|
||||||
|
} else if (is_requesting && msgtype == DHCPACK) {
|
||||||
|
// Don't validate the server id. Instead validate that the
|
||||||
|
// yiaddr matches. Some networks have multiple servers
|
||||||
|
// that don't respect the serverid that was specified in
|
||||||
|
// our DHCPREQUEST.
|
||||||
|
if (!memcmp(&packet->yiaddr, &cs->clientAddr, 4)) {
|
||||||
|
char clibuf[INET_ADDRSTRLEN];
|
||||||
|
char svrbuf[INET_ADDRSTRLEN];
|
||||||
|
char srcbuf[INET_ADDRSTRLEN];
|
||||||
|
int found;
|
||||||
|
uint32_t sid = get_option_serverid(packet, &found);
|
||||||
|
if (!found) {
|
||||||
|
log_line("%s: Invalid offer received: it didn't have a server id.",
|
||||||
|
client_config.interface);
|
||||||
|
return ANP_IGNORE;
|
||||||
|
}
|
||||||
|
if (cs->serverAddr != sid) {
|
||||||
|
cs->serverAddr = sid;
|
||||||
|
cs->srcAddr = srcaddr;
|
||||||
|
}
|
||||||
|
get_leasetime(cs, packet);
|
||||||
|
|
||||||
|
inet_ntop(AF_INET, &(struct in_addr){.s_addr=cs->clientAddr},
|
||||||
|
clibuf, sizeof clibuf);
|
||||||
|
inet_ntop(AF_INET, &(struct in_addr){.s_addr=cs->serverAddr},
|
||||||
|
svrbuf, sizeof svrbuf);
|
||||||
|
inet_ntop(AF_INET, &(struct in_addr){.s_addr=cs->srcAddr},
|
||||||
|
srcbuf, sizeof srcbuf);
|
||||||
|
log_line("%s: Received ACK: %s from server %s via %s. Validating...",
|
||||||
|
client_config.interface, clibuf, svrbuf, srcbuf);
|
||||||
|
return ANP_CHECK_IP;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ANP_IGNORE;
|
return ANP_IGNORE;
|
||||||
}
|
}
|
||||||
@ -429,7 +464,7 @@ reinit:
|
|||||||
int ret = COR_SUCCESS;
|
int ret = COR_SUCCESS;
|
||||||
if (sev_dhcp) {
|
if (sev_dhcp) {
|
||||||
int r = selecting_packet(cs, dhcp_packet, dhcp_msgtype,
|
int r = selecting_packet(cs, dhcp_packet, dhcp_msgtype,
|
||||||
dhcp_srcaddr);
|
dhcp_srcaddr, false);
|
||||||
if (r == ANP_SUCCESS) {
|
if (r == ANP_SUCCESS) {
|
||||||
// Send a request packet to the answering DHCP server.
|
// Send a request packet to the answering DHCP server.
|
||||||
sev_dhcp = false;
|
sev_dhcp = false;
|
||||||
@ -452,16 +487,9 @@ reinit:
|
|||||||
skip_to_requesting:
|
skip_to_requesting:
|
||||||
ret = COR_SUCCESS;
|
ret = COR_SUCCESS;
|
||||||
if (sev_dhcp) {
|
if (sev_dhcp) {
|
||||||
int r = an_packet(cs, dhcp_packet, dhcp_msgtype, dhcp_srcaddr,
|
int r = selecting_packet(cs, dhcp_packet, dhcp_msgtype,
|
||||||
true);
|
dhcp_srcaddr, true);
|
||||||
if (r == ANP_IGNORE) {
|
if (r == ANP_IGNORE) {
|
||||||
} else if (r == ANP_FAIL) {
|
|
||||||
ret = COR_ERROR;
|
|
||||||
scrReturn(ret);
|
|
||||||
continue;
|
|
||||||
} else if (r == ANP_REJECTED) {
|
|
||||||
sev_dhcp = false;
|
|
||||||
goto reinit;
|
|
||||||
} else if (r == ANP_CHECK_IP) {
|
} else if (r == ANP_CHECK_IP) {
|
||||||
if (arp_check(cs, dhcp_packet) < 0) {
|
if (arp_check(cs, dhcp_packet) < 0) {
|
||||||
log_warning("%s: Failed to make arp socket. Searching for new lease...",
|
log_warning("%s: Failed to make arp socket. Searching for new lease...",
|
||||||
@ -550,8 +578,7 @@ skip_to_requesting:
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
int ret = COR_SUCCESS;
|
int ret = COR_SUCCESS;
|
||||||
if (sev_dhcp && is_renewing(cs, nowts)) {
|
if (sev_dhcp && is_renewing(cs, nowts)) {
|
||||||
int r = an_packet(cs, dhcp_packet, dhcp_msgtype, dhcp_srcaddr,
|
int r = extend_packet(cs, dhcp_packet, dhcp_msgtype, dhcp_srcaddr);
|
||||||
false);
|
|
||||||
if (r == ANP_SUCCESS || r == ANP_IGNORE) {
|
if (r == ANP_SUCCESS || r == ANP_IGNORE) {
|
||||||
} else if (r == ANP_FAIL) {
|
} else if (r == ANP_FAIL) {
|
||||||
ret = COR_ERROR;
|
ret = COR_ERROR;
|
||||||
@ -651,6 +678,7 @@ skip_to_requesting:
|
|||||||
int r = ifup_action(cs);
|
int r = ifup_action(cs);
|
||||||
if (r == IFUP_REVALIDATE) {
|
if (r == IFUP_REVALIDATE) {
|
||||||
} else if (r == IFUP_NEWLEASE) {
|
} else if (r == IFUP_NEWLEASE) {
|
||||||
|
// XXX: Deconfigure the interface. The network has changed.
|
||||||
if (reinit_selecting(cs, 0) < 0) {
|
if (reinit_selecting(cs, 0) < 0) {
|
||||||
ret = COR_ERROR;
|
ret = COR_ERROR;
|
||||||
scrReturn(ret);
|
scrReturn(ret);
|
||||||
|
Loading…
Reference in New Issue
Block a user