From 9b3e1872cbb90c5ac3070b22eda7bb5344e61837 Mon Sep 17 00:00:00 2001 From: "Nicholas J. Kain" Date: Mon, 11 Jul 2011 10:39:36 -0400 Subject: [PATCH] Rather than use a single cs.timeout value, track individual timeouts for every ARP state and for the current DHCP state. --- ndhc/arp.c | 107 ++++++++++++++++++++++++++++++-------------------- ndhc/arp.h | 2 + ndhc/config.h | 2 +- ndhc/ndhc.c | 41 +++++++++++++++---- ndhc/state.c | 24 +++++------ 5 files changed, 114 insertions(+), 62 deletions(-) diff --git a/ndhc/arp.c b/ndhc/arp.c index 0fc3b39..0921ca3 100644 --- a/ndhc/arp.c +++ b/ndhc/arp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,8 @@ typedef enum { AS_MAX, } arp_state_t; +static int arp_timeout[AS_MAX] = { -1, -1, -1, -1, -1 }; + typedef enum { ASEND_COLLISION_CHECK, ASEND_GW_PING, @@ -83,15 +86,14 @@ 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. static long long last_conflict_ts; // TS of the last conflicting ARP seen. -static long long pending_def_ts; // TS of the upcoming defense ARP send. -static int def_old_oldTimeout; // Old value of cs->oldTimeout. static int gw_check_init_pingcount; // Initial count of ASEND_GW_PING when // AS_GW_CHECK was entered. -static long long collision_check_init_ts; // Initial ts when COLLISION_CHECK - // was entered. static uint16_t probe_wait_time; // Time to wait for a COLLISION_CHECK reply. +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 // the interface. Never decreases. @@ -256,7 +258,7 @@ static void arp_switch_state(struct client_state_t *cs, arp_state_t state) } } -int arp_close_fd(struct client_state_t *cs) +static int arp_min_close_fd(struct client_state_t *cs) { if (cs->arpFd == -1) return 0; @@ -267,10 +269,18 @@ int arp_close_fd(struct client_state_t *cs) return 1; } +int arp_close_fd(struct client_state_t *cs) +{ + arp_min_close_fd(cs); + for (int i = 0; i < AS_MAX; ++i) + arp_timeout[i] = -1; + return 1; +} + static int arp_reopen_fd(struct client_state_t *cs) { arp_state_t prev_state = arpState; - arp_close_fd(cs); + arp_min_close_fd(cs); if (arp_open_fd(cs) == -1) { log_warning("arp_reopen_fd: Failed to open. Something is very wrong."); log_warning("arp_reopen_fd: Client will still run, but functionality will be degraded."); @@ -331,6 +341,7 @@ static int arp_ip_anon_ping(struct client_state_t *cs, uint32_t test_ip) { BASE_ARPMSG(); memcpy(arp.dip4, &test_ip, sizeof test_ip); + log_line("arp: Probing for hosts that may conflict with our lease..."); if (arp_send(cs, &arp) == -1) return -1; arp_send_stats[ASEND_COLLISION_CHECK].count++; @@ -356,13 +367,12 @@ int arp_check(struct client_state_t *cs, struct dhcpmsg *packet) { memcpy(&arp_dhcp_packet, packet, sizeof (struct dhcpmsg)); arp_switch_state(cs, AS_COLLISION_CHECK); - log_line("arp: Probing for hosts that may conflict with our lease..."); if (arp_ip_anon_ping(cs, arp_dhcp_packet.yiaddr) == -1) return -1; cs->arpPrevState = cs->dhcpState; cs->dhcpState = DS_COLLISION_CHECK; - collision_check_init_ts = arp_send_stats[ASEND_COLLISION_CHECK].ts; - cs->timeout = probe_wait_time = PROBE_WAIT; + arp_check_start_ts = arp_send_stats[ASEND_COLLISION_CHECK].ts; + arp_timeout[AS_COLLISION_CHECK] = probe_wait_time = PROBE_WAIT; return 0; } @@ -377,8 +387,7 @@ int arp_gw_check(struct client_state_t *cs) arp_switch_state(cs, AS_GW_CHECK); cs->arpPrevState = cs->dhcpState; cs->dhcpState = DS_BOUND_GW_CHECK; - cs->oldTimeout = cs->timeout; - cs->timeout = ARP_RETRANS_DELAY + 250; + arp_timeout[AS_GW_CHECK] = ARP_RETRANS_DELAY + 250; return 0; } @@ -390,8 +399,7 @@ static int arp_get_gw_hwaddr(struct client_state_t *cs) log_line("arp: Searching for gw address..."); if (arp_ping(cs, cs->routerAddr) == -1) return -1; - cs->oldTimeout = cs->timeout; - cs->timeout = ARP_RETRANS_DELAY + 250; + arp_timeout[AS_GW_QUERY] = ARP_RETRANS_DELAY + 250; return 0; } @@ -399,6 +407,7 @@ static void arp_failed(struct client_state_t *cs) { log_line("arp: Offered address is in use -- declining"); send_decline(cs, arp_dhcp_packet.yiaddr); + arp_timeout[AS_COLLISION_CHECK] = -1; reinit_selecting(cs, total_conflicts < MAX_CONFLICTS ? 0 : RATE_LIMIT_INTERVAL); } @@ -406,7 +415,7 @@ static void arp_failed(struct client_state_t *cs) static void arp_gw_failed(struct client_state_t *cs) { log_line("arp: Gateway appears to have changed, getting new lease."); - cs->oldTimeout = 0; + arp_timeout[AS_GW_CHECK] = -1; reinit_selecting(cs, 0); } @@ -426,7 +435,7 @@ void arp_set_defense_mode(struct client_state_t *cs) void arp_success(struct client_state_t *cs) { - cs->timeout = (cs->renewTime * 1000) - (curms() - cs->leaseStartTime); + cs->dhcp_timeout = (cs->renewTime * 1000) - (curms() - cs->leaseStartTime); struct in_addr temp_addr = {.s_addr = arp_dhcp_packet.yiaddr}; log_line("arp: Lease of %s obtained, lease time %ld", @@ -435,8 +444,7 @@ void arp_success(struct client_state_t *cs) cs->dhcpState = DS_BOUND; cs->init = 0; last_conflict_ts = 0; - pending_def_ts = 0; - def_old_oldTimeout = 0; + arp_timeout[AS_COLLISION_CHECK] = -1; ifchange_bind(&arp_dhcp_packet); if (cs->arpPrevState == DS_RENEWING || cs->arpPrevState == DS_REBINDING) { arp_switch_state(cs, AS_DEFENSE); @@ -464,7 +472,7 @@ static void arp_gw_success(struct client_state_t *cs) arp_switch_state(cs, AS_DEFENSE); arp_announcement(cs); - cs->timeout = cs->oldTimeout; + arp_timeout[AS_GW_CHECK] = -1; cs->dhcpState = cs->arpPrevState; } @@ -526,15 +534,10 @@ static int arp_gen_probe_wait(void) static void arp_defense_timeout(struct client_state_t *cs, long long nowts) { - if (pending_def_ts) { + if (arp_timeout[AS_DEFENSE] != -1) { log_line("arp: Defending our lease IP."); - pending_def_ts = 0; arp_announcement(cs); - if (def_old_oldTimeout) { - cs->timeout = cs->oldTimeout; - cs->oldTimeout = def_old_oldTimeout; - def_old_oldTimeout = 0; - } + arp_timeout[AS_DEFENSE] = -1; } } @@ -546,7 +549,7 @@ static void arp_check_timeout(struct client_state_t *cs, long long nowts) return; long long rtts = arp_send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY; if (nowts < rtts) { - cs->timeout = rtts - nowts; + arp_timeout[arpState] = rtts - nowts; return; } log_line(arpState == AS_GW_CHECK ? @@ -554,14 +557,14 @@ static void arp_check_timeout(struct client_state_t *cs, long long nowts) "arp: Still looking for gateway hardware address..."); if (arp_ping(cs, cs->routerAddr) == -1) log_warning("arp: Failed to send ARP ping in retransmission."); - cs->timeout = ARP_RETRANS_DELAY; + arp_timeout[arpState] = ARP_RETRANS_DELAY; } static void arp_collision_timeout(struct client_state_t *cs, long long nowts) { arp_defense_timeout(cs, nowts); - if (nowts >= collision_check_init_ts + ANNOUNCE_WAIT || + if (nowts >= arp_check_start_ts + ANNOUNCE_WAIT || arp_send_stats[ASEND_COLLISION_CHECK].count >= PROBE_NUM) { arp_success(cs); return; @@ -569,21 +572,21 @@ static void arp_collision_timeout(struct client_state_t *cs, long long nowts) long long rtts = arp_send_stats[ASEND_COLLISION_CHECK].ts + probe_wait_time; if (nowts < rtts) { - cs->timeout = rtts - nowts; + arp_timeout[AS_COLLISION_CHECK] = rtts - nowts; return; } - log_line("arp: Probing for hosts that may conflict with our lease..."); if (arp_ip_anon_ping(cs, arp_dhcp_packet.yiaddr) == -1) log_warning("arp: Failed to send ARP ping in retransmission."); - cs->timeout = probe_wait_time = arp_gen_probe_wait(); + arp_timeout[AS_COLLISION_CHECK] = probe_wait_time = arp_gen_probe_wait(); } static void arp_do_defense(struct client_state_t *cs) { log_line("arp: detected a peer attempting to use our IP!"); - long long cur_conflict_ts = curms(); + long long nowts = curms(); + arp_timeout[AS_DEFENSE] = -1; if (!last_conflict_ts || - cur_conflict_ts - last_conflict_ts < DEFEND_INTERVAL) { + nowts - last_conflict_ts < DEFEND_INTERVAL) { log_line("arp: Defending our lease IP."); arp_announcement(cs); } else if (!arp_relentless_def) { @@ -591,23 +594,18 @@ static void arp_do_defense(struct client_state_t *cs) send_release(cs); reinit_selecting(cs, 0); } else { - pending_def_ts = arp_send_stats[ASEND_ANNOUNCE].ts + DEFEND_INTERVAL; - long long def_xmit_ts = pending_def_ts - cur_conflict_ts; - if (!cs->timeout || cs->timeout > def_xmit_ts) { - def_old_oldTimeout = cs->oldTimeout; - cs->oldTimeout = cs->timeout; - cs->timeout = def_xmit_ts; - } + arp_timeout[AS_DEFENSE] = + arp_send_stats[ASEND_ANNOUNCE].ts + DEFEND_INTERVAL - nowts; } total_conflicts++; - last_conflict_ts = cur_conflict_ts; + last_conflict_ts = nowts; } static void arp_do_gw_query(struct client_state_t *cs) { if (arp_is_query_reply(&arpreply) && !memcmp(arpreply.sip4, &cs->routerAddr, 4)) { - cs->timeout = cs->oldTimeout; + arp_timeout[AS_GW_QUERY] = -1; memcpy(cs->routerArp, arpreply.smac, 6); log_line("arp: Gateway hardware address %02x:%02x:%02x:%02x:%02x:%02x", cs->routerArp[0], cs->routerArp[1], @@ -715,3 +713,28 @@ void handle_arp_timeout(struct client_state_t *cs, long long nowts) if (arp_states[arpState].timeout_fn) arp_states[arpState].timeout_fn(cs, nowts); } + +int get_arp_timeout(void) +{ + int mt = INT_MAX; + for (int i = 0; i < AS_MAX; ++i) { + log_line("DEBUG: arp_timeout[%u] == %d", i, arp_timeout[i]); + if (arp_timeout[i] < mt && arp_timeout[i] >= 0) + mt = arp_timeout[i]; + } + if (mt == INT_MAX) + return -1; + return mt; +} + +void arp_timeout_adj(int off) +{ + for (int i = 0; i < AS_MAX; ++i) { + if (arp_timeout[i] == -1) + continue; + arp_timeout[i] -= off; + if (arp_timeout[i] < 0) + arp_timeout[i] = 0; + } +} + diff --git a/ndhc/arp.h b/ndhc/arp.h index e101384..5986f43 100644 --- a/ndhc/arp.h +++ b/ndhc/arp.h @@ -55,5 +55,7 @@ void arp_set_defense_mode(struct client_state_t *cs); void arp_success(struct client_state_t *cs); void handle_arp_response(struct client_state_t *cs); void handle_arp_timeout(struct client_state_t *cs, long long nowts); +int get_arp_timeout(void); +void arp_timeout_adj(int off); #endif /* ARP_H_ */ diff --git a/ndhc/config.h b/ndhc/config.h index cad4d00..e629daa 100644 --- a/ndhc/config.h +++ b/ndhc/config.h @@ -30,7 +30,7 @@ struct client_state_t { int ifsPrevState; int listenMode; int epollFd, signalFd, listenFd, arpFd, nlFd; - int timeout, oldTimeout; + int dhcp_timeout; uint32_t clientAddr, serverAddr, routerAddr; uint32_t lease, renewTime, rebindTime, xid; int using_dhcp_bpf; diff --git a/ndhc/ndhc.c b/ndhc/ndhc.c index 3f7a643..58e4238 100644 --- a/ndhc/ndhc.c +++ b/ndhc/ndhc.c @@ -162,7 +162,7 @@ static void do_work(void) { struct epoll_event events[3]; long long last_awake, nowts; - int timeout_delta; + int timeout, timeout_delta; cs.epollFd = epoll_create1(0); if (cs.epollFd == -1) @@ -170,17 +170,19 @@ static void do_work(void) setup_signals(&cs); epoll_add(&cs, cs.nlFd); set_listen_raw(&cs); - last_awake = curms(); - timeout_action(&cs, last_awake); + nowts = curms(); + goto jumpstart; for (;;) { - int r = epoll_wait(cs.epollFd, events, 3, cs.timeout); + log_line("DEBUG: before epoll_wait()"); + int r = epoll_wait(cs.epollFd, events, 3, timeout); if (r == -1) { if (errno == EINTR) continue; else suicide("epoll_wait failed"); } + log_line("DEBUG: after epoll_wait()"); for (int i = 0; i < r; ++i) { int fd = events[i].data.fd; if (fd == cs.signalFd) @@ -197,10 +199,35 @@ static void do_work(void) nowts = curms(); timeout_delta = nowts - last_awake; - cs.timeout -= timeout_delta; - if (cs.timeout <= 0) { - cs.timeout = 0; + + cs.dhcp_timeout -= timeout_delta; + if (cs.dhcp_timeout < 0) + cs.dhcp_timeout = 0; + arp_timeout_adj(timeout_delta); + + int arp_timeout = get_arp_timeout(); + log_line("DEBUG: arp_timeout = %d, dhcp_timeout = %d", + arp_timeout, cs.dhcp_timeout); + if (arp_timeout == -1) + timeout = cs.dhcp_timeout; + else if (arp_timeout < cs.dhcp_timeout) + timeout = arp_timeout; + else + timeout = cs.dhcp_timeout; + + if (timeout <= 0) { +jumpstart: timeout_action(&cs, nowts); + + int arp_timeout = get_arp_timeout(); + log_line("DEBUG: arp_timeout = %d, dhcp_timeout = %d", + arp_timeout, cs.dhcp_timeout); + if (arp_timeout == -1) + timeout = cs.dhcp_timeout; + else if (arp_timeout < cs.dhcp_timeout) + timeout = arp_timeout; + else + timeout = cs.dhcp_timeout; } last_awake = nowts; } diff --git a/ndhc/state.c b/ndhc/state.c index 669cfe2..a7bdf38 100644 --- a/ndhc/state.c +++ b/ndhc/state.c @@ -62,7 +62,7 @@ void reinit_selecting(struct client_state_t *cs, int timeout) ifchange_deconfig(); arp_close_fd(cs); cs->dhcpState = DS_SELECTING; - cs->timeout = timeout; + cs->dhcp_timeout = timeout; cs->clientAddr = 0; num_dhcp_requests = 0; arp_reset_send_stats(); @@ -74,7 +74,7 @@ static void set_released(struct client_state_t *cs) ifchange_deconfig(); arp_close_fd(cs); cs->dhcpState = DS_RELEASED; - cs->timeout = -1; + cs->dhcp_timeout = -1; cs->clientAddr = 0; num_dhcp_requests = 0; arp_reset_send_stats(); @@ -89,7 +89,7 @@ static void requesting_timeout(struct client_state_t *cs, long long nowts) { if (num_dhcp_requests < 5) { send_selecting(cs); - cs->timeout = delay_timeout(num_dhcp_requests); + cs->dhcp_timeout = delay_timeout(num_dhcp_requests); num_dhcp_requests++; } else reinit_selecting(cs, 0); @@ -101,7 +101,7 @@ static void bound_timeout(struct client_state_t *cs, long long nowts) { long long rnt = cs->leaseStartTime + cs->renewTime * 1000; if (nowts < rnt) { - cs->timeout = rnt - nowts; + cs->dhcp_timeout = rnt - nowts; return; } cs->dhcpState = DS_RENEWING; @@ -131,13 +131,13 @@ static void renewing_timeout(struct client_state_t *cs, long long nowts) send_renew(cs); else wt = rbt - nowts; - cs->timeout = wt; + cs->dhcp_timeout = wt; return; } long long elt = cs->leaseStartTime + cs->lease * 1000; if (nowts < elt) { cs->dhcpState = DS_REBINDING; - cs->timeout = (elt - nowts) / 2; + cs->dhcp_timeout = (elt - nowts) / 2; log_line("Entering rebinding state."); } else lease_timedout(cs); @@ -156,14 +156,14 @@ static void rebinding_timeout(struct client_state_t *cs, long long nowts) send_rebind(cs); else wt = elt - nowts; - cs->timeout = wt; + cs->dhcp_timeout = wt; } else lease_timedout(cs); } static void released_timeout(struct client_state_t *cs, long long nowts) { - cs->timeout = -1; + cs->dhcp_timeout = -1; } // Can transition to DS_BOUND or DS_SELECTING. @@ -200,7 +200,7 @@ static void an_packet(struct client_state_t *cs, struct dhcpmsg *packet, } } else { cs->dhcpState = DS_BOUND; - cs->timeout = cs->renewTime * 1000; + cs->dhcp_timeout = cs->renewTime * 1000; arp_set_defense_mode(cs); set_listen_none(cs); } @@ -222,7 +222,7 @@ static void selecting_packet(struct client_state_t *cs, struct dhcpmsg *packet, cs->xid = packet->xid; cs->clientAddr = packet->yiaddr; cs->dhcpState = DS_REQUESTING; - cs->timeout = 0; + cs->dhcp_timeout = 0; num_dhcp_requests = 0; } else { log_line("No server ID in message"); @@ -249,7 +249,7 @@ static void selecting_timeout(struct client_state_t *cs, long long nowts) if (num_dhcp_requests == 0) cs->xid = libc_random_u32(); send_discover(cs); - cs->timeout = delay_timeout(num_dhcp_requests); + cs->dhcp_timeout = delay_timeout(num_dhcp_requests); num_dhcp_requests++; } @@ -318,9 +318,9 @@ void packet_action(struct client_state_t *cs, struct dhcpmsg *packet, void timeout_action(struct client_state_t *cs, long long nowts) { + handle_arp_timeout(cs, nowts); if (dhcp_states[cs->dhcpState].timeout_fn) dhcp_states[cs->dhcpState].timeout_fn(cs, nowts); - handle_arp_timeout(cs, nowts); } void force_renew_action(struct client_state_t *cs)