| 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 |
| |