From 83594bce4a69679bccbe4f25cd3ef08642d5738c Mon Sep 17 00:00:00 2001 From: "Nicholas J. Kain" Date: Mon, 11 Jul 2011 11:31:27 -0400 Subject: [PATCH] Track pending events in time by using absolute times rather than relative timeouts. --- ndhc/arp.c | 70 +++++++++++++++++++++++---------------------------- ndhc/arp.h | 3 +-- ndhc/config.h | 1 - ndhc/ndhc.c | 58 ++++++++++++++++++------------------------ ndhc/state.c | 57 +++++++++++++++++++++-------------------- ndhc/state.h | 1 + 6 files changed, 87 insertions(+), 103 deletions(-) diff --git a/ndhc/arp.c b/ndhc/arp.c index 0921ca3..367f0d2 100644 --- a/ndhc/arp.c +++ b/ndhc/arp.c @@ -67,7 +67,7 @@ typedef enum { AS_MAX, } arp_state_t; -static int arp_timeout[AS_MAX] = { -1, -1, -1, -1, -1 }; +static long long arp_wake_ts[AS_MAX] = { -1, -1, -1, -1, -1 }; typedef enum { ASEND_COLLISION_CHECK, @@ -273,7 +273,7 @@ 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; + arp_wake_ts[i] = -1; return 1; } @@ -372,7 +372,8 @@ int arp_check(struct client_state_t *cs, struct dhcpmsg *packet) cs->arpPrevState = cs->dhcpState; cs->dhcpState = DS_COLLISION_CHECK; arp_check_start_ts = arp_send_stats[ASEND_COLLISION_CHECK].ts; - arp_timeout[AS_COLLISION_CHECK] = probe_wait_time = PROBE_WAIT; + probe_wait_time = PROBE_WAIT; + arp_wake_ts[AS_COLLISION_CHECK] = arp_check_start_ts + probe_wait_time; return 0; } @@ -387,7 +388,8 @@ 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; - arp_timeout[AS_GW_CHECK] = ARP_RETRANS_DELAY + 250; + arp_wake_ts[AS_GW_CHECK] = + arp_send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY + 250; return 0; } @@ -399,7 +401,8 @@ 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; - arp_timeout[AS_GW_QUERY] = ARP_RETRANS_DELAY + 250; + arp_wake_ts[AS_GW_QUERY] = + arp_send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY + 250; return 0; } @@ -407,7 +410,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; + arp_wake_ts[AS_COLLISION_CHECK] = -1; reinit_selecting(cs, total_conflicts < MAX_CONFLICTS ? 0 : RATE_LIMIT_INTERVAL); } @@ -415,7 +418,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."); - arp_timeout[AS_GW_CHECK] = -1; + arp_wake_ts[AS_GW_CHECK] = -1; reinit_selecting(cs, 0); } @@ -435,8 +438,6 @@ void arp_set_defense_mode(struct client_state_t *cs) void arp_success(struct client_state_t *cs) { - 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", inet_ntoa(temp_addr), cs->lease); @@ -444,7 +445,7 @@ void arp_success(struct client_state_t *cs) cs->dhcpState = DS_BOUND; cs->init = 0; last_conflict_ts = 0; - arp_timeout[AS_COLLISION_CHECK] = -1; + arp_wake_ts[AS_COLLISION_CHECK] = -1; ifchange_bind(&arp_dhcp_packet); if (cs->arpPrevState == DS_RENEWING || cs->arpPrevState == DS_REBINDING) { arp_switch_state(cs, AS_DEFENSE); @@ -472,7 +473,7 @@ static void arp_gw_success(struct client_state_t *cs) arp_switch_state(cs, AS_DEFENSE); arp_announcement(cs); - arp_timeout[AS_GW_CHECK] = -1; + arp_wake_ts[AS_GW_CHECK] = -1; cs->dhcpState = cs->arpPrevState; } @@ -534,10 +535,10 @@ static int arp_gen_probe_wait(void) static void arp_defense_timeout(struct client_state_t *cs, long long nowts) { - if (arp_timeout[AS_DEFENSE] != -1) { + if (arp_wake_ts[AS_DEFENSE] != -1) { log_line("arp: Defending our lease IP."); arp_announcement(cs); - arp_timeout[AS_DEFENSE] = -1; + arp_wake_ts[AS_DEFENSE] = -1; } } @@ -549,7 +550,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) { - arp_timeout[arpState] = rtts - nowts; + arp_wake_ts[arpState] = rtts; return; } log_line(arpState == AS_GW_CHECK ? @@ -557,7 +558,8 @@ 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."); - arp_timeout[arpState] = ARP_RETRANS_DELAY; + arp_wake_ts[arpState] = + arp_send_stats[ASEND_GW_PING].ts + ARP_RETRANS_DELAY; } static void arp_collision_timeout(struct client_state_t *cs, long long nowts) @@ -572,19 +574,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) { - arp_timeout[AS_COLLISION_CHECK] = rtts - nowts; + arp_wake_ts[AS_COLLISION_CHECK] = rtts; return; } if (arp_ip_anon_ping(cs, arp_dhcp_packet.yiaddr) == -1) log_warning("arp: Failed to send ARP ping in retransmission."); - arp_timeout[AS_COLLISION_CHECK] = probe_wait_time = arp_gen_probe_wait(); + probe_wait_time = arp_gen_probe_wait(); + arp_wake_ts[AS_COLLISION_CHECK] = + arp_send_stats[ASEND_COLLISION_CHECK].ts + probe_wait_time; } static void arp_do_defense(struct client_state_t *cs) { log_line("arp: detected a peer attempting to use our IP!"); long long nowts = curms(); - arp_timeout[AS_DEFENSE] = -1; + arp_wake_ts[AS_DEFENSE] = -1; if (!last_conflict_ts || nowts - last_conflict_ts < DEFEND_INTERVAL) { log_line("arp: Defending our lease IP."); @@ -594,8 +598,8 @@ static void arp_do_defense(struct client_state_t *cs) send_release(cs); reinit_selecting(cs, 0); } else { - arp_timeout[AS_DEFENSE] = - arp_send_stats[ASEND_ANNOUNCE].ts + DEFEND_INTERVAL - nowts; + arp_wake_ts[AS_DEFENSE] = + arp_send_stats[ASEND_ANNOUNCE].ts + DEFEND_INTERVAL; } total_conflicts++; last_conflict_ts = nowts; @@ -605,7 +609,7 @@ static void arp_do_gw_query(struct client_state_t *cs) { if (arp_is_query_reply(&arpreply) && !memcmp(arpreply.sip4, &cs->routerAddr, 4)) { - arp_timeout[AS_GW_QUERY] = -1; + arp_wake_ts[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], @@ -714,27 +718,15 @@ void handle_arp_timeout(struct client_state_t *cs, long long nowts) arp_states[arpState].timeout_fn(cs, nowts); } -int get_arp_timeout(void) +long long arp_get_wake_ts(void) { - int mt = INT_MAX; + long long mt = -1; 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 (arp_wake_ts[i] == -1) + continue; + if (mt == -1 || mt > arp_wake_ts[i]) + mt = arp_wake_ts[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 5986f43..77e8aff 100644 --- a/ndhc/arp.h +++ b/ndhc/arp.h @@ -55,7 +55,6 @@ 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); +long long arp_get_wake_ts(void); #endif /* ARP_H_ */ diff --git a/ndhc/config.h b/ndhc/config.h index e629daa..e08c0b2 100644 --- a/ndhc/config.h +++ b/ndhc/config.h @@ -30,7 +30,6 @@ struct client_state_t { int ifsPrevState; int listenMode; int epollFd, signalFd, listenFd, arpFd, nlFd; - 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 58e4238..79a9637 100644 --- a/ndhc/ndhc.c +++ b/ndhc/ndhc.c @@ -161,8 +161,8 @@ static int get_clientid_mac_string(char *str, size_t slen) static void do_work(void) { struct epoll_event events[3]; - long long last_awake, nowts; - int timeout, timeout_delta; + long long nowts; + int timeout; cs.epollFd = epoll_create1(0); if (cs.epollFd == -1) @@ -174,7 +174,6 @@ static void do_work(void) goto jumpstart; for (;;) { - log_line("DEBUG: before epoll_wait()"); int r = epoll_wait(cs.epollFd, events, 3, timeout); if (r == -1) { if (errno == EINTR) @@ -182,7 +181,6 @@ static void do_work(void) 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,39 +195,31 @@ static void do_work(void) suicide("epoll_wait: unknown fd"); } - nowts = curms(); - timeout_delta = nowts - last_awake; + for (;;) { + nowts = curms(); + long long arp_wake_ts = arp_get_wake_ts(); + long long dhcp_wake_ts = dhcp_get_wake_ts(); + if (arp_wake_ts == -1) { + if (dhcp_wake_ts != -1) { + timeout = dhcp_wake_ts - nowts; + if (timeout < 0) + timeout = 0; + } else + timeout = -1; + } else { + // If dhcp_wake_ts is -1 then we want to sleep anyway. + timeout = (arp_wake_ts < dhcp_wake_ts ? + arp_wake_ts : dhcp_wake_ts) - nowts; + if (timeout < 0) + 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) { + if (!timeout) { 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; + timeout_action(&cs, nowts); + } else + break; } - last_awake = nowts; } } diff --git a/ndhc/state.c b/ndhc/state.c index a7bdf38..da35e4e 100644 --- a/ndhc/state.c +++ b/ndhc/state.c @@ -12,6 +12,8 @@ #include "sys.h" #include "random.h" +static long long dhcp_wake_ts = -1; + static void selecting_packet(struct client_state_t *cs, struct dhcpmsg *packet, uint8_t *message); static void an_packet(struct client_state_t *cs, struct dhcpmsg *packet, @@ -62,7 +64,7 @@ void reinit_selecting(struct client_state_t *cs, int timeout) ifchange_deconfig(); arp_close_fd(cs); cs->dhcpState = DS_SELECTING; - cs->dhcp_timeout = timeout; + dhcp_wake_ts = curms() + timeout; cs->clientAddr = 0; num_dhcp_requests = 0; arp_reset_send_stats(); @@ -74,7 +76,7 @@ static void set_released(struct client_state_t *cs) ifchange_deconfig(); arp_close_fd(cs); cs->dhcpState = DS_RELEASED; - cs->dhcp_timeout = -1; + dhcp_wake_ts = -1; cs->clientAddr = 0; num_dhcp_requests = 0; arp_reset_send_stats(); @@ -89,7 +91,7 @@ static void requesting_timeout(struct client_state_t *cs, long long nowts) { if (num_dhcp_requests < 5) { send_selecting(cs); - cs->dhcp_timeout = delay_timeout(num_dhcp_requests); + dhcp_wake_ts = nowts + delay_timeout(num_dhcp_requests); num_dhcp_requests++; } else reinit_selecting(cs, 0); @@ -101,7 +103,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->dhcp_timeout = rnt - nowts; + dhcp_wake_ts = rnt; return; } cs->dhcpState = DS_RENEWING; @@ -126,21 +128,17 @@ static void renewing_timeout(struct client_state_t *cs, long long nowts) { long long rbt = cs->leaseStartTime + cs->rebindTime * 1000; if (nowts < rbt) { - long long wt = (rbt - nowts) / 2; - if (wt >= 30000) - send_renew(cs); - else - wt = rbt - nowts; - cs->dhcp_timeout = wt; - return; - } - long long elt = cs->leaseStartTime + cs->lease * 1000; - if (nowts < elt) { + if (rbt - nowts < 30000) { + dhcp_wake_ts = rbt; + return; + } + send_renew(cs); + dhcp_wake_ts = nowts + ((rbt - nowts) / 2); + } else { cs->dhcpState = DS_REBINDING; - cs->dhcp_timeout = (elt - nowts) / 2; log_line("Entering rebinding state."); - } else - lease_timedout(cs); + rebinding_timeout(cs, nowts); + } } // Triggered when a DHCP rebind request has been sent and no reply has been @@ -151,19 +149,19 @@ static void rebinding_timeout(struct client_state_t *cs, long long nowts) { long long elt = cs->leaseStartTime + cs->lease * 1000; if (nowts < elt) { - long long wt = (elt - nowts) / 2; - if (wt >= 30000) - send_rebind(cs); - else - wt = elt - nowts; - cs->dhcp_timeout = wt; + if (elt - nowts < 30000) { + dhcp_wake_ts = elt; + return; + } + send_rebind(cs); + dhcp_wake_ts = nowts + ((elt - nowts) / 2); } else lease_timedout(cs); } static void released_timeout(struct client_state_t *cs, long long nowts) { - cs->dhcp_timeout = -1; + dhcp_wake_ts = -1; } // Can transition to DS_BOUND or DS_SELECTING. @@ -189,6 +187,7 @@ static void an_packet(struct client_state_t *cs, struct dhcpmsg *packet, // the remote server values, if they even exist, for sanity. cs->renewTime = cs->lease >> 1; cs->rebindTime = (cs->lease >> 3) * 0x7; // * 0.875 + dhcp_wake_ts = cs->leaseStartTime + cs->renewTime * 1000; // Only check if we are either in the REQUESTING state, or if we // have received a lease with a different IP than what we had before. @@ -200,7 +199,6 @@ static void an_packet(struct client_state_t *cs, struct dhcpmsg *packet, } } else { cs->dhcpState = DS_BOUND; - cs->dhcp_timeout = cs->renewTime * 1000; arp_set_defense_mode(cs); set_listen_none(cs); } @@ -222,7 +220,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->dhcp_timeout = 0; + dhcp_wake_ts = curms(); num_dhcp_requests = 0; } else { log_line("No server ID in message"); @@ -249,7 +247,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->dhcp_timeout = delay_timeout(num_dhcp_requests); + dhcp_wake_ts = nowts + delay_timeout(num_dhcp_requests); num_dhcp_requests++; } @@ -335,3 +333,8 @@ void force_release_action(struct client_state_t *cs) dhcp_states[cs->dhcpState].force_release_fn(cs); } +long long dhcp_get_wake_ts(void) +{ + return dhcp_wake_ts; +} + diff --git a/ndhc/state.h b/ndhc/state.h index feb9654..ab86e7f 100644 --- a/ndhc/state.h +++ b/ndhc/state.h @@ -27,6 +27,7 @@ void force_release_action(struct client_state_t *cs); void ifup_action(struct client_state_t *cs); void ifnocarrier_action(struct client_state_t *cs); void ifdown_action(struct client_state_t *cs); +long long dhcp_get_wake_ts(void); #endif