aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Tan <samueltan@google.com>2015-08-13 16:18:58 -0700
committerSamuel Tan <samueltan@google.com>2015-08-18 13:46:39 -0700
commit1c4088e958ed0fded0040bcb1d67b73159acfe8c (patch)
tree6fd5bb4036b5aeb2c6edc1031a153e6cc196c493
parent9987cfd632cad92803f7cc9ae746d0a2513d3eaa (diff)
downloaddhcpcd-6.8.2-1c4088e958ed0fded0040bcb1d67b73159acfe8c.tar.gz
[PATCH] Optionally ARP for gateway IP address
Cherry-picked from https://chromium.googlesource.com/chromiumos/overlays/chromiumos-overlay/+/ master/net-misc/dhcpcd/files/patches/dhcpcd-6.8.2-Optionally-ARP-for-gateway- IP-address.patch. If the "arpgw" option is enabled in the config, we ARP for the gateway provided in the DHCP response as part of the process of testing our lease. If this fails (ARP times out) we DECLINE our lease in the hope that a new lease will work better. This can allow us to work around issues with infrastructures where IP address / MAC pairs are placed on a "dummy" VLAN under certain conditions. Requesting a different IP can sometimes help resolve this. The code is setup so that for each dhcpcd instance, the "arpgw" function is allowed to only fail once. This is to protect ourselves from mistakenly diagnosing a bad system, or from looping endlessly if the system is truly hosed. BUG: 22956197 Change-Id: I0f49882e85b2cac08dd3bd0046cacf564f8b4749 Reviewed-on: http://gerrit.chromium.org/gerrit/3080 Reviewed-on: http://gerrit.chromium.org/gerrit/3531
-rw-r--r--arp.c3
-rw-r--r--arp.h1
-rw-r--r--dhcp.c75
-rw-r--r--if-options.c6
-rw-r--r--if-options.h3
5 files changed, 85 insertions, 3 deletions
diff --git a/arp.c b/arp.c
index 01a8ba4..abb6d4e 100644
--- a/arp.c
+++ b/arp.c
@@ -279,7 +279,8 @@ arp_probe1(void *arg)
ifp->name, inet_ntoa(astate->addr),
astate->probes ? astate->probes : PROBE_NUM, PROBE_NUM,
timespec_to_double(&tv));
- if (arp_request(ifp, 0, astate->addr.s_addr) == -1)
+ if (arp_request(ifp, astate->src_addr.s_addr,
+ astate->addr.s_addr) == -1)
logger(ifp->ctx, LOG_ERR, "send_arp: %m");
}
diff --git a/arp.h b/arp.h
index 83422fe..8c50092 100644
--- a/arp.h
+++ b/arp.h
@@ -58,6 +58,7 @@ struct arp_state {
void (*announced_cb)(struct arp_state *);
void (*conflicted_cb)(struct arp_state *, const struct arp_msg *);
+ struct in_addr src_addr;
struct in_addr addr;
int probes;
int claims;
diff --git a/dhcp.c b/dhcp.c
index abdfc78..9cb50a7 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -2342,6 +2342,71 @@ whitelisted_ip(const struct if_options *ifo, in_addr_t addr)
}
static void
+dhcp_probe_gw_timeout(struct arp_state *astate) {
+ struct dhcp_state *state = D_STATE(astate->iface);
+
+ /* Allow ourselves to fail only once this way */
+ logger(astate->iface->ctx, LOG_ERR,
+ "%s: Probe gateway %s timed out ",
+ astate->iface->name, inet_ntoa(astate->addr));
+ astate->iface->options->options &= ~DHCPCD_ARPGW;
+
+ unlink(state->leasefile);
+ if (!state->lease.frominfo)
+ dhcp_decline(astate->iface);
+#ifdef IN_IFF_DUPLICATED
+ ia = ipv4_iffindaddr(astate->iface, &astate->addr, NULL);
+ if (ia)
+ ipv4_deladdr(astate->iface, &ia->addr, &ia->net);
+#endif
+ eloop_timeout_delete(astate->iface->ctx->eloop, NULL,
+ astate->iface);
+ eloop_timeout_add_sec(astate->iface->ctx->eloop,
+ DHCP_RAND_MAX, dhcp_discover, astate->iface);
+}
+
+static void
+dhcp_probe_gw_response(struct arp_state *astate, const struct arp_msg *amsg)
+{
+ /* Verify this is a response for the gateway probe. */
+ if (astate->src_addr.s_addr != 0 &&
+ amsg &&
+ amsg->tip.s_addr == astate->src_addr.s_addr &&
+ amsg->sip.s_addr == astate->addr.s_addr) {
+ dhcp_close(astate->iface);
+ eloop_timeout_delete(astate->iface->ctx->eloop,
+ NULL, astate->iface);
+#ifdef IN_IFF_TENTATIVE
+ ipv4_finaliseaddr(astate->iface);
+#else
+ dhcp_bind(astate->iface, NULL);
+#endif
+ arp_close(astate->iface);
+ }
+}
+
+static int
+dhcp_probe_gw(struct interface *ifp)
+{
+ struct dhcp_state *state = D_STATE(ifp);
+ struct arp_state *astate;
+ struct in_addr gateway_addr;
+
+ if (get_option_addr(ifp->ctx, &gateway_addr,
+ state->offer, DHO_ROUTER) == 0) {
+ astate = arp_new(ifp, &gateway_addr);
+ if (astate) {
+ astate->src_addr.s_addr = state->offer->yiaddr;
+ astate->probed_cb = dhcp_probe_gw_timeout;
+ astate->conflicted_cb = dhcp_probe_gw_response;
+ arp_probe(astate);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
dhcp_arp_probed(struct arp_state *astate)
{
struct dhcp_state *state;
@@ -2361,6 +2426,12 @@ dhcp_arp_probed(struct arp_state *astate)
dhcpcd_startinterface(astate->iface);
return;
}
+
+ /* Probe the gateway specified in the lease offer. */
+ if ((ifo->options & DHCPCD_ARPGW) && (dhcp_probe_gw(astate->iface))) {
+ return;
+ }
+
dhcp_close(astate->iface);
eloop_timeout_delete(astate->iface->ctx->eloop, NULL, astate->iface);
#ifdef IN_IFF_TENTATIVE
@@ -2782,6 +2853,10 @@ dhcp_handledhcp(struct interface *ifp, struct dhcp_message **dhcpp,
#endif
}
+ if ((ifo->options & DHCPCD_ARPGW) && (dhcp_probe_gw(ifp))) {
+ return;
+ }
+
dhcp_bind(ifp, astate);
}
diff --git a/if-options.c b/if-options.c
index 85b6e8e..c6226dd 100644
--- a/if-options.c
+++ b/if-options.c
@@ -141,7 +141,8 @@ const struct option cf_options[] = {
{"noipv4ll", no_argument, NULL, 'L'},
{"master", no_argument, NULL, 'M'},
{"nooption", optional_argument, NULL, 'O'},
- {"require", required_argument, NULL, 'Q'},
+ {"require", required_argument, NULL, 'Q'},
+ {"arpgw", no_argument, NULL, 'R'},
{"static", required_argument, NULL, 'S'},
{"test", no_argument, NULL, 'T'},
{"dumplease", no_argument, NULL, 'U'},
@@ -999,6 +1000,9 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
return -1;
}
break;
+ case 'R':
+ ifo->options |= DHCPCD_ARPGW;
+ break;
case 'S':
p = strchr(arg, '=');
if (p == NULL) {
diff --git a/if-options.h b/if-options.h
index 4d1de15..153f9b1 100644
--- a/if-options.h
+++ b/if-options.h
@@ -42,7 +42,7 @@
/* Don't set any optional arguments here so we retain POSIX
* compatibility with getopt */
#define IF_OPTS "46bc:de:f:gh:i:j:kl:m:no:pqr:s:t:u:v:wxy:z:" \
- "ABC:DEF:GHI:JKLMO:Q:S:TUVW:X:Z:"
+ "ABC:DEF:GHI:JKLMO:Q:RS:TUVW:X:Z:"
#define DEFAULT_TIMEOUT 30
#define DEFAULT_REBOOT 5
@@ -111,6 +111,7 @@
#define DHCPCD_ROUTER_HOST_ROUTE_WARNED (1ULL << 55)
#define DHCPCD_IPV6RA_ACCEPT_NOPUBLIC (1ULL << 56)
#define DHCPCD_BOOTP (1ULL << 57)
+#define DHCPCD_ARPGW (1ULL << 58)
#define DHCPCD_WARNINGS (DHCPCD_CSR_WARNED | \
DHCPCD_ROUTER_HOST_ROUTE_WARNED)