Consolidate all of the global static variables in arp.c into a single

struct, and use booleans where appropriate.
This commit is contained in:
Nicholas J. Kain 2014-04-06 22:12:31 -04:00
parent a86363f248
commit e2ee728982
3 changed files with 160 additions and 144 deletions

300
src/arp.c
View File

@ -61,7 +61,6 @@ int arp_probe_max = 2000; // maximum delay until repeated probe (ms)
#define RATE_LIMIT_INTERVAL 60000 // delay between successive attempts #define RATE_LIMIT_INTERVAL 60000 // delay between successive attempts
#define DEFEND_INTERVAL 10000 // minimum interval between defensive ARPs #define DEFEND_INTERVAL 10000 // minimum interval between defensive ARPs
typedef enum { typedef enum {
AS_NONE = 0, // Nothing to react to wrt ARP AS_NONE = 0, // Nothing to react to wrt ARP
AS_COLLISION_CHECK, // Checking to see if another host has our IP before AS_COLLISION_CHECK, // Checking to see if another host has our IP before
@ -73,8 +72,6 @@ typedef enum {
AS_MAX, AS_MAX,
} arp_state_t; } arp_state_t;
static long long arp_wake_ts[AS_MAX] = { -1, -1, -1, -1, -1 };
typedef enum { typedef enum {
ASEND_COLLISION_CHECK, ASEND_COLLISION_CHECK,
ASEND_GW_PING, ASEND_GW_PING,
@ -82,45 +79,62 @@ typedef enum {
ASEND_MAX, ASEND_MAX,
} arp_send_t; } arp_send_t;
static arp_state_t arpState;
struct arp_stats { struct arp_stats {
long long ts; long long ts;
int count; int count;
}; };
static struct arp_stats arp_send_stats[ASEND_MAX];
static int using_arp_bpf; // Is a BPF installed on the ARP socket?
int arp_relentless_def; // Don't give up defense no matter what. struct arp_data {
static long long last_conflict_ts; // TS of the last conflicting ARP seen. struct dhcpmsg dhcp_packet; // Used only for AS_COLLISION_CHECK
struct arpMsg reply;
struct arp_stats send_stats[ASEND_MAX];
long long wake_ts[AS_MAX];
long long last_conflict_ts; // TS of the last conflicting ARP seen.
long long arp_check_start_ts; // TS of when we started the
// AS_COLLISION_CHECK state.
size_t reply_offset;
arp_state_t state;
unsigned int total_conflicts; // Total number of address conflicts on
// the interface. Never decreases.
int gw_check_initpings; // Initial count of ASEND_GW_PING when
// AS_GW_CHECK was entered.
uint16_t probe_wait_time; // Time to wait for a COLLISION_CHECK reply
// (in ms?).
bool using_bpf:1; // Is a BPF installed on the ARP socket?
bool relentless_def:1; // Don't give up defense no matter what.
bool router_replied:1;
bool server_replied:1;
};
static int gw_check_init_pingcount; // Initial count of ASEND_GW_PING when static struct arp_data garp = {
// AS_GW_CHECK was entered. .state = AS_NONE,
.wake_ts = { -1, -1, -1, -1, -1 },
.send_stats = {{0},{0},{0}},
.last_conflict_ts = 0,
.gw_check_initpings = 0,
.arp_check_start_ts = 0,
.total_conflicts = 0,
.probe_wait_time = 0,
.reply_offset = 0,
.using_bpf = false,
.relentless_def = false,
.router_replied = false,
.server_replied = false,
};
static uint16_t probe_wait_time; // Time to wait for a COLLISION_CHECK reply. void set_arp_relentless_def(void) { garp.relentless_def = true; }
static long long arp_check_start_ts; // TS of when we started the
// AS_COLLISION_CHECK state.
static unsigned int total_conflicts; // Total number of address conflicts on static void arp_reply_clear(void)
// the interface. Never decreases.
static struct dhcpmsg arp_dhcp_packet; // Used only for AS_COLLISION_CHECK
static char arp_router_has_replied;
static char arp_server_has_replied;
static struct arpMsg arpreply;
static size_t arpreply_offset;
static void arpreply_clear(void)
{ {
memset(&arpreply, 0, sizeof arpreply); memset(&garp.reply, 0, sizeof garp.reply);
arpreply_offset = 0; garp.reply_offset = 0;
} }
void arp_reset_send_stats(void) void arp_reset_send_stats(void)
{ {
for (int i = 0; i < ASEND_MAX; ++i) { for (int i = 0; i < ASEND_MAX; ++i) {
arp_send_stats[i].ts = 0; garp.send_stats[i].ts = 0;
arp_send_stats[i].count = 0; garp.send_stats[i].count = 0;
} }
} }
@ -129,8 +143,8 @@ static int get_arp_basic_socket(void)
char resp; char resp;
int fd = request_sockd_fd("a", 1, &resp); int fd = request_sockd_fd("a", 1, &resp);
switch (resp) { switch (resp) {
case 'A': using_arp_bpf = 1; break; case 'A': garp.using_bpf = true; break;
case 'a': using_arp_bpf = 0; break; case 'a': garp.using_bpf = false; break;
default: suicide("%s: (%s) expected a or A sockd reply but got %c", default: suicide("%s: (%s) expected a or A sockd reply but got %c",
client_config.interface, __func__, resp); client_config.interface, __func__, resp);
} }
@ -150,8 +164,8 @@ static int get_arp_defense_socket(struct client_state_t *cs)
char resp; char resp;
int fd = request_sockd_fd(buf, buflen, &resp); int fd = request_sockd_fd(buf, buflen, &resp);
switch (resp) { switch (resp) {
case 'D': using_arp_bpf = 1; break; case 'D': garp.using_bpf = true; break;
case 'd': using_arp_bpf = 0; break; case 'd': garp.using_bpf = false; break;
default: suicide("%s: (%s) expected d or D sockd reply but got %c", default: suicide("%s: (%s) expected d or D sockd reply but got %c",
client_config.interface, __func__, resp); client_config.interface, __func__, resp);
} }
@ -180,7 +194,7 @@ static int arp_open_fd(struct client_state_t *cs, arp_state_t state)
return -1; return -1;
} }
epoll_add(cs->epollFd, cs->arpFd); epoll_add(cs->epollFd, cs->arpFd);
arpreply_clear(); arp_reply_clear();
return 0; return 0;
} }
@ -191,38 +205,38 @@ static void arp_min_close_fd(struct client_state_t *cs)
epoll_del(cs->epollFd, cs->arpFd); epoll_del(cs->epollFd, cs->arpFd);
close(cs->arpFd); close(cs->arpFd);
cs->arpFd = -1; cs->arpFd = -1;
arpState = AS_NONE; garp.state = AS_NONE;
} }
static void arp_switch_state(struct client_state_t *cs, arp_state_t state) static void arp_switch_state(struct client_state_t *cs, arp_state_t state)
{ {
if (arpState == state || arpState >= AS_MAX) if (garp.state == state || garp.state >= AS_MAX)
return; return;
if (state == AS_NONE) { if (state == AS_NONE) {
arp_close_fd(cs); arp_close_fd(cs);
return; return;
} }
bool force_reopen = state == AS_DEFENSE || arpState == AS_DEFENSE; bool force_reopen = state == AS_DEFENSE || garp.state == AS_DEFENSE;
if (force_reopen) if (force_reopen)
arp_min_close_fd(cs); arp_min_close_fd(cs);
if (cs->arpFd < 0 || force_reopen) { if (cs->arpFd < 0 || force_reopen) {
if (arp_open_fd(cs, state) < 0) if (arp_open_fd(cs, state) < 0)
suicide("arp: Failed to open arpFd when changing state %u -> %u", suicide("arp: Failed to open arpFd when changing state %u -> %u",
arpState, state); garp.state, state);
} }
arpState = state; garp.state = state;
} }
void arp_close_fd(struct client_state_t *cs) void arp_close_fd(struct client_state_t *cs)
{ {
arp_min_close_fd(cs); arp_min_close_fd(cs);
for (int i = 0; i < AS_MAX; ++i) for (int i = 0; i < AS_MAX; ++i)
arp_wake_ts[i] = -1; garp.wake_ts[i] = -1;
} }
static void arp_reopen_fd(struct client_state_t *cs) static void arp_reopen_fd(struct client_state_t *cs)
{ {
arp_state_t prev_state = arpState; arp_state_t prev_state = garp.state;
arp_min_close_fd(cs); arp_min_close_fd(cs);
arp_switch_state(cs, prev_state); arp_switch_state(cs, prev_state);
} }
@ -270,8 +284,8 @@ static int arp_ping(struct client_state_t *cs, uint32_t test_ip)
memcpy(arp.dip4, &test_ip, sizeof test_ip); memcpy(arp.dip4, &test_ip, sizeof test_ip);
if (arp_send(cs, &arp) < 0) if (arp_send(cs, &arp) < 0)
return -1; return -1;
arp_send_stats[ASEND_GW_PING].count++; garp.send_stats[ASEND_GW_PING].count++;
arp_send_stats[ASEND_GW_PING].ts = curms(); garp.send_stats[ASEND_GW_PING].ts = curms();
return 0; return 0;
} }
@ -283,8 +297,8 @@ static int arp_ip_anon_ping(struct client_state_t *cs, uint32_t test_ip)
log_line("arp: Probing for hosts that may conflict with our lease..."); log_line("arp: Probing for hosts that may conflict with our lease...");
if (arp_send(cs, &arp) < 0) if (arp_send(cs, &arp) < 0)
return -1; return -1;
arp_send_stats[ASEND_COLLISION_CHECK].count++; garp.send_stats[ASEND_COLLISION_CHECK].count++;
arp_send_stats[ASEND_COLLISION_CHECK].ts = curms(); garp.send_stats[ASEND_COLLISION_CHECK].ts = curms();
return 0; return 0;
} }
@ -295,8 +309,8 @@ static int arp_announcement(struct client_state_t *cs)
memcpy(arp.dip4, &cs->clientAddr, 4); memcpy(arp.dip4, &cs->clientAddr, 4);
if (arp_send(cs, &arp) < 0) if (arp_send(cs, &arp) < 0)
return -1; return -1;
arp_send_stats[ASEND_ANNOUNCE].count++; garp.send_stats[ASEND_ANNOUNCE].count++;
arp_send_stats[ASEND_ANNOUNCE].ts = curms(); garp.send_stats[ASEND_ANNOUNCE].ts = curms();
return 0; return 0;
} }
#undef BASE_ARPMSG #undef BASE_ARPMSG
@ -304,38 +318,39 @@ static int arp_announcement(struct client_state_t *cs)
// Callable from DS_REQUESTING, DS_RENEWING, or DS_REBINDING via an_packet() // Callable from DS_REQUESTING, DS_RENEWING, or DS_REBINDING via an_packet()
int arp_check(struct client_state_t *cs, struct dhcpmsg *packet) int arp_check(struct client_state_t *cs, struct dhcpmsg *packet)
{ {
memcpy(&arp_dhcp_packet, packet, sizeof (struct dhcpmsg)); memcpy(&garp.dhcp_packet, packet, sizeof (struct dhcpmsg));
arp_switch_state(cs, AS_COLLISION_CHECK); arp_switch_state(cs, AS_COLLISION_CHECK);
if (arp_ip_anon_ping(cs, arp_dhcp_packet.yiaddr) < 0) if (arp_ip_anon_ping(cs, garp.dhcp_packet.yiaddr) < 0)
return -1; return -1;
cs->arpPrevState = cs->dhcpState; cs->arpPrevState = cs->dhcpState;
cs->dhcpState = DS_COLLISION_CHECK; cs->dhcpState = DS_COLLISION_CHECK;
arp_check_start_ts = arp_send_stats[ASEND_COLLISION_CHECK].ts; garp.arp_check_start_ts = garp.send_stats[ASEND_COLLISION_CHECK].ts;
probe_wait_time = arp_probe_wait; garp.probe_wait_time = arp_probe_wait;
arp_wake_ts[AS_COLLISION_CHECK] = arp_check_start_ts + probe_wait_time; garp.wake_ts[AS_COLLISION_CHECK] = garp.arp_check_start_ts
+ garp.probe_wait_time;
return 0; return 0;
} }
// Callable only from DS_BOUND via state.c:ifup_action(). // Callable only from DS_BOUND via state.c:ifup_action().
int arp_gw_check(struct client_state_t *cs) int arp_gw_check(struct client_state_t *cs)
{ {
if (arpState == AS_GW_CHECK) // Guard against state bounce. if (garp.state == AS_GW_CHECK) // Guard against state bounce.
return 0; return 0;
gw_check_init_pingcount = arp_send_stats[ASEND_GW_PING].count; garp.gw_check_initpings = garp.send_stats[ASEND_GW_PING].count;
arp_server_has_replied = 0; garp.server_replied = false;
if (arp_ping(cs, cs->serverAddr) < 0) if (arp_ping(cs, cs->serverAddr) < 0)
return -1; return -1;
if (cs->routerAddr) { if (cs->routerAddr) {
arp_router_has_replied = 0; garp.router_replied = false;
if (arp_ping(cs, cs->routerAddr) < 0) if (arp_ping(cs, cs->routerAddr) < 0)
return -1; return -1;
} else } else
arp_router_has_replied = 1; garp.router_replied = true;
arp_switch_state(cs, AS_GW_CHECK); arp_switch_state(cs, AS_GW_CHECK);
cs->arpPrevState = cs->dhcpState; cs->arpPrevState = cs->dhcpState;
cs->dhcpState = DS_BOUND_GW_CHECK; cs->dhcpState = DS_BOUND_GW_CHECK;
arp_wake_ts[AS_GW_CHECK] = garp.wake_ts[AS_GW_CHECK] =
arp_send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY + 250; garp.send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY + 250;
return 0; return 0;
} }
@ -358,32 +373,32 @@ static int arp_get_gw_hwaddr(struct client_state_t *cs)
return -1; return -1;
} else } else
cs->got_router_arp = 1; cs->got_router_arp = 1;
arp_wake_ts[AS_GW_QUERY] = garp.wake_ts[AS_GW_QUERY] =
arp_send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY + 250; garp.send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY + 250;
return 0; return 0;
} }
static void arp_failed(struct client_state_t *cs) static void arp_failed(struct client_state_t *cs)
{ {
log_line("arp: Offered address is in use. Declining."); log_line("arp: Offered address is in use. Declining.");
send_decline(cs, arp_dhcp_packet.yiaddr); send_decline(cs, garp.dhcp_packet.yiaddr);
arp_wake_ts[AS_COLLISION_CHECK] = -1; garp.wake_ts[AS_COLLISION_CHECK] = -1;
reinit_selecting(cs, total_conflicts < MAX_CONFLICTS ? reinit_selecting(cs, garp.total_conflicts < MAX_CONFLICTS ?
0 : RATE_LIMIT_INTERVAL); 0 : RATE_LIMIT_INTERVAL);
} }
static void arp_gw_failed(struct client_state_t *cs) static void arp_gw_failed(struct client_state_t *cs)
{ {
arp_wake_ts[AS_GW_CHECK] = -1; garp.wake_ts[AS_GW_CHECK] = -1;
reinit_selecting(cs, 0); reinit_selecting(cs, 0);
} }
static int act_if_arp_gw_failed(struct client_state_t *cs) static int act_if_arp_gw_failed(struct client_state_t *cs)
{ {
if (arp_send_stats[ASEND_GW_PING].count >= gw_check_init_pingcount + 6) { if (garp.send_stats[ASEND_GW_PING].count >= garp.gw_check_initpings + 6) {
if (arp_router_has_replied && !arp_server_has_replied) if (garp.router_replied && !garp.server_replied)
log_line("arp: DHCP server didn't reply. Getting new lease."); log_line("arp: DHCP server didn't reply. Getting new lease.");
else if (!arp_router_has_replied && arp_server_has_replied) else if (!garp.router_replied && garp.server_replied)
log_line("arp: Gateway didn't reply. Getting new lease."); log_line("arp: Gateway didn't reply. Getting new lease.");
else else
log_line("arp: DHCP server and gateway didn't reply. Getting new lease."); log_line("arp: DHCP server and gateway didn't reply. Getting new lease.");
@ -401,20 +416,20 @@ void arp_set_defense_mode(struct client_state_t *cs)
void arp_success(struct client_state_t *cs) void arp_success(struct client_state_t *cs)
{ {
char clibuf[INET_ADDRSTRLEN]; char clibuf[INET_ADDRSTRLEN];
struct in_addr temp_addr = {.s_addr = arp_dhcp_packet.yiaddr}; struct in_addr temp_addr = {.s_addr = garp.dhcp_packet.yiaddr};
inet_ntop(AF_INET, &temp_addr, clibuf, sizeof clibuf); inet_ntop(AF_INET, &temp_addr, clibuf, sizeof clibuf);
log_line("Lease of %s obtained. Lease time is %ld seconds.", log_line("Lease of %s obtained. Lease time is %ld seconds.",
clibuf, cs->lease); clibuf, cs->lease);
cs->clientAddr = arp_dhcp_packet.yiaddr; cs->clientAddr = garp.dhcp_packet.yiaddr;
cs->dhcpState = DS_BOUND; cs->dhcpState = DS_BOUND;
cs->init = 0; cs->init = 0;
last_conflict_ts = 0; garp.last_conflict_ts = 0;
arp_wake_ts[AS_COLLISION_CHECK] = -1; garp.wake_ts[AS_COLLISION_CHECK] = -1;
ifchange_bind(cs, &arp_dhcp_packet); ifchange_bind(cs, &garp.dhcp_packet);
if (cs->arpPrevState == DS_RENEWING || cs->arpPrevState == DS_REBINDING) { if (cs->arpPrevState == DS_RENEWING || cs->arpPrevState == DS_REBINDING) {
arp_switch_state(cs, AS_DEFENSE); arp_switch_state(cs, AS_DEFENSE);
} else { } else {
cs->routerAddr = get_option_router(&arp_dhcp_packet); cs->routerAddr = get_option_router(&garp.dhcp_packet);
arp_get_gw_hwaddr(cs); arp_get_gw_hwaddr(cs);
} }
set_listen_none(cs); set_listen_none(cs);
@ -432,7 +447,7 @@ static void arp_gw_success(struct client_state_t *cs)
arp_switch_state(cs, AS_DEFENSE); arp_switch_state(cs, AS_DEFENSE);
arp_announcement(cs); arp_announcement(cs);
arp_wake_ts[AS_GW_CHECK] = -1; garp.wake_ts[AS_GW_CHECK] = -1;
cs->dhcpState = cs->arpPrevState; cs->dhcpState = cs->arpPrevState;
} }
@ -496,10 +511,10 @@ static int arp_gen_probe_wait(struct client_state_t *cs)
static void arp_defense_timeout(struct client_state_t *cs, long long nowts) static void arp_defense_timeout(struct client_state_t *cs, long long nowts)
{ {
(void)nowts; // Suppress warning; parameter necessary but unused. (void)nowts; // Suppress warning; parameter necessary but unused.
if (arp_wake_ts[AS_DEFENSE] != -1) { if (garp.wake_ts[AS_DEFENSE] != -1) {
log_line("arp: Defending our lease IP."); log_line("arp: Defending our lease IP.");
arp_announcement(cs); arp_announcement(cs);
arp_wake_ts[AS_DEFENSE] = -1; garp.wake_ts[AS_DEFENSE] = -1;
} }
} }
@ -509,32 +524,32 @@ static void arp_gw_check_timeout(struct client_state_t *cs, long long nowts)
if (act_if_arp_gw_failed(cs)) if (act_if_arp_gw_failed(cs))
return; return;
long long rtts = arp_send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY; long long rtts = garp.send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY;
if (nowts < rtts) { if (nowts < rtts) {
arp_wake_ts[AS_GW_CHECK] = rtts; garp.wake_ts[AS_GW_CHECK] = rtts;
return; return;
} }
if (!arp_router_has_replied) { if (!garp.router_replied) {
log_line("arp: Still waiting for gateway to reply to arp ping..."); log_line("arp: Still waiting for gateway to reply to arp ping...");
if (arp_ping(cs, cs->routerAddr) < 0) if (arp_ping(cs, cs->routerAddr) < 0)
log_warning("arp: Failed to send ARP ping in retransmission."); log_warning("arp: Failed to send ARP ping in retransmission.");
} }
if (!arp_server_has_replied) { if (!garp.server_replied) {
log_line("arp: Still waiting for DHCP server to reply to arp ping..."); log_line("arp: Still waiting for DHCP server to reply to arp ping...");
if (arp_ping(cs, cs->serverAddr) < 0) if (arp_ping(cs, cs->serverAddr) < 0)
log_warning("arp: Failed to send ARP ping in retransmission."); log_warning("arp: Failed to send ARP ping in retransmission.");
} }
arp_wake_ts[AS_GW_CHECK] = garp.wake_ts[AS_GW_CHECK] =
arp_send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY; garp.send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY;
} }
static void arp_gw_query_timeout(struct client_state_t *cs, long long nowts) static void arp_gw_query_timeout(struct client_state_t *cs, long long nowts)
{ {
arp_defense_timeout(cs, nowts); arp_defense_timeout(cs, nowts);
long long rtts = arp_send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY; long long rtts = garp.send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY;
if (nowts < rtts) { if (nowts < rtts) {
arp_wake_ts[AS_GW_QUERY] = rtts; garp.wake_ts[AS_GW_QUERY] = rtts;
return; return;
} }
if (!cs->got_router_arp) { if (!cs->got_router_arp) {
@ -547,30 +562,30 @@ static void arp_gw_query_timeout(struct client_state_t *cs, long long nowts)
if (arp_ping(cs, cs->serverAddr) < 0) if (arp_ping(cs, cs->serverAddr) < 0)
log_warning("arp: Failed to send ARP ping in retransmission."); log_warning("arp: Failed to send ARP ping in retransmission.");
} }
arp_wake_ts[AS_GW_QUERY] = garp.wake_ts[AS_GW_QUERY] =
arp_send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY; garp.send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY;
} }
static void arp_collision_timeout(struct client_state_t *cs, long long nowts) static void arp_collision_timeout(struct client_state_t *cs, long long nowts)
{ {
arp_defense_timeout(cs, nowts); arp_defense_timeout(cs, nowts);
if (nowts >= arp_check_start_ts + ANNOUNCE_WAIT || if (nowts >= garp.arp_check_start_ts + ANNOUNCE_WAIT ||
arp_send_stats[ASEND_COLLISION_CHECK].count >= arp_probe_num) { garp.send_stats[ASEND_COLLISION_CHECK].count >= arp_probe_num) {
arp_success(cs); arp_success(cs);
return; return;
} }
long long rtts = arp_send_stats[ASEND_COLLISION_CHECK].ts + long long rtts = garp.send_stats[ASEND_COLLISION_CHECK].ts +
probe_wait_time; garp.probe_wait_time;
if (nowts < rtts) { if (nowts < rtts) {
arp_wake_ts[AS_COLLISION_CHECK] = rtts; garp.wake_ts[AS_COLLISION_CHECK] = rtts;
return; return;
} }
if (arp_ip_anon_ping(cs, arp_dhcp_packet.yiaddr) < 0) if (arp_ip_anon_ping(cs, garp.dhcp_packet.yiaddr) < 0)
log_warning("arp: Failed to send ARP ping in retransmission."); log_warning("arp: Failed to send ARP ping in retransmission.");
probe_wait_time = arp_gen_probe_wait(cs); garp.probe_wait_time = arp_gen_probe_wait(cs);
arp_wake_ts[AS_COLLISION_CHECK] = garp.wake_ts[AS_COLLISION_CHECK] =
arp_send_stats[ASEND_COLLISION_CHECK].ts + probe_wait_time; garp.send_stats[ASEND_COLLISION_CHECK].ts + garp.probe_wait_time;
} }
static void arp_do_defense(struct client_state_t *cs) static void arp_do_defense(struct client_state_t *cs)
@ -578,43 +593,43 @@ static void arp_do_defense(struct client_state_t *cs)
// Even though the BPF will usually catch this case, sometimes there are // Even though the BPF will usually catch this case, sometimes there are
// packets still in the socket buffer that arrived before the defense // packets still in the socket buffer that arrived before the defense
// BPF was installed, so it's necessary to check here. // BPF was installed, so it's necessary to check here.
if (!arp_validate_bpf_defense(cs, &arpreply)) if (!arp_validate_bpf_defense(cs, &garp.reply))
return; return;
log_line("arp: Detected a peer attempting to use our IP!"); log_line("arp: Detected a peer attempting to use our IP!");
long long nowts = curms(); long long nowts = curms();
arp_wake_ts[AS_DEFENSE] = -1; garp.wake_ts[AS_DEFENSE] = -1;
if (!last_conflict_ts || if (!garp.last_conflict_ts ||
nowts - last_conflict_ts < DEFEND_INTERVAL) { nowts - garp.last_conflict_ts < DEFEND_INTERVAL) {
log_line("arp: Defending our lease IP."); log_line("arp: Defending our lease IP.");
arp_announcement(cs); arp_announcement(cs);
} else if (!arp_relentless_def) { } else if (!garp.relentless_def) {
log_line("arp: Conflicting peer is persistent. Requesting new lease."); log_line("arp: Conflicting peer is persistent. Requesting new lease.");
send_release(cs); send_release(cs);
reinit_selecting(cs, 0); reinit_selecting(cs, 0);
} else { } else {
arp_wake_ts[AS_DEFENSE] = garp.wake_ts[AS_DEFENSE] =
arp_send_stats[ASEND_ANNOUNCE].ts + DEFEND_INTERVAL; garp.send_stats[ASEND_ANNOUNCE].ts + DEFEND_INTERVAL;
} }
total_conflicts++; garp.total_conflicts++;
last_conflict_ts = nowts; garp.last_conflict_ts = nowts;
} }
static void arp_do_gw_query_done(struct client_state_t *cs) static void arp_do_gw_query_done(struct client_state_t *cs)
{ {
arp_wake_ts[AS_GW_QUERY] = -1; garp.wake_ts[AS_GW_QUERY] = -1;
arp_switch_state(cs, AS_DEFENSE); arp_switch_state(cs, AS_DEFENSE);
arp_announcement(cs); // Do a second announcement. arp_announcement(cs); // Do a second announcement.
} }
static void arp_do_gw_query(struct client_state_t *cs) static void arp_do_gw_query(struct client_state_t *cs)
{ {
if (!arp_is_query_reply(&arpreply)) { if (!arp_is_query_reply(&garp.reply)) {
arp_do_defense(cs); arp_do_defense(cs);
return; return;
} }
if (!memcmp(arpreply.sip4, &cs->routerAddr, 4)) { if (!memcmp(garp.reply.sip4, &cs->routerAddr, 4)) {
memcpy(cs->routerArp, arpreply.smac, 6); memcpy(cs->routerArp, garp.reply.smac, 6);
log_line("arp: Gateway hardware address %02x:%02x:%02x:%02x:%02x:%02x", log_line("arp: Gateway hardware address %02x:%02x:%02x:%02x:%02x:%02x",
cs->routerArp[0], cs->routerArp[1], cs->routerArp[0], cs->routerArp[1],
cs->routerArp[2], cs->routerArp[3], cs->routerArp[2], cs->routerArp[3],
@ -626,9 +641,9 @@ static void arp_do_gw_query(struct client_state_t *cs)
arp_do_gw_query_done(cs); arp_do_gw_query_done(cs);
return; return;
} }
if (!memcmp(arpreply.sip4, &cs->serverAddr, 4)) { if (!memcmp(garp.reply.sip4, &cs->serverAddr, 4)) {
server_is_router: server_is_router:
memcpy(cs->serverArp, arpreply.smac, 6); memcpy(cs->serverArp, garp.reply.smac, 6);
log_line("arp: DHCP Server hardware address %02x:%02x:%02x:%02x:%02x:%02x", log_line("arp: DHCP Server hardware address %02x:%02x:%02x:%02x:%02x:%02x",
cs->serverArp[0], cs->serverArp[1], cs->serverArp[0], cs->serverArp[1],
cs->serverArp[2], cs->serverArp[3], cs->serverArp[2], cs->serverArp[3],
@ -643,29 +658,29 @@ server_is_router:
static void arp_do_collision_check(struct client_state_t *cs) static void arp_do_collision_check(struct client_state_t *cs)
{ {
if (!arp_is_query_reply(&arpreply)) if (!arp_is_query_reply(&garp.reply))
return; return;
// If this packet was sent from our lease IP, and does not have a // If this packet was sent from our lease IP, and does not have a
// MAC address matching our own (the latter check guards against stupid // MAC address matching our own (the latter check guards against stupid
// hubs or repeaters), then it's a conflict and thus a failure. // hubs or repeaters), then it's a conflict and thus a failure.
if (!memcmp(arpreply.sip4, &arp_dhcp_packet.yiaddr, 4) && if (!memcmp(garp.reply.sip4, &garp.dhcp_packet.yiaddr, 4) &&
!memcmp(client_config.arp, arpreply.smac, 6)) { !memcmp(client_config.arp, garp.reply.smac, 6)) {
total_conflicts++; garp.total_conflicts++;
arp_failed(cs); arp_failed(cs);
} }
} }
static void arp_do_gw_check(struct client_state_t *cs) static void arp_do_gw_check(struct client_state_t *cs)
{ {
if (!arp_is_query_reply(&arpreply)) if (!arp_is_query_reply(&garp.reply))
return; return;
if (!memcmp(arpreply.sip4, &cs->routerAddr, 4)) { if (!memcmp(garp.reply.sip4, &cs->routerAddr, 4)) {
// Success only if the router/gw MAC matches stored value // Success only if the router/gw MAC matches stored value
if (!memcmp(cs->routerArp, arpreply.smac, 6)) { if (!memcmp(cs->routerArp, garp.reply.smac, 6)) {
arp_router_has_replied = 1; garp.router_replied = true;
if (cs->routerAddr == cs->serverAddr) if (cs->routerAddr == cs->serverAddr)
goto server_is_router; goto server_is_router;
if (arp_server_has_replied) if (garp.server_replied)
arp_gw_success(cs); arp_gw_success(cs);
} else { } else {
log_line("arp: Gateway is different. Getting a new lease."); log_line("arp: Gateway is different. Getting a new lease.");
@ -673,12 +688,12 @@ static void arp_do_gw_check(struct client_state_t *cs)
} }
return; return;
} }
if (!memcmp(arpreply.sip4, &cs->serverAddr, 4)) { if (!memcmp(garp.reply.sip4, &cs->serverAddr, 4)) {
server_is_router: server_is_router:
// Success only if the server MAC matches stored value // Success only if the server MAC matches stored value
if (!memcmp(cs->serverArp, arpreply.smac, 6)) { if (!memcmp(cs->serverArp, garp.reply.smac, 6)) {
arp_server_has_replied = 1; garp.server_replied = true;
if (arp_router_has_replied) if (garp.router_replied)
arp_gw_success(cs); arp_gw_success(cs);
} else { } else {
log_line("arp: DHCP server is different. Getting a new lease."); log_line("arp: DHCP server is different. Getting a new lease.");
@ -689,7 +704,7 @@ server_is_router:
static void arp_do_invalid(struct client_state_t *cs) static void arp_do_invalid(struct client_state_t *cs)
{ {
log_error("handle_arp_response: called in invalid state %u", arpState); log_error("handle_arp_response: called in invalid state %u", garp.state);
arp_close_fd(cs); arp_close_fd(cs);
} }
@ -710,18 +725,18 @@ static const arp_state_fn_t arp_states[] = {
void handle_arp_response(struct client_state_t *cs) void handle_arp_response(struct client_state_t *cs)
{ {
ssize_t r = 0; ssize_t r = 0;
if (arpreply_offset < sizeof arpreply) { if (garp.reply_offset < sizeof garp.reply) {
r = safe_read(cs->arpFd, (char *)&arpreply + arpreply_offset, r = safe_read(cs->arpFd, (char *)&garp.reply + garp.reply_offset,
sizeof arpreply - arpreply_offset); sizeof garp.reply - garp.reply_offset);
if (r < 0) { if (r < 0) {
log_error("arp: ARP response read failed: %s", strerror(errno)); log_error("arp: ARP response read failed: %s", strerror(errno));
switch (arpState) { switch (garp.state) {
case AS_COLLISION_CHECK: arp_failed(cs); break; case AS_COLLISION_CHECK: arp_failed(cs); break;
case AS_GW_CHECK: arp_gw_failed(cs); break; case AS_GW_CHECK: arp_gw_failed(cs); break;
default: arp_reopen_fd(cs); break; default: arp_reopen_fd(cs); break;
} }
} else } else
arpreply_offset += (size_t)r; garp.reply_offset += (size_t)r;
} }
if (r <= 0) { if (r <= 0) {
@ -729,37 +744,38 @@ void handle_arp_response(struct client_state_t *cs)
return; return;
} }
if (arpreply_offset < ARP_MSG_SIZE) if (garp.reply_offset < ARP_MSG_SIZE)
return; return;
// Emulate the BPF filters if they are not in use. // Emulate the BPF filters if they are not in use.
if (!using_arp_bpf && (!arp_validate_bpf(&arpreply) || if (!garp.using_bpf &&
(arpState == AS_DEFENSE && (!arp_validate_bpf(&garp.reply) ||
!arp_validate_bpf_defense(cs, &arpreply)))) { (garp.state == AS_DEFENSE &&
arpreply_clear(); !arp_validate_bpf_defense(cs, &garp.reply)))) {
arp_reply_clear();
return; return;
} }
if (arp_states[arpState].packet_fn) if (arp_states[garp.state].packet_fn)
arp_states[arpState].packet_fn(cs); arp_states[garp.state].packet_fn(cs);
arpreply_clear(); arp_reply_clear();
} }
// Perform retransmission if necessary. // Perform retransmission if necessary.
void handle_arp_timeout(struct client_state_t *cs, long long nowts) void handle_arp_timeout(struct client_state_t *cs, long long nowts)
{ {
if (arp_states[arpState].timeout_fn) if (arp_states[garp.state].timeout_fn)
arp_states[arpState].timeout_fn(cs, nowts); arp_states[garp.state].timeout_fn(cs, nowts);
} }
long long arp_get_wake_ts(void) long long arp_get_wake_ts(void)
{ {
long long mt = -1; long long mt = -1;
for (int i = 0; i < AS_MAX; ++i) { for (int i = 0; i < AS_MAX; ++i) {
if (arp_wake_ts[i] < 0) if (garp.wake_ts[i] < 0)
continue; continue;
if (mt < 0 || mt > arp_wake_ts[i]) if (mt < 0 || mt > garp.wake_ts[i])
mt = arp_wake_ts[i]; mt = garp.wake_ts[i];
} }
return mt; return mt;
} }

View File

@ -56,8 +56,8 @@ extern int arp_probe_wait;
extern int arp_probe_num; extern int arp_probe_num;
extern int arp_probe_min; extern int arp_probe_min;
extern int arp_probe_max; extern int arp_probe_max;
extern int arp_relentless_def;
void set_arp_relentless_def(void);
void arp_reset_send_stats(void); void arp_reset_send_stats(void);
void arp_close_fd(struct client_state_t *cs); void arp_close_fd(struct client_state_t *cs);
int arp_check(struct client_state_t *cs, struct dhcpmsg *packet); int arp_check(struct client_state_t *cs, struct dhcpmsg *packet);

View File

@ -538,7 +538,7 @@ static void parse_program_options(int argc, char *argv[])
seccomp_enforce = true; seccomp_enforce = true;
break; break;
case 'd': case 'd':
arp_relentless_def = 1; set_arp_relentless_def();
break; break;
case 'w': case 'w':
case 'W': { case 'W': {