arp: Handle initial announcement asynchronously and retry failures.

We need to send two ARP announcements, so these are now done via a
timeout callback so that failures can be handled properly.
This commit is contained in:
Nicholas J. Kain 2017-02-24 06:27:15 -05:00
parent 4d87d5075a
commit 34a8cd7ad9
4 changed files with 32 additions and 9 deletions

View File

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

View File

@ -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);

View File

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

View File

@ -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) {