blob: 63383397295abff777191504d16055e08ba0a99b [file] [log] [blame]
From db757a21a04957aa74445bc6858346d4e5e909cf Mon Sep 17 00:00:00 2001
From: Paul Stewart <pstew@chromium.org>
Date: Tue, 1 Jun 2021 19:36:23 +0000
Subject: [PATCH 07/19] 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
---
src/arp.c | 4 ++++
src/dhcp.c | 28 +++++++++++++++++++++++-----
src/dhcp.h | 3 +++
3 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/src/arp.c b/src/arp.c
index 98081865..f3b2e8ad 100644
--- a/src/arp.c
+++ b/src/arp.c
@@ -45,6 +45,7 @@
#include "bpf.h"
#include "ipv4.h"
#include "common.h"
+#include "dhcp.h"
#include "dhcpcd.h"
#include "eloop.h"
#include "if.h"
@@ -119,6 +120,7 @@ arp_packet(struct interface *ifp, uint8_t *data, size_t len)
const struct iarp_state *state;
struct arp_state *astate, *astaten;
uint8_t *hw_s, *hw_t;
+ struct dhcp_state *d_state;
/* We must have a full ARP header */
if (len < sizeof(ar))
@@ -171,6 +173,8 @@ arp_packet(struct interface *ifp, uint8_t *data, size_t len)
/* Run the conflicts */
state = ARP_CSTATE(ifp);
+ d_state = D_STATE(ifp);
+ d_state->failed.s_addr = 0;
TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, astaten) {
if (arm.sip.s_addr != astate->addr.s_addr &&
arm.tip.s_addr != astate->addr.s_addr)
diff --git a/src/dhcp.c b/src/dhcp.c
index 782eb231..97722a48 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -1966,6 +1966,8 @@ dhcp_discover(void *arg)
struct if_options *ifo = ifp->options;
state->state = DHS_DISCOVER;
+ state->nak_receive_count = 0;
+ state->failed_address_offer_count = 0;
dhcp_new_xid(ifp);
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
if (ifo->fallback)
@@ -1992,6 +1994,7 @@ dhcp_request(void *arg)
struct dhcp_state *state = D_STATE(ifp);
state->state = DHS_REQUEST;
+ state->nak_receive_count = 0;
send_request(ifp);
}
@@ -2082,6 +2085,7 @@ dhcp_startrenew(void *arg)
logdebugx("%s: renewing lease of %s", ifp->name,
inet_ntoa(lease->addr));
state->state = DHS_RENEW;
+ state->nak_receive_count = 0;
dhcp_new_xid(ifp);
state->interval = 0;
send_renew(ifp);
@@ -2107,6 +2111,7 @@ dhcp_rebind(void *arg)
state->state = DHS_REBIND;
eloop_timeout_delete(ifp->ctx->eloop, send_renew, ifp);
state->lease.server.s_addr = INADDR_ANY;
+ state->nak_receive_count = 0;
state->interval = 0;
ifp->options->options &= ~(DHCPCD_CSR_WARNED |
DHCPCD_ROUTER_HOST_ROUTE_WARNED);
@@ -2320,10 +2325,13 @@ 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);
#ifdef ARP
@@ -2608,7 +2616,9 @@ dhcp_arp_address(struct interface *ifp)
return 0;
}
#else
- if (ifp->options->options & DHCPCD_ARP && ia == NULL) {
+ if ((ifp->options->options & DHCPCD_ARP && ia == NULL) ||
+ state->nak_receive_count > 0 ||
+ state->offer->yiaddr == state->failed.s_addr) {
struct dhcp_lease l;
state->state = DHS_PROBE;
@@ -3299,8 +3309,10 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,
if (state->state == DHS_INFORM) /* INFORM should not be NAKed */
return;
LOGDHCP(logwarnx, "NAK (deferred)");
- 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;
}
@@ -3388,6 +3400,12 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,
bootp_copied = false;
if ((type == 0 || type == DHCP_OFFER) && state->state == DHS_DISCOVER) {
+ if (bootp->yiaddr == state->failed.s_addr &&
+ state->failed_address_offer_count == 0) {
+ LOGDHCP(logwarnx, "reject previously declined address");
+ state->failed_address_offer_count++;
+ return;
+ }
lease->frominfo = 0;
lease->addr.s_addr = bootp->yiaddr;
memcpy(&lease->cookie, bootp->vend, sizeof(lease->cookie));
diff --git a/src/dhcp.h b/src/dhcp.h
index becf27c2..21716505 100644
--- a/src/dhcp.h
+++ b/src/dhcp.h
@@ -237,6 +237,9 @@ struct dhcp_state {
#ifdef ARPING
ssize_t arping_index;
#endif
+ int nak_receive_count;
+ int failed_address_offer_count;
+ struct in_addr failed;
struct dhcp_server_info server_info;
};
--
2.33.0.800.g4c38ced690-goog