diff --git a/src/arp.c b/src/arp.c index f32b36c..7b04fa8 100644 --- a/src/arp.c +++ b/src/arp.c @@ -49,6 +49,10 @@ #define ARP_MSG_SIZE 0x2a #define ARP_RETRANS_DELAY 5000 // ms +// Maximum number of retries when finding the DHCP server ethernet address +// via ARP queries before assuming it is on a different segment. +#define MAX_DHCP_SERVER_HWADDR_QUERIES 1 + // From RFC5227 int arp_probe_wait = 1000; // initial random delay (ms) int arp_probe_num = 3; // number of probe packets @@ -568,6 +572,13 @@ static void arp_gw_check_timeout(struct client_state_t *cs, long long nowts) garp.send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY; } +static void arp_do_gw_query_done(struct client_state_t *cs) +{ + garp.wake_ts[AS_GW_QUERY] = -1; + arp_switch_state(cs, AS_DEFENSE); + arp_announcement(cs); // Do a second announcement. +} + static void arp_gw_query_timeout(struct client_state_t *cs, long long nowts) { arp_defense_timeout(cs, nowts); @@ -584,7 +595,17 @@ static void arp_gw_query_timeout(struct client_state_t *cs, long long nowts) log_warning("%s: arp: Failed to send ARP ping in retransmission.", client_config.interface); } + // Here it can be a bit tricky, since DHCP proxies do exist and can + // mean that the DHCP server will not be on the local segment and thus + // will not respond to ARP. Therefore, the serverAddr can only be + // treated as extra information that may or may not exist. if (!cs->got_server_arp) { + if (++cs->server_arp_tries > MAX_DHCP_SERVER_HWADDR_QUERIES) { + log_line("%s: arp: No ARP response from DHCP server after %d tries. Proceeding.", + client_config.interface, cs->server_arp_tries); + arp_do_gw_query_done(cs); + return; + } log_line("%s: arp: Still looking for DHCP server hardware address...", client_config.interface); if (arp_ping(cs, cs->serverAddr) < 0) @@ -646,13 +667,6 @@ static void arp_do_defense(struct client_state_t *cs) garp.last_conflict_ts = nowts; } -static void arp_do_gw_query_done(struct client_state_t *cs) -{ - garp.wake_ts[AS_GW_QUERY] = -1; - arp_switch_state(cs, AS_DEFENSE); - arp_announcement(cs); // Do a second announcement. -} - static void arp_do_gw_query(struct client_state_t *cs) { if (!arp_is_query_reply(&garp.reply)) { @@ -709,6 +723,13 @@ static void arp_do_gw_check(struct client_state_t *cs) // Success only if the router/gw MAC matches stored value if (!memcmp(cs->routerArp, garp.reply.smac, 6)) { garp.router_replied = true; + // Handle the case where the DHCP server is proxed from + // a different segment and will never reply. + if (!cs->got_server_arp && + cs->server_arp_tries > MAX_DHCP_SERVER_HWADDR_QUERIES) { + arp_gw_success(cs); + return; + } if (cs->routerAddr == cs->serverAddr) goto server_is_router; if (garp.server_replied) diff --git a/src/ndhc.h b/src/ndhc.h index d9ad0c4..c3d4903 100644 --- a/src/ndhc.h +++ b/src/ndhc.h @@ -42,6 +42,7 @@ struct client_state_t { int ifDeconfig; // Set if the interface has already been deconfigured. int epollFd, signalFd, listenFd, arpFd, nlFd; int nlPortId; + int server_arp_tries; uint32_t clientAddr, serverAddr, routerAddr; uint32_t lease, renewTime, rebindTime, xid; struct nk_random_state_u32 rnd32_state; diff --git a/src/state.c b/src/state.c index 93289e1..31db685 100644 --- a/src/state.c +++ b/src/state.c @@ -96,6 +96,7 @@ static void reinit_shared_deconfig(struct client_state_t *cs) num_dhcp_requests = 0; cs->got_router_arp = 0; cs->got_server_arp = 0; + cs->server_arp_tries = 0; memset(&cs->routerArp, 0, sizeof cs->routerArp); memset(&cs->serverArp, 0, sizeof cs->serverArp); arp_reset_send_stats();