diff --git a/src/arp.c b/src/arp.c index 14299b1..30aa73d 100644 --- a/src/arp.c +++ b/src/arp.c @@ -63,7 +63,7 @@ int arp_probe_max = 2000; // maximum delay until repeated probe (ms) #define DEFEND_INTERVAL 10000 // minimum interval between defensive ARPs static struct arp_data garp = { - .wake_ts = { -1, -1, -1, -1, -1 }, + .wake_ts = { -1, -1, -1, -1, -1, -1 }, .send_stats = {{0,0},{0,0},{0,0}}, .last_conflict_ts = 0, .gw_check_initpings = 0, @@ -565,14 +565,36 @@ int arp_query_gateway(struct client_state_t cs[static 1]) int arp_announce(struct client_state_t cs[static 1]) { + if (cs->sent_first_announce && cs->sent_second_announce) { + garp.wake_ts[AS_ANNOUNCE] = -1; + return ARPR_OK; + } if (arp_announcement(cs) < 0) { log_warning("%s: (%s) Failed to send ARP announcement: %s", client_config.interface, __func__, strerror(errno)); + garp.wake_ts[AS_ANNOUNCE] = curms() + ARP_RETRANS_DELAY ; return ARPR_FAIL; } + if (!cs->sent_first_announce) + cs->sent_first_announce = true; + else if (!cs->sent_second_announce) + cs->sent_second_announce = true; + if (!cs->sent_first_announce || !cs->sent_second_announce) + garp.wake_ts[AS_ANNOUNCE] = curms() + ARP_RETRANS_DELAY; + else + garp.wake_ts[AS_ANNOUNCE] = -1; return ARPR_OK; } +// 1 == not yet time, 0 == timed out, success, -1 == timed out, failure +int arp_announce_timeout(struct client_state_t cs[static 1], long long nowts) +{ + long long rtts = garp.wake_ts[AS_ANNOUNCE]; + if (rtts == -1) return 0; + if (nowts < rtts) return 1; + return arp_announce(cs) == ARPR_OK ? 0 : -1; +} + int arp_do_defense(struct client_state_t cs[static 1]) { // Even though the BPF will usually catch this case, sometimes there are @@ -620,9 +642,6 @@ int arp_do_gw_query(struct client_state_t cs[static 1]) garp.wake_ts[AS_GW_QUERY] = -1; if (arp_open_fd(cs, true) < 0) return ARPR_FAIL; - // Do a second announcement. - if (arp_announcement(cs) < 0) - return ARPR_FAIL; return ARPR_FREE; } return ARPR_OK; @@ -639,9 +658,6 @@ server_is_router: garp.wake_ts[AS_GW_QUERY] = -1; if (arp_open_fd(cs, true) < 0) return ARPR_FAIL; - // Do a second announcement. - if (arp_announcement(cs) < 0) - return ARPR_FAIL; return ARPR_FREE; } return ARPR_OK; diff --git a/src/arp.h b/src/arp.h index a87adc1..2279f55 100644 --- a/src/arp.h +++ b/src/arp.h @@ -60,6 +60,7 @@ extern int arp_probe_max; typedef enum { AS_NONE = 0, // Nothing to react to wrt ARP + AS_ANNOUNCE, // Announcing our MAC/IP mapping to ethernet peers. AS_COLLISION_CHECK, // Checking to see if another host has our IP before // accepting a new lease. AS_GW_CHECK, // Seeing if the default GW still exists on the local @@ -119,7 +120,9 @@ int arp_do_collision_check(struct client_state_t cs[static 1]); int arp_collision_timeout(struct client_state_t cs[static 1], long long nowts); int arp_query_gateway(struct client_state_t cs[static 1]); + int arp_announce(struct client_state_t cs[static 1]); +int arp_announce_timeout(struct client_state_t cs[static 1], long long nowts); int arp_do_defense(struct client_state_t cs[static 1]); int arp_defense_timeout(struct client_state_t cs[static 1], long long nowts); diff --git a/src/ndhc.h b/src/ndhc.h index b0a85c0..cd527ce 100644 --- a/src/ndhc.h +++ b/src/ndhc.h @@ -47,6 +47,7 @@ struct client_state_t { uint8_t routerArp[6], serverArp[6]; bool using_dhcp_bpf, got_router_arp, got_server_arp, arp_is_defense, check_fingerprint, program_init; + bool sent_gw_query, sent_first_announce, sent_second_announce; }; struct client_config_t { diff --git a/src/state.c b/src/state.c index b486664..e1175eb 100644 --- a/src/state.c +++ b/src/state.c @@ -537,8 +537,9 @@ skip_to_requesting: if (arp_timeout) { int r = arp_collision_timeout(cs, nowts); if (r == ARPR_FREE) { - arp_query_gateway(cs); // XXX: Handle failure - arp_announce(cs); // XXX: Handle failure + if (arp_query_gateway(cs) == ARPR_OK) + cs->sent_gw_query = true; // XXX: Handle the false case + arp_announce(cs); break; } else if (r == ARPR_OK) { } else if (r == ARPR_FAIL) { @@ -643,6 +644,8 @@ skip_to_requesting: } if (arp_timeout) { arp_defense_timeout(cs, nowts); + if (!cs->sent_first_announce || !cs->sent_second_announce) + arp_announce_timeout(cs, nowts); if (!cs->got_router_arp || !cs->got_server_arp) { int r = arp_gw_query_timeout(cs, nowts); if (r == ARPR_OK) {