Skip to content

Commit

Permalink
Send secs field in DHCP4 requests to handle dhcpd min-secs setting (b…
Browse files Browse the repository at this point in the history
…nc#889314)

Signed-off-by: Olaf Hering <olaf@aepfle.de>
  • Loading branch information
olafhering committed Aug 29, 2014
1 parent a494d61 commit 8752b9b
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 53 deletions.
47 changes: 32 additions & 15 deletions dhcp4/device.c
Expand Up @@ -259,13 +259,15 @@ ni_dhcp4_acquire(ni_dhcp4_device_t *dev, const ni_dhcp4_request_t *info)
return rv;

config = xcalloc(1, sizeof(*config));

/* RFC 2131 4.1 suggests these values */
config->capture_retry_timeout = NI_DHCP4_RESEND_TIMEOUT_INIT;
config->capture_max_timeout = NI_DHCP4_RESEND_TIMEOUT_MAX;

config->dry_run = info->dry_run;
config->start_delay = info->start_delay;
config->defer_timeout = info->start_delay;
config->acquire_timeout = info->acquire_timeout;
config->resend_timeout = NI_DHCP4_RESEND_TIMEOUT_INIT;
config->request_timeout = info->acquire_timeout?: NI_DHCP4_REQUEST_TIMEOUT;
config->initial_discovery_timeout = NI_DHCP4_DISCOVERY_TIMEOUT;
config->uuid = info->uuid;
config->flags = info->flags;
config->update = info->update;
Expand Down Expand Up @@ -306,7 +308,7 @@ ni_dhcp4_acquire(ni_dhcp4_device_t *dev, const ni_dhcp4_request_t *info)

if (ni_debug & NI_TRACE_DHCP) {
ni_trace("Received request:");
ni_trace(" acquire-timeout %u", config->request_timeout);
ni_trace(" acquire-timeout %u", config->acquire_timeout);
ni_trace(" lease-time %u", config->max_lease_time);
ni_trace(" start-delay %u", config->start_delay);
ni_trace(" hostname %s", config->hostname[0]? config->hostname : "<none>");
Expand Down Expand Up @@ -539,6 +541,23 @@ ni_dhcp4_device_drop_buffer(ni_dhcp4_device_t *dev)
ni_buffer_destroy(&dev->message);
}

static int
ni_dhcp4_device_prepare_message(void *data)
{
ni_dhcp4_device_t *dev = data;

/* Allocate an empty buffer */
ni_dhcp4_device_alloc_buffer(dev);

/* Build the DHCP4 message */
if (ni_dhcp4_build_message(dev, dev->transmit.msg_code, dev->transmit.lease, &dev->message) < 0) {
/* This is really terminal */
ni_error("unable to build DHCP4 message");
return -1;
}
return 0;
}

int
ni_dhcp4_device_send_message(ni_dhcp4_device_t *dev, unsigned int msg_code, const ni_addrconf_lease_t *lease)
{
Expand All @@ -552,22 +571,18 @@ ni_dhcp4_device_send_message(ni_dhcp4_device_t *dev, unsigned int msg_code, cons
ni_dhcp4_xid = random();
dev->dhcp4.xid = ni_dhcp4_xid++;

dev->transmit.msg_code = msg_code;
dev->transmit.lease = lease;

if (ni_dhcp4_socket_open(dev) < 0) {
ni_error("unable to open capture socket");
goto transient_failure;
}

ni_debug_dhcp("sending %s with xid 0x%x", ni_dhcp4_message_name(msg_code), htonl(dev->dhcp4.xid));

/* Allocate an empty buffer */
ni_dhcp4_device_alloc_buffer(dev);

/* Build the DHCP4 message */
if ((rv = ni_dhcp4_build_message(dev, msg_code, lease, buf)) < 0) {
/* This is really terminal */
ni_error("unable to build DHCP4 message");
if ((rv = ni_dhcp4_device_prepare_message(dev)) < 0)
return -1;
}

switch (msg_code) {
case DHCP4_DECLINE:
Expand All @@ -579,12 +594,14 @@ ni_dhcp4_device_send_message(ni_dhcp4_device_t *dev, unsigned int msg_code, cons
case DHCP4_REQUEST:
case DHCP4_INFORM:
memset(&timeout, 0, sizeof(timeout));
timeout.timeout = dev->config->resend_timeout;
timeout.increment = dev->config->resend_timeout;
timeout.timeout = dev->config->capture_retry_timeout;
timeout.increment = -1;
timeout.max_timeout = dev->config->capture_timeout;
timeout.nretries = -1;
timeout.jitter.min = -1;/* add a random jitter of +/-1 sec */
timeout.jitter.max = 1;
timeout.max_timeout = NI_DHCP4_RESEND_TIMEOUT_MAX;
timeout.timeout_callback = ni_dhcp4_device_prepare_message;
timeout.timeout_data = dev;

if (dev->fsm.state == NI_DHCP4_STATE_RENEWING) {
struct sockaddr_in sin = {
Expand Down
23 changes: 12 additions & 11 deletions dhcp4/dhcp.h
Expand Up @@ -60,6 +60,11 @@ typedef struct ni_dhcp4_device {
unsigned int failed : 1,
notify : 1;

struct {
unsigned int msg_code;
const ni_addrconf_lease_t *lease;
} transmit;

struct {
uint32_t xid;
unsigned int nak_backoff; /* backoff timer when we get NAKs */
Expand All @@ -80,14 +85,11 @@ typedef struct ni_dhcp4_device {
} best_offer;
} ni_dhcp4_device_t;

#define NI_DHCP4_RESEND_TIMEOUT_INIT 3 /* seconds */
#define NI_DHCP4_RESEND_TIMEOUT_MAX 60 /* seconds */
#define NI_DHCP4_RESEND_TIMEOUT_INIT 4 /* seconds */
#define NI_DHCP4_RESEND_TIMEOUT_MAX 64 /* seconds */
#define NI_DHCP4_REQUEST_TIMEOUT 60 /* seconds */
#define NI_DHCP4_ARP_TIMEOUT 200 /* msec */

/* Initial discovery period while we scan all available leases. */
#define NI_DHCP4_DISCOVERY_TIMEOUT 20 /* seconds */

enum {
DHCP4_DO_ARP = 0x00000001, /* TODO: -> wickedd */
DHCP4_DO_HOSTNAME = 0x00000002,
Expand Down Expand Up @@ -172,12 +174,11 @@ struct ni_dhcp4_config {

unsigned int start_delay;
unsigned int defer_timeout;
unsigned int acquire_timeout;

/* cleanup this: */
unsigned int initial_discovery_timeout;
unsigned int request_timeout;
unsigned int resend_timeout;
unsigned int capture_retry_timeout; /* timeout for first request */
unsigned int capture_max_timeout; /* timeout for the capture */
unsigned int capture_timeout; /* timeout for actual capture, then fsm restarts */
unsigned int elapsed_timeout; /* elapsed time within fsm */
unsigned int acquire_timeout; /* 0 means retry forever */

/* A combination of DHCP4_DO_* flags above */
unsigned int update;
Expand Down
99 changes: 72 additions & 27 deletions dhcp4/fsm.c
Expand Up @@ -222,6 +222,7 @@ ni_dhcp4_fsm_restart(ni_dhcp4_device_t *dev)
dev->fsm.timer = NULL;
}
dev->dhcp4.xid = 0;
dev->config->elapsed_timeout = 0;

ni_dhcp4_device_drop_lease(dev);
}
Expand Down Expand Up @@ -260,7 +261,10 @@ __ni_dhcp4_fsm_discover(ni_dhcp4_device_t *dev, int scan_offers)
{
ni_addrconf_lease_t *lease;

ni_info("%s: Initiating DHCPv4 discovery (ifindex %d)", dev->ifname, dev->link.ifindex);
if (dev->config->elapsed_timeout)
ni_info("%s: Reinitiating DHCPv4 discovery (ifindex %d)", dev->ifname, dev->link.ifindex);
else
ni_info("%s: Initiating DHCPv4 discovery (ifindex %d)", dev->ifname, dev->link.ifindex);

/* If we already have a lease, try asking for the same.
* If not, create a dummy lease with NULL fields.
Expand All @@ -272,20 +276,23 @@ __ni_dhcp4_fsm_discover(ni_dhcp4_device_t *dev, int scan_offers)
lease->uuid = dev->config->uuid;

dev->fsm.state = NI_DHCP4_STATE_SELECTING;
ni_dhcp4_device_send_message(dev, DHCP4_DISCOVER, lease);

dev->dhcp4.accept_any_offer = 1;

ni_debug_dhcp("valid lease: %d; have prefs: %d",
ni_addrconf_lease_is_valid(dev->lease),
ni_dhcp4_config_have_server_preference());
if (ni_addrconf_lease_is_valid(dev->lease)
|| (scan_offers && ni_dhcp4_config_have_server_preference())) {
ni_dhcp4_fsm_set_timeout(dev, dev->config->initial_discovery_timeout);
dev->dhcp4.accept_any_offer = 0;
} else {
ni_dhcp4_fsm_set_timeout(dev, dev->config->request_timeout);
}

dev->config->capture_timeout = dev->config->capture_max_timeout;
if (dev->config->acquire_timeout && dev->config->acquire_timeout - dev->config->elapsed_timeout < dev->config->capture_max_timeout)
dev->config->capture_timeout = dev->config->acquire_timeout - dev->config->elapsed_timeout;
ni_dhcp4_fsm_set_timeout(dev, dev->config->capture_timeout);

ni_dhcp4_device_send_message(dev, DHCP4_DISCOVER, lease);

ni_dhcp4_device_drop_best_offer(dev);

if (lease != dev->lease)
Expand All @@ -295,19 +302,20 @@ __ni_dhcp4_fsm_discover(ni_dhcp4_device_t *dev, int scan_offers)
static void
ni_dhcp4_fsm_discover(ni_dhcp4_device_t *dev)
{
dev->start_time = time(NULL);
dev->config->elapsed_timeout = 0;
__ni_dhcp4_fsm_discover(dev, 1);
}

static void
ni_dhcp4_fsm_request(ni_dhcp4_device_t *dev, const ni_addrconf_lease_t *lease)
{
ni_info("%s: Requesting DHCPv4 lease with timeout %d sec",
dev->ifname, dev->config->request_timeout);

dev->fsm.state = NI_DHCP4_STATE_REQUESTING;
/* Ignore the return value; sending the request may actually
* fail transiently */
ni_dhcp4_fsm_set_timeout(dev, dev->config->request_timeout);
dev->config->capture_timeout = dev->config->capture_max_timeout;
if (dev->config->acquire_timeout && dev->config->acquire_timeout - dev->config->elapsed_timeout < dev->config->capture_max_timeout)
dev->config->capture_timeout = dev->config->acquire_timeout - dev->config->elapsed_timeout;
ni_dhcp4_fsm_set_timeout(dev, dev->config->capture_timeout);

ni_dhcp4_device_send_message(dev, DHCP4_REQUEST, lease);
}

Expand All @@ -317,6 +325,7 @@ ni_dhcp4_fsm_renewal(ni_dhcp4_device_t *dev)
ni_info("%s: Initiating renewal of DHCPv4 lease",
dev->ifname);

dev->start_time = time(NULL);
dev->fsm.state = NI_DHCP4_STATE_RENEWING;
ni_dhcp4_fsm_set_deadline(dev,
dev->lease->time_acquired + dev->lease->dhcp4.rebind_time);
Expand All @@ -326,31 +335,44 @@ ni_dhcp4_fsm_renewal(ni_dhcp4_device_t *dev)
static void
ni_dhcp4_fsm_reboot(ni_dhcp4_device_t *dev)
{
time_t deadline;
time_t now = time(NULL);
time_t expire_time, deadline = now + 10;

/* RFC 2131, 3.2 (see also 3.1) */
ni_debug_dhcp("trying to confirm lease for %s", dev->ifname);

dev->config->elapsed_timeout = 0;
dev->start_time = time(NULL);
dev->fsm.state = NI_DHCP4_STATE_REBOOT;
deadline = time(NULL) + 10;
if (deadline > dev->lease->time_acquired + dev->lease->dhcp4.rebind_time)
deadline = dev->lease->time_acquired + dev->lease->dhcp4.rebind_time;

ni_dhcp4_fsm_set_deadline(dev, deadline);
expire_time = dev->lease->time_acquired + dev->lease->dhcp4.rebind_time;
if (expire_time > now && deadline > expire_time)
deadline = expire_time;
dev->config->capture_timeout = deadline - now;

ni_dhcp4_fsm_set_timeout(dev, dev->config->capture_timeout);
ni_dhcp4_device_send_message(dev, DHCP4_REQUEST, dev->lease);
}

static void
ni_dhcp4_fsm_rebind(ni_dhcp4_device_t *dev)
{
time_t expire_time, now = time(NULL);

ni_info("%s: Initiating rebind of DHCPv4 lease",
dev->ifname);

dev->lease->dhcp4.server_id.s_addr = 0;

dev->start_time = time(NULL);
dev->fsm.state = NI_DHCP4_STATE_REBINDING;
ni_dhcp4_fsm_set_deadline(dev,
dev->lease->time_acquired + dev->lease->dhcp4.lease_time);
dev->lease->dhcp4.server_id.s_addr = 0;
expire_time = dev->lease->time_acquired + dev->lease->dhcp4.lease_time;
dev->config->capture_timeout = dev->config->capture_max_timeout;
if (expire_time > now) {
if (expire_time - now < dev->config->capture_max_timeout)
dev->config->capture_timeout = expire_time - now;
ni_dhcp4_fsm_set_deadline(dev, expire_time);
} else
ni_dhcp4_fsm_set_timeout(dev, dev->config->capture_timeout);
ni_dhcp4_device_send_message(dev, DHCP4_REQUEST, dev->lease);
}

Expand All @@ -359,6 +381,8 @@ ni_dhcp4_fsm_decline(ni_dhcp4_device_t *dev)
{
ni_warn("%s: Declining DHCPv4 lease with address %s", dev->ifname,
inet_ntoa(dev->lease->dhcp4.address));

dev->start_time = time(NULL);
dev->fsm.state = NI_DHCP4_STATE_INIT;
ni_dhcp4_device_send_message(dev, DHCP4_DECLINE, dev->lease);

Expand Down Expand Up @@ -390,10 +414,13 @@ ni_dhcp4_fsm_release(ni_dhcp4_device_t *dev)
static void
ni_dhcp4_fsm_timeout(ni_dhcp4_device_t *dev)
{
ni_dhcp4_config_t *conf = dev->config;
ni_debug_dhcp("%s: timeout in state %s",
dev->ifname, ni_dhcp4_fsm_state_name(dev->fsm.state));
dev->fsm.timer = NULL;

conf->elapsed_timeout += conf->capture_timeout;

switch (dev->fsm.state) {
case NI_DHCP4_STATE_INIT:
/* We get here if we previously received a NAK, and have
Expand All @@ -405,7 +432,6 @@ ni_dhcp4_fsm_timeout(ni_dhcp4_device_t *dev)

case NI_DHCP4_STATE_SELECTING:
if (!dev->dhcp4.accept_any_offer) {
ni_dhcp4_config_t *conf = dev->config;

/* We were scanning all offers to check for a best offer.
* There was no perfect match, but we may have a "good enough"
Expand All @@ -421,22 +447,35 @@ ni_dhcp4_fsm_timeout(ni_dhcp4_device_t *dev)
}

ni_dhcp4_fsm_fail_lease(dev);
if (conf->initial_discovery_timeout < conf->request_timeout) {
__ni_dhcp4_fsm_discover(dev, 0);
return;
}
dev->start_time = time(NULL);
}
if (conf->acquire_timeout == 0) {
ni_debug_dhcp("%s: discovery got no (valid) reply, retrying.", dev->ifname);
__ni_dhcp4_fsm_discover(dev, 0);
return;
} else if (conf->elapsed_timeout < conf->acquire_timeout) {
ni_debug_dhcp("%s: discovery got no (valid) reply, retrying. %u seconds left until timeout.",
dev->ifname, conf->acquire_timeout - conf->elapsed_timeout);
__ni_dhcp4_fsm_discover(dev, 0);
return;
}
/* fallthrough */

case NI_DHCP4_STATE_REQUESTING:
if (conf->acquire_timeout && conf->elapsed_timeout < conf->acquire_timeout) {
ni_debug_dhcp("%s: discovery got no (valid) reply, retrying. %u seconds left until timeout.",
dev->ifname, conf->acquire_timeout - conf->elapsed_timeout);
ni_dhcp4_fsm_request(dev, dev->transmit.lease);
return;
}
ni_error("%s: DHCP4 discovery failed", dev->ifname);
ni_dhcp4_fsm_fail_lease(dev);
ni_dhcp4_fsm_restart(dev);

ni_dhcp4_send_event(NI_DHCP4_EVENT_LOST, dev, NULL);

/* Now decide whether we should keep trying */
if (dev->config->request_timeout == ~0U)
if (dev->config->acquire_timeout == 0)
ni_dhcp4_fsm_discover(dev);
break;

Expand Down Expand Up @@ -491,6 +530,8 @@ __ni_dhcp4_fsm_timeout(void *user_data, const ni_timer_t *timer)
void
ni_dhcp4_fsm_link_up(ni_dhcp4_device_t *dev)
{
dev->start_time = time(NULL);

if (dev->config == NULL)
return;

Expand Down Expand Up @@ -567,6 +608,10 @@ ni_dhcp4_process_offer(ni_dhcp4_device_t *dev, ni_addrconf_lease_t *lease)
ni_dhcp4_fsm_restart(dev);
ni_dhcp4_device_stop(dev);
} else {
ni_info("%s: Requesting DHCPv4 lease with timeout %u sec",
dev->ifname, dev->config->acquire_timeout);
dev->start_time = time(NULL);
dev->config->elapsed_timeout = 0;
ni_dhcp4_fsm_request(dev, lease);
}
return 0;
Expand Down
2 changes: 2 additions & 0 deletions include/wicked/socket.h
Expand Up @@ -27,6 +27,8 @@ typedef struct ni_timeout_param {
unsigned int max_timeout; /* timeout is capped by max_timeout */

ni_bool_t (*backoff_callback)(struct ni_timeout_param *);
int (*timeout_callback)(void *);
void *timeout_data;
} ni_timeout_param_t;

typedef struct ni_timer ni_timer_t;
Expand Down
3 changes: 3 additions & 0 deletions src/capture.c
Expand Up @@ -361,6 +361,9 @@ ni_capture_retransmit(ni_capture_t *capture)
if (!ni_timeout_recompute(&capture->retrans.timeout))
return;

if (capture->retrans.timeout.timeout_callback)
capture->retrans.timeout.timeout_callback(capture->retrans.timeout.timeout_data);

rv = __ni_capture_send(capture, capture->retrans.buffer);

/* We don't care whether sending failed or not. Quite possibly
Expand Down

0 comments on commit 8752b9b

Please sign in to comment.