Split state machine apart into functions.

This commit is contained in:
Nicholas J. Kain 2010-12-24 06:31:06 -05:00
parent 6772ddac1b
commit 406e316274

View File

@ -321,100 +321,241 @@ static void arp_success(void)
background(); background();
} }
/* Handle select timeout dropping to zero */ static void init_selecting_timeout()
{
if (packet_num < NUMPACKETS) {
if (packet_num == 0)
xid = random_xid();
/* broadcast */
send_discover(xid, requested_ip);
timeout = ((packet_num == NUMPACKETS - 1) ? 4 : 2) * 1000;
packet_num++;
} else {
if (client_config.background_if_no_lease) {
log_line("No lease, going to background.");
background();
} else if (client_config.abort_if_no_lease) {
log_line("No lease, failing.");
exit(EXIT_FAILURE);
}
/* wait to try again */
packet_num = 0;
timeout = RETRY_DELAY * 1000;
}
}
static void renew_requested_timeout()
{
if (packet_num < NUMPACKETS) {
/* send unicast request packet */
send_renew(xid, server_addr, requested_ip);
timeout = ((packet_num == NUMPACKETS - 1) ? 10 : 2) * 1000;
packet_num++;
} else {
/* timed out, go back to init state */
run_script(NULL, SCRIPT_DECONFIG);
dhcp_state = INIT_SELECTING;
timeout = 0;
packet_num = 0;
change_listen_mode(LISTEN_RAW);
}
}
static void requesting_timeout()
{
if (packet_num < NUMPACKETS) {
/* send broadcast request packet */
send_selecting(xid, server_addr, requested_ip);
timeout = ((packet_num == NUMPACKETS - 1) ? 10 : 2) * 1000;
packet_num++;
} else {
/* timed out, go back to init state */
dhcp_state = INIT_SELECTING;
timeout = 0;
packet_num = 0;
change_listen_mode(LISTEN_RAW);
}
}
static void renewing_timeout()
{
/* Either set a new T1, or enter REBINDING state */
if ((t2 - t1) <= (lease / 14400 + 1)) {
/* timed out, enter rebinding state */
dhcp_state = REBINDING;
timeout = (t2 - t1) * 1000;
log_line("Entering rebinding state.");
} else {
/* send a request packet */
send_renew(xid, server_addr, requested_ip); /* unicast */
t1 = ((t2 - t1) >> 1) + t1;
timeout = (t1 * 1000) - (curms() - start);
}
}
static void bound_timeout()
{
/* Lease is starting to run out, time to enter renewing state */
dhcp_state = RENEWING;
change_listen_mode(LISTEN_KERNEL);
log_line("Entering renew state.");
renewing_timeout();
}
static void rebinding_timeout()
{
/* Either set a new T2, or enter INIT state */
if ((lease - t2) <= (lease / 14400 + 1)) {
/* timed out, enter init state */
dhcp_state = INIT_SELECTING;
log_line("Lease lost, entering init state.");
run_script(NULL, SCRIPT_DECONFIG);
timeout = 0;
packet_num = 0;
change_listen_mode(LISTEN_RAW);
} else {
/* send a request packet */
send_renew(xid, 0, requested_ip); /* broadcast */
t2 = ((lease - t2) >> 1) + t2;
timeout = (t2 * 1000) - (curms() - start);
}
}
/* Handle epoll timeout expiring */
static void handle_timeout(void) static void handle_timeout(void)
{ {
switch (dhcp_state) { switch (dhcp_state) {
case INIT_SELECTING: case INIT_SELECTING: init_selecting_timeout(); break;
if (packet_num < NUMPACKETS) { case RENEW_REQUESTED: renew_requested_timeout(); break;
if (packet_num == 0) case REQUESTING: requesting_timeout(); break;
xid = random_xid(); case RENEWING: renewing_timeout(); break;
/* broadcast */ case BOUND: bound_timeout(); break;
send_discover(xid, requested_ip); case REBINDING: rebinding_timeout(); break;
case RELEASED: timeout = -1; break;
case ARP_CHECK: arp_success(); break;
default: break;
}
}
timeout = ((packet_num == NUMPACKETS - 1) ? 4 : 2) * 1000; static void init_selecting_packet(struct dhcpMessage *packet,
packet_num++; unsigned char *message)
} else { {
if (client_config.background_if_no_lease) { unsigned char *temp = NULL;
log_line("No lease, going to background."); /* Must be a DHCPOFFER to one of our xid's */
background(); if (*message == DHCPOFFER) {
} else if (client_config.abort_if_no_lease) { if ((temp = get_option(packet, DHCP_SERVER_ID))) {
log_line("No lease, failing."); /* Memcpy to a temp buffer to force alignment */
exit(EXIT_FAILURE); memcpy(&server_addr, temp, 4);
} xid = packet->xid;
/* wait to try again */ requested_ip = packet->yiaddr;
packet_num = 0;
timeout = RETRY_DELAY * 1000; /* enter requesting state */
} dhcp_state = REQUESTING;
timeout = 0;
packet_num = 0;
} else {
log_line("No server ID in message");
}
}
}
static void dhcp_ack_or_nak_packet(struct dhcpMessage *packet,
unsigned char *message)
{
unsigned char *temp = NULL;
if (*message == DHCPACK) {
if (!(temp = get_option(packet, DHCP_LEASE_TIME))) {
log_line("No lease time received, assuming 1h.");
lease = 60 * 60;
} else {
/* Memcpy to a temp buffer to force alignment */
memcpy(&lease, temp, 4);
lease = ntohl(lease);
/* Enforce upper and lower bounds on lease. */
lease &= 0x0fffffff;
if (lease < RETRY_DELAY)
lease = RETRY_DELAY;
}
arp_prev_dhcp_state = dhcp_state;
dhcp_state = ARP_CHECK;
memcpy(&arp_dhcp_packet, packet, sizeof (struct dhcpMessage));
arpFd = arpping(arp_dhcp_packet.yiaddr, NULL, 0,
client_config.arp, client_config.interface);
epoll_add(arpFd);
timeout = 2000;
memset(&arpreply, 0, sizeof arpreply);
arpreply_offset = 0;
// Can transition to BOUND or INIT_SELECTING.
} else if (*message == DHCPNAK) {
/* return to init state */
log_line("Received DHCP NAK.");
run_script(packet, SCRIPT_NAK);
if (dhcp_state != REQUESTING)
run_script(NULL, SCRIPT_DECONFIG);
dhcp_state = INIT_SELECTING;
timeout = 0;
requested_ip = 0;
packet_num = 0;
change_listen_mode(LISTEN_RAW);
// XXX: this isn't rfc compliant: should be exp backoff
sleep(3); /* avoid excessive network traffic */
}
}
static void handle_packet(void)
{
unsigned char *message = NULL;
int len;
struct dhcpMessage packet;
if (listen_mode == LISTEN_KERNEL)
len = get_packet(&packet, listenFd);
else if (listen_mode == LISTEN_RAW)
len = get_raw_packet(&packet, listenFd);
else /* LISTEN_NONE */
return;
if (len == -1 && errno != EINTR) {
log_error("reopening socket.");
change_listen_mode(listen_mode); /* just close and reopen */
}
if (len < 0)
return;
if (packet.xid != xid) {
log_line("Ignoring XID %lx (our xid is %lx).",
(uint32_t) packet.xid, xid);
return;
}
if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
log_line("couldnt get option from packet -- ignoring");
return;
}
switch (dhcp_state) {
case INIT_SELECTING:
init_selecting_packet(&packet, message);
break;
case ARP_CHECK:
/* We ignore dhcp packets for now. This state will
* be changed by the callback for arp ping.
*/
break; break;
case RENEW_REQUESTED: case RENEW_REQUESTED:
case REQUESTING: case REQUESTING:
if (packet_num < NUMPACKETS) { case RENEWING:
/* send request packet */ case REBINDING:
if (dhcp_state == RENEW_REQUESTED) dhcp_ack_or_nak_packet(&packet, message);
/* unicast */
send_renew(xid, server_addr, requested_ip);
else
/* broadcast */
send_selecting(xid, server_addr, requested_ip);
timeout = ((packet_num == NUMPACKETS - 1) ? 10 : 2) * 1000;
packet_num++;
} else {
/* timed out, go back to init state */
if (dhcp_state == RENEW_REQUESTED)
run_script(NULL, SCRIPT_DECONFIG);
dhcp_state = INIT_SELECTING;
timeout = 0;
packet_num = 0;
change_listen_mode(LISTEN_RAW);
}
break; break;
case BOUND: case BOUND:
/* Lease is starting to run out, time to enter renewing state */
dhcp_state = RENEWING;
change_listen_mode(LISTEN_KERNEL);
log_line("Entering renew state.");
/* fall right through */
case RENEWING:
/* Either set a new T1, or enter REBINDING state */
if ((t2 - t1) <= (lease / 14400 + 1)) {
/* timed out, enter rebinding state */
dhcp_state = REBINDING;
timeout = (t2 - t1) * 1000;
log_line("Entering rebinding state.");
} else {
/* send a request packet */
send_renew(xid, server_addr, requested_ip); /* unicast */
t1 = ((t2 - t1) >> 1) + t1;
timeout = (t1 * 1000) - (curms() - start);
}
break;
case REBINDING:
/* Either set a new T2, or enter INIT state */
if ((lease - t2) <= (lease / 14400 + 1)) {
/* timed out, enter init state */
dhcp_state = INIT_SELECTING;
log_line("Lease lost, entering init state.");
run_script(NULL, SCRIPT_DECONFIG);
timeout = 0;
packet_num = 0;
change_listen_mode(LISTEN_RAW);
} else {
/* send a request packet */
send_renew(xid, 0, requested_ip); /* broadcast */
t2 = ((lease - t2) >> 1) + t2;
timeout = (t2 * 1000) - (curms() - start);
}
break;
case RELEASED: case RELEASED:
/* yah, I know, *you* say it would never happen */
timeout = -1;
break;
case ARP_CHECK:
/* No response to ARP obviously means that the address is free. */
arp_success();
default: default:
break; break;
} }
@ -457,113 +598,6 @@ static void handle_arp_response(void)
} }
} }
static void handle_packet(void)
{
unsigned char *temp = NULL, *message = NULL;
int len;
struct dhcpMessage packet;
if (listen_mode == LISTEN_KERNEL)
len = get_packet(&packet, listenFd);
else if (listen_mode == LISTEN_RAW)
len = get_raw_packet(&packet, listenFd);
else /* LISTEN_NONE */
return;
if (len == -1 && errno != EINTR) {
log_error("reopening socket.");
change_listen_mode(listen_mode); /* just close and reopen */
}
if (len < 0)
return;
if (packet.xid != xid) {
log_line("Ignoring XID %lx (our xid is %lx).",
(uint32_t) packet.xid, xid);
return;
}
if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
log_line("couldnt get option from packet -- ignoring");
return;
}
switch (dhcp_state) {
case INIT_SELECTING:
/* Must be a DHCPOFFER to one of our xid's */
if (*message == DHCPOFFER) {
if ((temp = get_option(&packet, DHCP_SERVER_ID))) {
/* Memcpy to a temp buffer to force alignment */
memcpy(&server_addr, temp, 4);
xid = packet.xid;
requested_ip = packet.yiaddr;
/* enter requesting state */
dhcp_state = REQUESTING;
timeout = 0;
packet_num = 0;
} else {
log_line("No server ID in message");
}
}
break;
case ARP_CHECK:
/* We ignore dhcp packets for now. This state will
* be changed by the callback for arp ping.
*/
break;
case RENEW_REQUESTED:
case REQUESTING:
case RENEWING:
case REBINDING:
if (*message == DHCPACK) {
if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) {
log_line("No lease time received, assuming 1h.");
lease = 60 * 60;
} else {
/* Memcpy to a temp buffer to force alignment */
memcpy(&lease, temp, 4);
lease = ntohl(lease);
/* Enforce upper and lower bounds on lease. */
lease &= 0x0fffffff;
if (lease < RETRY_DELAY)
lease = RETRY_DELAY;
}
arp_prev_dhcp_state = dhcp_state;
dhcp_state = ARP_CHECK;
memcpy(&arp_dhcp_packet, &packet, sizeof packet);
arpFd = arpping(arp_dhcp_packet.yiaddr, NULL, 0,
client_config.arp, client_config.interface);
epoll_add(arpFd);
timeout = 2000;
memset(&arpreply, 0, sizeof arpreply);
arpreply_offset = 0;
// Can transition to BOUND or INIT_SELECTING.
} else if (*message == DHCPNAK) {
/* return to init state */
log_line("Received DHCP NAK.");
run_script(&packet, SCRIPT_NAK);
if (dhcp_state != REQUESTING)
run_script(NULL, SCRIPT_DECONFIG);
dhcp_state = INIT_SELECTING;
timeout = 0;
requested_ip = 0;
packet_num = 0;
change_listen_mode(LISTEN_RAW);
// XXX: this isn't rfc compliant: should be exp backoff
sleep(3); /* avoid excessive network traffic */
}
break;
case BOUND:
case RELEASED:
default:
break;
}
}
static void setup_signals() static void setup_signals()
{ {
sigset_t mask; sigset_t mask;