blob: c87e275f808c975d84155ce6775312425c730715 [file] [log] [blame]
From b1753f9bcb89e978d64e25f4a27d26f95220f509 Mon Sep 17 00:00:00 2001
From: Paul Stewart <pstew@chromium.org>
Date: Wed, 13 May 2015 15:53:35 -0700
Subject: [PATCH] Track and validate disputed addresses
Keep track of whether we have received a NAK response. If we
accept an ACK shortly after a NAK, although we accept this
address, it must be more carefully evaluated by using the "ARP
for self" method. If this method succeeds, the system should
proceed as normal (either doing a gateway ARP or sending the
success notification via D-Bus).
Moreover, if the "ARP self" method fails, it will DECLINE the
lease and restart the DHCP process. When examining any messages
from the DHCP server in this restarted session, ignore the
first OFFER if it is for the previously declined IP address.
In scenarios where there may be more than one conflicting DHCP
server on the network, this allows the possibility to accept
a more viable offer.
BUG=chromium:384897
TEST=New network_DhcpNak test subcase in CL:208248
Reviewed-on: https://chromium-review.googlesource.com/208273
---
arp.c | 1 +
dhcp.c | 31 +++++++++++++++++++++++++------
dhcp.h | 3 +++
3 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/arp.c b/arp.c
index 5fb23a3..96cf40c 100644
--- a/arp.c
+++ b/arp.c
@@ -133,6 +133,7 @@ arp_packet(void *arg)
int flags;
state = D_STATE(ifp);
+ state->failed.s_addr = 0;
flags = 0;
while (!(flags & RAW_EOF)) {
bytes = if_readrawpacket(ifp, ETHERTYPE_ARP,
diff --git a/dhcp.c b/dhcp.c
index e57f6e0..8fca2f6 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -1818,6 +1818,8 @@ dhcp_discover(void *arg)
state->state = DHS_DISCOVER;
state->xid = dhcp_xid(ifp);
+ state->nak_receive_count = 0;
+ state->failed_address_offer_count = 0;
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
if (ifo->fallback)
eloop_timeout_add_sec(ifp->ctx->eloop,
@@ -1844,6 +1846,7 @@ dhcp_request(void *arg)
struct dhcp_state *state = D_STATE(ifp);
state->state = DHS_REQUEST;
+ state->nak_receive_count = 0;
send_request(ifp);
}
@@ -1883,6 +1886,7 @@ dhcp_renew(void *arg)
lease->leasetime - lease->renewaltime);
state->state = DHS_RENEW;
state->xid = dhcp_xid(ifp);
+ state->nak_receive_count = 0;
send_renew(ifp);
}
@@ -1909,6 +1913,7 @@ dhcp_rebind(void *arg)
state->state = DHS_REBIND;
eloop_timeout_delete(ifp->ctx->eloop, send_renew, ifp);
state->lease.server.s_addr = 0;
+ state->nak_receive_count = 0;
ifp->options->options &= ~(DHCPCD_CSR_WARNED |
DHCPCD_ROUTER_HOST_ROUTE_WARNED);
send_rebind(ifp);
@@ -2610,10 +2615,14 @@ dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
struct ipv4_addr *ia;
#endif
- if (amsg)
+ if (amsg) {
astate->failed.s_addr = state->offer->yiaddr;
- else
+ state->failed.s_addr = state->offer->yiaddr;
+ } else {
astate->failed = astate->addr;
+ state->failed = astate->addr;
+ }
+
arp_report_conflicted(astate, amsg);
unlink(state->leasefile);
if (!state->lease.frominfo)
@@ -2781,9 +2790,10 @@ dhcp_handledhcp(struct interface *ifp, struct dhcp_message **dhcpp,
return;
log_dhcp(LOG_WARNING, "NAK (deferred):", ifp, dhcp, from);
- eloop_timeout_add_sec(ifp->ctx->eloop,
- DHCP_BASE, handle_nak, ifp);
-
+ if (state->nak_receive_count == 0)
+ eloop_timeout_add_sec(ifp->ctx->eloop,
+ DHCP_BASE, handle_nak, ifp);
+ state->nak_receive_count++;
return;
}
@@ -2878,6 +2888,14 @@ dhcp_handledhcp(struct interface *ifp, struct dhcp_message **dhcpp,
if ((type == 0 || type == DHCP_OFFER) &&
(state->state == DHS_DISCOVER || state->state == DHS_IPV4LL_BOUND))
{
+ if (dhcp->yiaddr == state->failed.s_addr &&
+ state->failed_address_offer_count == 0) {
+ log_dhcp(LOG_WARNING,
+ "reject previously declined address",
+ ifp, dhcp, from);
+ state->failed_address_offer_count++;
+ return;
+ }
lease->frominfo = 0;
lease->addr.s_addr = dhcp->yiaddr;
lease->cookie = dhcp->cookie;
@@ -2953,7 +2971,8 @@ dhcp_handledhcp(struct interface *ifp, struct dhcp_message **dhcpp,
astate = NULL;
#ifndef IN_IFF_TENTATIVE
- if (ifo->options & DHCPCD_ARP
+ if ((ifo->options & DHCPCD_ARP || state->nak_receive_count > 0 ||
+ dhcp->yiaddr == state->failed.s_addr)
&& state->addr.s_addr != state->offer->yiaddr)
#endif
{
diff --git a/dhcp.h b/dhcp.h
index ed94395..e136a9c 100644
--- a/dhcp.h
+++ b/dhcp.h
@@ -240,6 +240,9 @@ struct dhcp_state {
struct arp_state *arp_ipv4ll;
unsigned int conflicts;
+ int nak_receive_count;
+ int failed_address_offer_count;
+ struct in_addr failed;
time_t defend;
char randomstate[128];
struct dhcp_server_info server_info;
--
2.2.0.rc0.207.ga3a616c