Centralize DHCP timeout, packet reciept, and user-demanded action handling
into state.[ch]. Remove timeout.c.
This commit is contained in:
parent
bef54a23fb
commit
76ecfffce2
@ -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;
|
||||||
|
@ -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,
|
||||||
|
86
ndhc/ndhc.c
86
ndhc/ndhc.c
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
@ -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
314
ndhc/state.c
Normal 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
26
ndhc/state.h
Normal 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
|
||||||
|
|
160
ndhc/timeout.c
160
ndhc/timeout.c
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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_ */
|
|
Loading…
Reference in New Issue
Block a user