Centralize DHCP timeout, packet reciept, and user-demanded action handling

into state.[ch].  Remove timeout.c.
This commit is contained in:
Nicholas J. Kain 2011-06-29 23:47:31 -04:00
parent bef54a23fb
commit 76ecfffce2
10 changed files with 358 additions and 365 deletions

View File

@ -30,6 +30,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include "arp.h" #include "arp.h"
#include "state.h"
#include "packet.h" #include "packet.h"
#include "sys.h" #include "sys.h"
#include "ifchange.h" #include "ifchange.h"
@ -180,7 +181,7 @@ static void arp_failed(struct client_state_t *cs)
if (cs->arpPrevState != DS_REQUESTING) if (cs->arpPrevState != DS_REQUESTING)
ifchange(NULL, IFCHANGE_DECONFIG); ifchange(NULL, IFCHANGE_DECONFIG);
cs->dhcpState = DS_INIT_SELECTING; cs->dhcpState = DS_SELECTING;
cs->requestedIP = 0; cs->requestedIP = 0;
cs->timeout = 0; cs->timeout = 0;
cs->packetNum = 0; cs->packetNum = 0;
@ -194,7 +195,7 @@ void arp_gw_failed(struct client_state_t *cs)
// Same as packet.c: line 258 // Same as packet.c: line 258
ifchange(NULL, IFCHANGE_DECONFIG); ifchange(NULL, IFCHANGE_DECONFIG);
cs->dhcpState = DS_INIT_SELECTING; cs->dhcpState = DS_SELECTING;
cs->oldTimeout = 0; cs->oldTimeout = 0;
cs->timeout = 0; cs->timeout = 0;
cs->requestedIP = 0; cs->requestedIP = 0;

View File

@ -26,19 +26,6 @@
#define NUMPACKETS 3 /* number of packets to send before delay */ #define NUMPACKETS 3 /* number of packets to send before delay */
#define RETRY_DELAY 30 /* time in seconds to delay after sending NUMPACKETS */ #define RETRY_DELAY 30 /* time in seconds to delay after sending NUMPACKETS */
enum {
DS_NULL = 0,
DS_INIT_SELECTING,
DS_REQUESTING,
DS_BOUND,
DS_RENEWING,
DS_REBINDING,
DS_ARP_GW_CHECK,
DS_ARP_CHECK,
DS_RENEW_REQUESTED,
DS_RELEASED
};
enum { enum {
LM_NONE = 0, LM_NONE = 0,
LM_KERNEL, LM_KERNEL,

View File

@ -25,12 +25,12 @@
#include <unistd.h> #include <unistd.h>
#include <getopt.h> #include <getopt.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h> #include <signal.h>
#include <time.h> #include <time.h>
#include <string.h> #include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h> #include <sys/epoll.h>
#include <sys/signalfd.h> #include <sys/signalfd.h>
#include <net/if.h> #include <net/if.h>
@ -40,9 +40,9 @@
#include "ndhc-defines.h" #include "ndhc-defines.h"
#include "config.h" #include "config.h"
#include "state.h"
#include "options.h" #include "options.h"
#include "packet.h" #include "packet.h"
#include "timeout.h"
#include "sys.h" #include "sys.h"
#include "ifchange.h" #include "ifchange.h"
#include "arp.h" #include "arp.h"
@ -60,8 +60,8 @@
#define VERSION "1.0" #define VERSION "1.0"
struct client_state_t cs = { struct client_state_t cs = {
.dhcpState = DS_INIT_SELECTING, .dhcpState = DS_SELECTING,
.arpPrevState = DS_NULL, .arpPrevState = DS_SELECTING,
.ifsPrevState = IFS_NONE, .ifsPrevState = IFS_NONE,
.listenMode = LM_NONE, .listenMode = LM_NONE,
.packetNum = 0, .packetNum = 0,
@ -120,72 +120,6 @@ static void show_usage(void)
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
/* perform a renew */
static void force_renew(void)
{
log_line("Performing a DHCP renew...");
retry:
switch (cs.dhcpState) {
case DS_BOUND:
change_listen_mode(&cs, LM_KERNEL);
case DS_ARP_CHECK:
// Cancel arp ping in progress and treat as previous state.
epoll_del(&cs, cs.arpFd);
close(cs.arpFd);
cs.arpFd = -1;
cs.dhcpState = cs.arpPrevState;
goto retry;
case DS_RENEWING:
case DS_REBINDING:
cs.dhcpState = DS_RENEW_REQUESTED;
break;
case DS_RENEW_REQUESTED: /* impatient are we? fine, square 1 */
ifchange(NULL, IFCHANGE_DECONFIG);
case DS_REQUESTING:
case DS_RELEASED:
change_listen_mode(&cs, LM_RAW);
cs.dhcpState = DS_INIT_SELECTING;
break;
case DS_INIT_SELECTING:
default:
break;
}
/* start things over */
cs.packetNum = 0;
/* Kill any timeouts because the user wants this to hurry along */
cs.timeout = 0;
}
/* perform a release */
static void force_release(void)
{
struct in_addr temp_saddr, temp_raddr;
/* send release packet */
if (cs.dhcpState == DS_BOUND || cs.dhcpState == DS_RENEWING ||
cs.dhcpState == DS_REBINDING || cs.dhcpState == DS_ARP_CHECK) {
temp_saddr.s_addr = cs.serverAddr;
temp_raddr.s_addr = cs.requestedIP;
log_line("Unicasting a release of %s to %s.",
inet_ntoa(temp_raddr), inet_ntoa(temp_saddr));
send_release(cs.serverAddr, cs.requestedIP); /* unicast */
ifchange(NULL, IFCHANGE_DECONFIG);
}
log_line("Entering released state.");
if (cs.dhcpState == DS_ARP_CHECK) {
epoll_del(&cs, cs.arpFd);
close(cs.arpFd);
cs.arpFd = -1;
}
change_listen_mode(&cs, LM_NONE);
cs.dhcpState = DS_RELEASED;
cs.timeout = -1;
}
static void signal_dispatch() static void signal_dispatch()
{ {
int t, off = 0; int t, off = 0;
@ -203,10 +137,10 @@ static void signal_dispatch()
} }
switch (si.ssi_signo) { switch (si.ssi_signo) {
case SIGUSR1: case SIGUSR1:
force_renew(); force_renew_action(&cs);
break; break;
case SIGUSR2: case SIGUSR2:
force_release(); force_release_action(&cs);
break; break;
case SIGTERM: case SIGTERM:
log_line("Received SIGTERM. Exiting gracefully."); log_line("Received SIGTERM. Exiting gracefully.");
@ -228,7 +162,7 @@ static void do_work(void)
setup_signals(&cs); setup_signals(&cs);
epoll_add(&cs, cs.nlFd); epoll_add(&cs, cs.nlFd);
change_listen_mode(&cs, LM_RAW); change_listen_mode(&cs, LM_RAW);
handle_timeout(&cs); timeout_action(&cs);
for (;;) { for (;;) {
last_awake = curms(); last_awake = curms();
@ -257,7 +191,7 @@ static void do_work(void)
cs.timeout -= timeout_delta; cs.timeout -= timeout_delta;
if (cs.timeout <= 0) { if (cs.timeout <= 0) {
cs.timeout = 0; cs.timeout = 0;
handle_timeout(&cs); timeout_action(&cs);
} }
} }
} }

View File

@ -113,7 +113,7 @@ static void takedown_if(struct client_state_t *cs)
log_line("nl: taking down interface"); log_line("nl: taking down interface");
// Same as packet.c: line 258 // Same as packet.c: line 258
ifchange(NULL, IFCHANGE_DECONFIG); ifchange(NULL, IFCHANGE_DECONFIG);
cs->dhcpState = DS_INIT_SELECTING; cs->dhcpState = DS_SELECTING;
cs->timeout = 0; cs->timeout = 0;
cs->requestedIP = 0; cs->requestedIP = 0;
cs->packetNum = 0; cs->packetNum = 0;
@ -157,7 +157,7 @@ static void nl_handlemsg(struct nlmsghdr *msg, unsigned int len,
if (cs->dhcpState == DS_BOUND) { if (cs->dhcpState == DS_BOUND) {
if (arp_gw_check(cs) == -1) if (arp_gw_check(cs) == -1)
log_warning("arp_gw_check could not make arp socket, assuming lease is still OK"); log_warning("arp_gw_check could not make arp socket, assuming lease is still OK");
} else if (cs->dhcpState != DS_INIT_SELECTING) } else if (cs->dhcpState != DS_SELECTING)
takedown_if(cs); takedown_if(cs);
} }
} else { } else {

View File

@ -23,7 +23,7 @@
#ifndef NK_NETLINK_H_ #ifndef NK_NETLINK_H_
#define NK_NETLINK_H_ #define NK_NETLINK_H_
#include "config.h" #include "state.h"
int nl_open(struct client_state_t *cs); int nl_open(struct client_state_t *cs);
void nl_close(struct client_state_t *cs); void nl_close(struct client_state_t *cs);

View File

@ -39,6 +39,7 @@
#include <errno.h> #include <errno.h>
#include "packet.h" #include "packet.h"
#include "state.h"
#include "arp.h" #include "arp.h"
#include "ifchange.h" #include "ifchange.h"
#include "sys.h" #include "sys.h"
@ -470,69 +471,6 @@ void change_listen_mode(struct client_state_t *cs, int new_mode)
new_mode == LM_RAW ? "raw" : "cooked"); new_mode == LM_RAW ? "raw" : "cooked");
} }
static void init_selecting_packet(struct client_state_t *cs,
struct dhcpmsg *packet,
uint8_t *message)
{
uint8_t *temp = NULL;
ssize_t optlen;
if (*message == DHCPOFFER) {
if ((temp = get_option_data(packet, DHCP_SERVER_ID, &optlen))) {
memcpy(&cs->serverAddr, temp, 4);
cs->xid = packet->xid;
cs->requestedIP = packet->yiaddr;
cs->dhcpState = DS_REQUESTING;
cs->timeout = 0;
cs->packetNum = 0;
} else {
log_line("No server ID in message");
}
}
}
static void dhcp_ack_or_nak_packet(struct client_state_t *cs,
struct dhcpmsg *packet,
uint8_t *message)
{
uint8_t *temp = NULL;
ssize_t optlen;
if (*message == DHCPACK) {
if (!(temp = get_option_data(packet, DHCP_LEASE_TIME, &optlen))) {
log_line("No lease time received, assuming 1h.");
cs->lease = 60 * 60;
} else {
memcpy(&cs->lease, temp, 4);
cs->lease = ntohl(cs->lease);
// Enforce upper and lower bounds on lease.
cs->lease &= 0x0fffffff;
if (cs->lease < RETRY_DELAY)
cs->lease = RETRY_DELAY;
}
// Can transition from DS_ARP_CHECK to DS_BOUND or DS_INIT_SELECTING.
if (arp_check(cs, packet) == -1) {
log_warning("arp_check failed to make arp socket, retrying lease");
ifchange(NULL, IFCHANGE_DECONFIG);
cs->dhcpState = DS_INIT_SELECTING;
cs->timeout = 30000;
cs->requestedIP = 0;
cs->packetNum = 0;
change_listen_mode(cs, LM_RAW);
}
} else if (*message == DHCPNAK) {
log_line("Received DHCP NAK.");
ifchange(packet, IFCHANGE_NAK);
if (cs->dhcpState != DS_REQUESTING)
ifchange(NULL, IFCHANGE_DECONFIG);
cs->dhcpState = DS_INIT_SELECTING;
cs->timeout = 3000;
cs->requestedIP = 0;
cs->packetNum = 0;
change_listen_mode(cs, LM_RAW);
}
}
void handle_packet(struct client_state_t *cs) void handle_packet(struct client_state_t *cs)
{ {
uint8_t *message = NULL; uint8_t *message = NULL;
@ -578,25 +516,7 @@ void handle_packet(struct client_state_t *cs)
return; return;
} }
switch (cs->dhcpState) { packet_action(cs, &packet, message);
case DS_INIT_SELECTING:
init_selecting_packet(cs, &packet, message);
break;
case DS_ARP_CHECK:
// We ignore dhcp packets for now. This state will
// be changed by the callback for arp ping.
break;
case DS_RENEW_REQUESTED:
case DS_REQUESTING:
case DS_RENEWING:
case DS_REBINDING:
dhcp_ack_or_nak_packet(cs, &packet, message);
break;
case DS_BOUND:
case DS_RELEASED:
default:
break;
}
} }
// Initialize a DHCP client packet that will be sent to a server // Initialize a DHCP client packet that will be sent to a server

314
ndhc/state.c Normal file
View File

@ -0,0 +1,314 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "state.h"
#include "ifchange.h"
#include "arp.h"
#include "options.h"
#include "log.h"
#include "sys.h"
#include "random.h"
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,
uint8_t *message);
static void selecting_timeout(struct client_state_t *cs);
static void requesting_timeout(struct client_state_t *cs);
static void bound_timeout(struct client_state_t *cs);
static void renewing_timeout(struct client_state_t *cs);
static void rebinding_timeout(struct client_state_t *cs);
static void renew_requested_timeout(struct client_state_t *cs);
static void released_timeout(struct client_state_t *cs);
static void nfrelease(struct client_state_t *cs);
static void frelease(struct client_state_t *cs);
static void frenew(struct client_state_t *cs);
typedef struct {
void (*packet_fn)(struct client_state_t *cs, struct dhcpmsg *packet,
uint8_t *message);
void (*timeout_fn)(struct client_state_t *cs);
void (*force_renew_fn)(struct client_state_t *cs);
void (*force_release_fn)(struct client_state_t *cs);
} dhcp_state_t;
// packet, timeout, renew, release
dhcp_state_t dhcp_states[] = {
{ selecting_packet, selecting_timeout, 0, frelease}, // SELECTING
{ an_packet, requesting_timeout, frenew, frelease}, // REQUESTING
{ 0, bound_timeout, frenew, nfrelease}, // BOUND
{ an_packet, renewing_timeout, frenew, nfrelease}, // RENEWING
{ an_packet, rebinding_timeout, frenew, nfrelease}, // REBINDING
{ 0, arp_gw_failed, frenew, frelease}, // ARP_GW_CHECK XXX
{ 0, arp_success, frenew, nfrelease}, // ARP_CHECK
{ an_packet, renew_requested_timeout, frenew, frelease}, // RENEW_REQUESTED
{ 0, released_timeout, frenew, frelease}, // RELEASED
{ 0, 0, 0, 0}, // NUM_STATES
};
// Triggered after a DHCP lease request packet has been sent and no reply has
// been received within the response wait time. If we've not exceeded the
// maximum number of request retransmits, then send another packet and wait
// again. Otherwise, return to the DHCP initialization state.
static void requesting_timeout(struct client_state_t *cs)
{
if (cs->packetNum < NUMPACKETS) {
send_selecting(cs->xid, cs->serverAddr, cs->requestedIP);
cs->timeout = ((cs->packetNum == NUMPACKETS - 1) ? 10 : 2) * 1000;
cs->packetNum++;
} else {
cs->dhcpState = DS_SELECTING;
cs->timeout = 0;
cs->packetNum = 0;
change_listen_mode(cs, LM_RAW);
}
}
// Triggered when the lease has been held for a significant fraction of its
// total time, and it is time to renew the lease so that it is not lost.
static void bound_timeout(struct client_state_t *cs)
{
cs->dhcpState = DS_RENEWING;
change_listen_mode(cs, LM_KERNEL);
log_line("Entering renew state.");
renewing_timeout(cs);
}
// Triggered when a DHCP renew request has been sent and no reply has been
// received within the response wait time. This function is also directly
// called by bound_timeout() when it is time to renew a lease before it
// expires. Check to see if the lease is still valid, and if it is, send
// a unicast DHCP renew packet. If it is not, then change to the REBINDING
// state to get a new lease.
static void renewing_timeout(struct client_state_t *cs)
{
if ((cs->t2 - cs->t1) <= (cs->lease / 14400 + 1)) {
cs->dhcpState = DS_REBINDING;
cs->timeout = (cs->t2 - cs->t1) * 1000;
log_line("Entering rebinding state.");
} else {
send_renew(cs->xid, cs->serverAddr, cs->requestedIP);
cs->t1 = ((cs->t2 - cs->t1) >> 1) + cs->t1;
cs->timeout = (cs->t1 * 1000) - (curms() - cs->leaseStartTime);
}
}
static void rebinding_timeout(struct client_state_t *cs)
{
/* Either set a new T2, or enter INIT state */
if ((cs->lease - cs->t2) <= (cs->lease / 14400 + 1)) {
/* timed out, enter init state */
cs->dhcpState = DS_SELECTING;
log_line("Lease lost, entering init state.");
ifchange(NULL, IFCHANGE_DECONFIG);
cs->timeout = 0;
cs->packetNum = 0;
change_listen_mode(cs, LM_RAW);
} else {
/* send a request packet */
send_renew(cs->xid, 0, cs->requestedIP); /* broadcast */
cs->t2 = ((cs->lease - cs->t2) >> 1) + cs->t2;
cs->timeout = (cs->t2 * 1000) - (curms() - cs->leaseStartTime);
}
}
static void renew_requested_timeout(struct client_state_t *cs)
{
if (cs->packetNum < NUMPACKETS) {
/* send unicast request packet */
send_renew(cs->xid, cs->serverAddr, cs->requestedIP);
cs->timeout = ((cs->packetNum == NUMPACKETS - 1) ? 10 : 2) * 1000;
cs->packetNum++;
} else {
ifchange(NULL, IFCHANGE_DECONFIG);
cs->dhcpState = DS_SELECTING;
cs->timeout = 0;
cs->packetNum = 0;
change_listen_mode(cs, LM_RAW);
}
}
static void released_timeout(struct client_state_t *cs)
{
cs->timeout = -1;
}
static void an_packet(struct client_state_t *cs, struct dhcpmsg *packet,
uint8_t *message)
{
uint8_t *temp = NULL;
ssize_t optlen;
if (*message == DHCPACK) {
if (!(temp = get_option_data(packet, DHCP_LEASE_TIME, &optlen))) {
log_line("No lease time received, assuming 1h.");
cs->lease = 60 * 60;
} else {
memcpy(&cs->lease, temp, 4);
cs->lease = ntohl(cs->lease);
// Enforce upper and lower bounds on lease.
cs->lease &= 0x0fffffff;
if (cs->lease < RETRY_DELAY)
cs->lease = RETRY_DELAY;
}
// Can transition from DS_ARP_CHECK to DS_BOUND or DS_SELECTING.
if (arp_check(cs, packet) == -1) {
log_warning("arp_check failed to make arp socket, retrying lease");
ifchange(NULL, IFCHANGE_DECONFIG);
cs->dhcpState = DS_SELECTING;
cs->timeout = 30000;
cs->requestedIP = 0;
cs->packetNum = 0;
change_listen_mode(cs, LM_RAW);
}
} else if (*message == DHCPNAK) {
log_line("Received DHCP NAK.");
ifchange(packet, IFCHANGE_NAK);
if (cs->dhcpState != DS_REQUESTING)
ifchange(NULL, IFCHANGE_DECONFIG);
cs->dhcpState = DS_SELECTING;
cs->timeout = 3000;
cs->requestedIP = 0;
cs->packetNum = 0;
change_listen_mode(cs, LM_RAW);
}
}
static void selecting_packet(struct client_state_t *cs, struct dhcpmsg *packet,
uint8_t *message)
{
uint8_t *temp = NULL;
ssize_t optlen;
if (*message == DHCPOFFER) {
if ((temp = get_option_data(packet, DHCP_SERVER_ID, &optlen))) {
memcpy(&cs->serverAddr, temp, 4);
cs->xid = packet->xid;
cs->requestedIP = packet->yiaddr;
cs->dhcpState = DS_REQUESTING;
cs->timeout = 0;
cs->packetNum = 0;
} else {
log_line("No server ID in message");
}
}
}
#define DELAY_SEC (((RETRY_DELAY - (RETRY_DELAY / NUMPACKETS)) / NUMPACKETS) + 1)
// Triggered after a DHCP discover packet has been sent and no reply has
// been received within the response wait time. If we've not exceeded the
// maximum number of discover retransmits, then send another packet and wait
// again. Otherwise, background or fail.
static void selecting_timeout(struct client_state_t *cs)
{
if (cs->packetNum < NUMPACKETS) {
if (cs->packetNum == 0)
cs->xid = libc_random_u32();
send_discover(cs->xid, cs->requestedIP);
cs->timeout = DELAY_SEC * (cs->packetNum + 1) * 1000;
cs->packetNum++;
} else {
if (cs->init) {
if (client_config.background_if_no_lease) {
log_line("No lease, going to background.");
cs->init = 0;
background(cs);
} else if (client_config.abort_if_no_lease) {
log_line("No lease, failing.");
exit(EXIT_FAILURE);
}
}
cs->packetNum = 0;
cs->timeout = RETRY_DELAY * 1000;
}
}
#undef DELAY_SEC
static void nfrelease(struct client_state_t *cs)
{
struct in_addr temp_saddr = { .s_addr = cs->serverAddr };
struct in_addr temp_raddr = { .s_addr = cs->requestedIP };
log_line("Unicasting a release of %s to %s.",
inet_ntoa(temp_raddr), inet_ntoa(temp_saddr));
send_release(cs->serverAddr, cs->requestedIP);
ifchange(NULL, IFCHANGE_DECONFIG);
frelease(cs);
}
static void frelease(struct client_state_t *cs)
{
log_line("Entering released state.");
if (cs->dhcpState == DS_ARP_CHECK) {
epoll_del(cs, cs->arpFd);
close(cs->arpFd);
cs->arpFd = -1;
}
change_listen_mode(cs, LM_NONE);
cs->dhcpState = DS_RELEASED;
cs->timeout = -1;
}
// XXX: DS_ARP_CHECK_GW? Also split this up?
static void frenew(struct client_state_t *cs)
{
log_line("Performing a DHCP renew...");
retry:
switch (cs->dhcpState) {
case DS_BOUND:
change_listen_mode(cs, LM_KERNEL);
case DS_ARP_CHECK:
// Cancel arp ping in progress and treat as previous state.
epoll_del(cs, cs->arpFd);
close(cs->arpFd);
cs->arpFd = -1;
cs->dhcpState = cs->arpPrevState;
goto retry;
case DS_RENEWING:
case DS_REBINDING:
cs->dhcpState = DS_RENEW_REQUESTED;
break;
case DS_RENEW_REQUESTED: /* impatient are we? fine, square 1 */
ifchange(NULL, IFCHANGE_DECONFIG);
case DS_REQUESTING:
case DS_RELEASED:
change_listen_mode(cs, LM_RAW);
cs->dhcpState = DS_SELECTING;
break;
case DS_SELECTING:
default:
break;
}
// Start over
cs->packetNum = 0;
cs->timeout = 0;
}
void packet_action(struct client_state_t *cs, struct dhcpmsg *packet,
uint8_t *message)
{
if (dhcp_states[cs->dhcpState].packet_fn)
dhcp_states[cs->dhcpState].packet_fn(cs, packet, message);
}
void timeout_action(struct client_state_t *cs)
{
if (dhcp_states[cs->dhcpState].timeout_fn)
dhcp_states[cs->dhcpState].timeout_fn(cs);
}
void force_renew_action(struct client_state_t *cs)
{
if (dhcp_states[cs->dhcpState].force_renew_fn)
dhcp_states[cs->dhcpState].force_renew_fn(cs);
}
void force_release_action(struct client_state_t *cs)
{
if (dhcp_states[cs->dhcpState].force_release_fn)
dhcp_states[cs->dhcpState].force_release_fn(cs);
}

26
ndhc/state.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef NDHC_STATE_H_
#define NDHC_STATE_H_
#include "config.h"
#include "packet.h"
typedef enum {
DS_SELECTING = 0,
DS_REQUESTING,
DS_BOUND,
DS_RENEWING,
DS_REBINDING,
DS_ARP_GW_CHECK,
DS_ARP_CHECK,
DS_RENEW_REQUESTED,
DS_RELEASED,
DS_NUM_STATES,
} dhcp_states_t;
void packet_action(struct client_state_t *cs, struct dhcpmsg *packet,
uint8_t *message);
void timeout_action(struct client_state_t *cs);
void force_renew_action(struct client_state_t *cs);
void force_release_action(struct client_state_t *cs);
#endif

View File

@ -1,160 +0,0 @@
/* timeout.c - callbacks to react to event timeouts
* Time-stamp: <2011-06-11 11:13:22 njk>
*
* (c) 2004-2011 Nicholas J. Kain <njkain at gmail dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <unistd.h>
#include <stdlib.h>
#include "timeout.h"
#include "config.h"
#include "ifchange.h"
#include "packet.h"
#include "arp.h"
#include "log.h"
#include "random.h"
#define DELAY_SEC (((RETRY_DELAY - (RETRY_DELAY / NUMPACKETS)) / NUMPACKETS) + 1)
// Triggered after a DHCP discover packet has been sent and no reply has
// been received within the response wait time. If we've not exceeded the
// maximum number of discover retransmits, then send another packet and wait
// again. Otherwise, background or fail.
static void init_selecting_timeout(struct client_state_t *cs)
{
if (cs->packetNum < NUMPACKETS) {
if (cs->packetNum == 0)
cs->xid = libc_random_u32();
send_discover(cs->xid, cs->requestedIP);
cs->timeout = DELAY_SEC * (cs->packetNum + 1) * 1000;
cs->packetNum++;
} else {
if (cs->init) {
if (client_config.background_if_no_lease) {
log_line("No lease, going to background.");
cs->init = 0;
background(cs);
} else if (client_config.abort_if_no_lease) {
log_line("No lease, failing.");
exit(EXIT_FAILURE);
}
}
cs->packetNum = 0;
cs->timeout = RETRY_DELAY * 1000;
}
}
#undef DELAY_SEC
static void renew_requested_timeout(struct client_state_t *cs)
{
if (cs->packetNum < NUMPACKETS) {
/* send unicast request packet */
send_renew(cs->xid, cs->serverAddr, cs->requestedIP);
cs->timeout = ((cs->packetNum == NUMPACKETS - 1) ? 10 : 2) * 1000;
cs->packetNum++;
} else {
ifchange(NULL, IFCHANGE_DECONFIG);
cs->dhcpState = DS_INIT_SELECTING;
cs->timeout = 0;
cs->packetNum = 0;
change_listen_mode(cs, LM_RAW);
}
}
// Triggered after a DHCP lease request packet has been sent and no reply has
// been received within the response wait time. If we've not exceeded the
// maximum number of request retransmits, then send another packet and wait
// again. Otherwise, return to the DHCP initialization state.
static void requesting_timeout(struct client_state_t *cs)
{
if (cs->packetNum < NUMPACKETS) {
send_selecting(cs->xid, cs->serverAddr, cs->requestedIP);
cs->timeout = ((cs->packetNum == NUMPACKETS - 1) ? 10 : 2) * 1000;
cs->packetNum++;
} else {
cs->dhcpState = DS_INIT_SELECTING;
cs->timeout = 0;
cs->packetNum = 0;
change_listen_mode(cs, LM_RAW);
}
}
// Triggered when a DHCP renew request has been sent and no reply has been
// received within the response wait time. This function is also directly
// called by bound_timeout() when it is time to renew a lease before it
// expires. Check to see if the lease is still valid, and if it is, send
// a unicast DHCP renew packet. If it is not, then change to the REBINDING
// state to get a new lease.
static void renewing_timeout(struct client_state_t *cs)
{
if ((cs->t2 - cs->t1) <= (cs->lease / 14400 + 1)) {
cs->dhcpState = DS_REBINDING;
cs->timeout = (cs->t2 - cs->t1) * 1000;
log_line("Entering rebinding state.");
} else {
send_renew(cs->xid, cs->serverAddr, cs->requestedIP);
cs->t1 = ((cs->t2 - cs->t1) >> 1) + cs->t1;
cs->timeout = (cs->t1 * 1000) - (curms() - cs->leaseStartTime);
}
}
// Triggered when the lease has been held for a significant fraction of its
// total time, and it is time to renew the lease so that it is not lost.
static void bound_timeout(struct client_state_t *cs)
{
cs->dhcpState = DS_RENEWING;
change_listen_mode(cs, LM_KERNEL);
log_line("Entering renew state.");
renewing_timeout(cs);
}
static void rebinding_timeout(struct client_state_t *cs)
{
/* Either set a new T2, or enter INIT state */
if ((cs->lease - cs->t2) <= (cs->lease / 14400 + 1)) {
/* timed out, enter init state */
cs->dhcpState = DS_INIT_SELECTING;
log_line("Lease lost, entering init state.");
ifchange(NULL, IFCHANGE_DECONFIG);
cs->timeout = 0;
cs->packetNum = 0;
change_listen_mode(cs, LM_RAW);
} else {
/* send a request packet */
send_renew(cs->xid, 0, cs->requestedIP); /* broadcast */
cs->t2 = ((cs->lease - cs->t2) >> 1) + cs->t2;
cs->timeout = (cs->t2 * 1000) - (curms() - cs->leaseStartTime);
}
}
// Handle epoll timeout expiring
void handle_timeout(struct client_state_t *cs)
{
switch (cs->dhcpState) {
case DS_INIT_SELECTING: init_selecting_timeout(cs); break;
case DS_RENEW_REQUESTED: renew_requested_timeout(cs); break;
case DS_REQUESTING: requesting_timeout(cs); break;
case DS_RENEWING: renewing_timeout(cs); break;
case DS_BOUND: bound_timeout(cs); break;
case DS_REBINDING: rebinding_timeout(cs); break;
case DS_RELEASED: cs->timeout = -1; break;
case DS_ARP_CHECK: arp_success(cs); break;
case DS_ARP_GW_CHECK: arp_gw_failed(cs); break;
default: break;
}
}

View File

@ -1,29 +0,0 @@
/* timeout.h - callbacks to react to event timeouts
* Time-stamp: <2011-03-30 23:42:25 nk>
*
* (c) 2004-2011 Nicholas J. Kain <njkain at gmail dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef TIMEOUT_H_
#define TIMEOUT_H_
#include "config.h"
#include "sys.h"
void handle_timeout(struct client_state_t *cs);
#endif /* TIMEOUT_H_ */