
git subtree pull --prefix shared/n-dhcp4/ git@github.com:nettools/n-dhcp4.git master --squash
1303 lines
47 KiB
C
1303 lines
47 KiB
C
/*
|
|
* DHCPv4 Client Probes
|
|
*
|
|
* The probe object is used to represent the lifetime of a DHCP client session.
|
|
* A running probe discovers DHCP servers, requests a lease, and maintains that
|
|
* lease.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <c-list.h>
|
|
#include <c-siphash.h>
|
|
#include <c-stdaux.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/auxv.h>
|
|
#include "n-dhcp4.h"
|
|
#include "n-dhcp4-private.h"
|
|
|
|
|
|
static int n_dhcp4_client_probe_option_new(NDhcp4ClientProbeOption **optionp,
|
|
uint8_t option,
|
|
const void *data,
|
|
uint8_t n_data) {
|
|
NDhcp4ClientProbeOption *op;
|
|
|
|
op = malloc(sizeof(*op) + n_data);
|
|
if (!op)
|
|
return -ENOMEM;
|
|
|
|
op->option = option;
|
|
op->n_data = n_data;
|
|
memcpy(op->data, data, n_data);
|
|
|
|
*optionp = op;
|
|
return 0;
|
|
}
|
|
|
|
static void n_dhcp4_client_probe_option_free(NDhcp4ClientProbeOption *option) {
|
|
if (option)
|
|
free(option);
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_config_new() - create new probe configuration
|
|
* @configp: output argument to store new configuration
|
|
*
|
|
* This creates a new probe configuration object. The object is a collection of
|
|
* parameters for probes. No data verification is done by the configuration
|
|
* object. Instead, when passing the configuration to the constructor of a
|
|
* probe, this constructor will perform parameter validation.
|
|
*
|
|
* A probe configuration is an unlinked object only used to pass information to
|
|
* a probe constructor. The caller fully owns the returned configuration object
|
|
* and is responsible to free it when no longer needed.
|
|
*
|
|
* Return: 0 on success, negative error code on failure.
|
|
*/
|
|
_c_public_ int n_dhcp4_client_probe_config_new(NDhcp4ClientProbeConfig **configp) {
|
|
_c_cleanup_(n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *config = NULL;
|
|
|
|
config = calloc(1, sizeof(*config));
|
|
if (!config)
|
|
return -ENOMEM;
|
|
|
|
*config = (NDhcp4ClientProbeConfig)N_DHCP4_CLIENT_PROBE_CONFIG_NULL(*config);
|
|
|
|
*configp = config;
|
|
config = NULL;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_config_free() - destroy probe configuration
|
|
* @config: configuration to operate on, or NULL
|
|
*
|
|
* This destroys a probe configuration object and deallocates all its
|
|
* resources.
|
|
*
|
|
* If @config is NULL, this is a no-op.
|
|
*
|
|
* Return: NULL is returned.
|
|
*/
|
|
_c_public_ NDhcp4ClientProbeConfig *n_dhcp4_client_probe_config_free(NDhcp4ClientProbeConfig *config) {
|
|
if (!config)
|
|
return NULL;
|
|
|
|
for (unsigned int i = 0; i <= UINT8_MAX; ++i)
|
|
n_dhcp4_client_probe_option_free(config->options[i]);
|
|
|
|
free(config);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_config_dup() - duplicate probe configuration
|
|
* @config: configuration to operate on
|
|
* @dupp: output argument for duplicate
|
|
*
|
|
* This duplicates the probe configuration given as @config and returns it in
|
|
* @dupp to the caller.
|
|
*
|
|
* Return: 0 on success, negative error code on failure.
|
|
*/
|
|
int n_dhcp4_client_probe_config_dup(NDhcp4ClientProbeConfig *config,
|
|
NDhcp4ClientProbeConfig **dupp) {
|
|
_c_cleanup_(n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *dup = NULL;
|
|
int r;
|
|
|
|
r = n_dhcp4_client_probe_config_new(&dup);
|
|
if (r)
|
|
return r;
|
|
|
|
dup->inform_only = config->inform_only;
|
|
dup->init_reboot = config->init_reboot;
|
|
dup->requested_ip = config->requested_ip;
|
|
dup->ms_start_delay = config->ms_start_delay;
|
|
|
|
for (unsigned int i = 0; i < config->n_request_parameters; ++i)
|
|
dup->request_parameters[dup->n_request_parameters++] = config->request_parameters[i];
|
|
|
|
for (unsigned int i = 0; i <= UINT8_MAX; ++i) {
|
|
if (!config->options[i])
|
|
break;
|
|
|
|
r = n_dhcp4_client_probe_option_new(&dup->options[i],
|
|
config->options[i]->option,
|
|
config->options[i]->data,
|
|
config->options[i]->n_data);
|
|
if (r)
|
|
return r;
|
|
}
|
|
|
|
*dupp = dup;
|
|
dup = NULL;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_config_set_inform_only() - set inform-only property
|
|
* @config: configuration to operate on
|
|
* @inform_only: value to set
|
|
*
|
|
* This sets the inform-only property of the given configuration object. This
|
|
* property controls whether the client probe should request a full lease, or
|
|
* whether it should just ask for auxiliary information without requesting an
|
|
* address.
|
|
*
|
|
* The default is to request a full lease and address. If inform-only is set to
|
|
* true, only auxiliary information will be requested.
|
|
*
|
|
* XXX: This is currently not implemented, and setting the property has no effect.
|
|
*/
|
|
_c_public_ void n_dhcp4_client_probe_config_set_inform_only(NDhcp4ClientProbeConfig *config, bool inform_only) {
|
|
config->inform_only = inform_only;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_config_set_init_reboot() - set init-reboot property
|
|
* @config: configuration to operate on
|
|
* @init_reboot: value to set
|
|
*
|
|
* This sets the init-reboot property of the given configuration object. If this
|
|
* is enabled, a requested IP address must also be set.
|
|
*
|
|
* The default is false. If set to true, a probe will make use of the
|
|
* INIT-REBOOT path, as described by the DHCP specification. In most cases, you
|
|
* do not want this.
|
|
*
|
|
* Background: The INIT-REBOOT path allows a DHCP client to skip
|
|
* server-discovery when rebooting/resuming their machine. The DHCP
|
|
* client simply re-requests the lease it had acquired before. This
|
|
* saves one roundtrip in the success-case, since the DISCOVER step
|
|
* is skipped. However, there are little to no timeouts involved,
|
|
* so the roundtrip should be barely noticeable. In contrast, if
|
|
* the INIT-REBOOT fails (because the lease is no longer valid, or
|
|
* not valid on this network), the client has to wait for a
|
|
* possible answer to the request before actually starting the DHCP
|
|
* process all over. This significantly increases the time needed
|
|
* to switch networks.
|
|
* The INIT-REBOOT state might have been a real improvements with
|
|
* the old resend-timeouts mandated by the DHCP specification.
|
|
* However, on modern networks with improved timeout values we
|
|
* recommend against using it.
|
|
*/
|
|
_c_public_ void n_dhcp4_client_probe_config_set_init_reboot(NDhcp4ClientProbeConfig *config, bool init_reboot) {
|
|
config->init_reboot = init_reboot;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_config_set_requested_ip() - set requested-ip property
|
|
* @config: configuration to operate on
|
|
* @ip: value to set
|
|
*
|
|
* This sets the requested-ip property of the given configuration object.
|
|
*
|
|
* The default is all 0. If set to something else, the DHCP discovery will
|
|
* include this IP in its requests to tell DHCP servers which address to pick.
|
|
* Servers are not required to honor this, nor does this have any effect on
|
|
* servers not serving this address.
|
|
*
|
|
* This field should always be set if the caller knows of an address that was
|
|
* previously acquired on this network. It serves as hint to servers and will
|
|
* allow them to provide the same address again.
|
|
*/
|
|
_c_public_ void n_dhcp4_client_probe_config_set_requested_ip(NDhcp4ClientProbeConfig *config, struct in_addr ip) {
|
|
config->requested_ip = ip;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_config_set_start_delay() - set start delay
|
|
* @config: configuration to operate on
|
|
* @msecs: value to set
|
|
*
|
|
* This sets the start delay property of the given configuration object.
|
|
*
|
|
* The default is 9000 ms, which is based on RFC2131. In the RFC the start
|
|
* delay is specified to be a random value in the range 1000 to 10.000 ms.
|
|
* However, there does not appear to be any particular reason to
|
|
* unconditionally wait at least one second, so we move the range down to
|
|
* start at 0 ms. The reaon for the random delay is to avoid network-wide
|
|
* events causing too much simultaneous network traffic. However, on modern
|
|
* networks, a more reasonable value may be in the 10 ms range.
|
|
*/
|
|
_c_public_ void n_dhcp4_client_probe_config_set_start_delay(NDhcp4ClientProbeConfig *config, uint64_t msecs) {
|
|
config->ms_start_delay = msecs;
|
|
}
|
|
|
|
/**
|
|
* n_dhpc4_client_probe_config_request_option() - append option to request from the server
|
|
* @config: configuration to operate on
|
|
* @option: option to request
|
|
*
|
|
* This adds an option to the list of options to request from the server.
|
|
*
|
|
* A server may send options that we do not requst, and it may omit options
|
|
* that we do request. However, to increase the likelyhood of uniform behavior
|
|
* between server implementations, we do not expose options that were not
|
|
* explicitly requested.
|
|
*
|
|
* When called multiple times, the order matters. Earlier requests are
|
|
* considered higher priority than later requests, in case the server must omit
|
|
* some, due to a lack of space. If the same option is requested more than once,
|
|
* only the first call has an effect.
|
|
*/
|
|
_c_public_ void n_dhcp4_client_probe_config_request_option(NDhcp4ClientProbeConfig *config, uint8_t option) {
|
|
for (unsigned int i = 0; i < config->n_request_parameters; ++i) {
|
|
if (config->request_parameters[i] == option)
|
|
return;
|
|
}
|
|
|
|
c_assert(config->n_request_parameters <= UINT8_MAX);
|
|
|
|
config->request_parameters[config->n_request_parameters++] = option;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_config_append_option() - append option to outgoing messages
|
|
* @config: configuration to operate on
|
|
* @option: DHCP option number
|
|
* @data: payload
|
|
* @n_data: number of bytes in payload
|
|
*
|
|
* This sets extra options on a given configuration object.
|
|
*
|
|
* These options are appended verbatim to outgoing messages where
|
|
* that is supported by the specification. The same options are
|
|
* appended to all messages.
|
|
*
|
|
* No option may be appended more than once. Options considered internal
|
|
* to the DHCP protocol may not be appended.
|
|
*
|
|
* Return: 0 on success, N_DHCP4_E_DUPLICATE_OPTION if an option has already been
|
|
* appended, N_DHCP4_E_INTERNAL if the option is not configurable, or
|
|
* a negative error code on failure.
|
|
*/
|
|
_c_public_ int n_dhcp4_client_probe_config_append_option(NDhcp4ClientProbeConfig *config,
|
|
uint8_t option,
|
|
const void *data,
|
|
uint8_t n_data) {
|
|
int r;
|
|
|
|
/* XXX: filter internal options */
|
|
|
|
for (unsigned int i = 0; i <= UINT8_MAX; ++i) {
|
|
if (config->options[i]) {
|
|
if (config->options[i]->option == option)
|
|
return N_DHCP4_E_DUPLICATE_OPTION;
|
|
|
|
continue;
|
|
}
|
|
|
|
r = n_dhcp4_client_probe_option_new(&config->options[i],
|
|
option,
|
|
data,
|
|
n_data);
|
|
if (r)
|
|
return r;
|
|
|
|
return 0;
|
|
}
|
|
|
|
c_assert(0);
|
|
return -ENOTRECOVERABLE;
|
|
}
|
|
|
|
static void n_dhcp4_client_probe_config_initialize_random_seed(NDhcp4ClientProbeConfig *config) {
|
|
uint8_t hash_seed[] = {
|
|
0x25, 0x3f, 0x02, 0x75, 0x3a, 0xb8, 0x4f, 0x91,
|
|
0x9d, 0x0a, 0xd6, 0x15, 0x9d, 0x72, 0x7b, 0xcb,
|
|
};
|
|
CSipHash hash = C_SIPHASH_NULL;
|
|
unsigned short int seed16v[3];
|
|
const uint8_t *p;
|
|
uint64_t u64;
|
|
int r;
|
|
|
|
/*
|
|
* Initialize seed48_r(3)
|
|
*
|
|
* We need random jitter for all timeouts and delays, used to reduce
|
|
* network traffic during bursts. This is not meant as security measure
|
|
* but only meant to improve network utilization during bursts. The
|
|
* random source is thus negligible. However, we want, under all
|
|
* circumstances, avoid two instances running with the same seed. Thus
|
|
* we source the seed from AT_RANDOM, which grants us a per-process
|
|
* unique seed. We then add the current time to make sure consequetive
|
|
* instances use different seeds (to avoid clashes if processes are
|
|
* duplicated, or similar), and lastly we add the config memory address
|
|
* to avoid clashes of multiple parallel instances.
|
|
*
|
|
* Again, none of these are meant as security measure, but only to
|
|
* avoid *ACCIDENTAL* seed clashes. That is, in the case that many
|
|
* transactions are started in parallel, we delay the individual
|
|
* messages (as described in the spec), to reduce the traffic on the
|
|
* network and the chance of packets being dropped (and thus triggering
|
|
* timeouts and resends).
|
|
*
|
|
* We hash everything through SipHash, to avoid exposing AT_RANDOM and
|
|
* other sources to the network. We use a static salt to distinguish it
|
|
* from other implementations using the same random source.
|
|
*/
|
|
c_siphash_init(&hash, hash_seed);
|
|
|
|
p = (const uint8_t *)getauxval(AT_RANDOM);
|
|
if (p)
|
|
c_siphash_append(&hash, p, 16);
|
|
|
|
u64 = n_dhcp4_gettime(CLOCK_MONOTONIC);
|
|
c_siphash_append(&hash, (const uint8_t *)&u64, sizeof(u64));
|
|
|
|
c_siphash_append(&hash, (const uint8_t *)&config, sizeof(config));
|
|
|
|
u64 = c_siphash_finalize(&hash);
|
|
|
|
seed16v[0] = (u64 >> 0) ^ (u64 >> 48);
|
|
seed16v[1] = (u64 >> 16) ^ (u64 >> 0);
|
|
seed16v[2] = (u64 >> 32) ^ (u64 >> 16);
|
|
|
|
r = seed48_r(seed16v, &config->entropy);
|
|
c_assert(!r);
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_config_get_random() - get random data
|
|
* @config: config object to operate on
|
|
*
|
|
* Fetch the next 32bit random number from the entropy pool in @config.
|
|
* Note that this is in no way suitable for security purposes.
|
|
*
|
|
* Return: the random data.
|
|
*/
|
|
uint32_t n_dhcp4_client_probe_config_get_random(NDhcp4ClientProbeConfig *config) {
|
|
long int result;
|
|
int r;
|
|
|
|
r = mrand48_r(&config->entropy, &result);
|
|
c_assert(!r);
|
|
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_new() - create new client probe
|
|
* @probep: output argument for new client probe
|
|
* @config: probe configuration
|
|
* @client: client to probe on behalf of
|
|
* @ns_now: the current time
|
|
*
|
|
* This creates a new client probe object.
|
|
*
|
|
* If one is already running, the new one will be immediately (but asynchronously)
|
|
* cancelled. Otherwise, a DISCOVER event is scheduled after a randomized delay.
|
|
*
|
|
* Return: 0 on success, or a negative error code on failure.
|
|
*/
|
|
int n_dhcp4_client_probe_new(NDhcp4ClientProbe **probep,
|
|
NDhcp4ClientProbeConfig *config,
|
|
NDhcp4Client *client,
|
|
uint64_t ns_now) {
|
|
_c_cleanup_(n_dhcp4_client_probe_freep) NDhcp4ClientProbe *probe = NULL;
|
|
bool active;
|
|
int r;
|
|
|
|
/*
|
|
* If there is already a probe attached, we create the new probe in
|
|
* detached state. It will not be linked into the epoll context and not
|
|
* be useful in any way. We immediately raise the CANCELLED event to
|
|
* notify the caller about it.
|
|
*/
|
|
active = !client->current_probe;
|
|
|
|
probe = calloc(1, sizeof(*probe));
|
|
if (!probe)
|
|
return -ENOMEM;
|
|
|
|
*probe = (NDhcp4ClientProbe)N_DHCP4_CLIENT_PROBE_NULL(*probe);
|
|
probe->client = n_dhcp4_client_ref(client);
|
|
|
|
r = n_dhcp4_client_probe_config_dup(config, &probe->config);
|
|
if (r)
|
|
return r;
|
|
|
|
/*
|
|
* XXX: make seed initialization optional, so the entropy can be reused.
|
|
*/
|
|
n_dhcp4_client_probe_config_initialize_random_seed(probe->config);
|
|
|
|
/* The new probe keeps a reference on @client. So we are sure that &client->log_queue
|
|
* stays alive as long as we need it. */
|
|
|
|
r = n_dhcp4_c_connection_init(&probe->connection,
|
|
client->config,
|
|
probe->config,
|
|
&client->log_queue,
|
|
active ? client->fd_epoll : -1);
|
|
if (r)
|
|
return r;
|
|
|
|
if (probe->config->requested_ip.s_addr != INADDR_ANY)
|
|
probe->last_address = probe->config->requested_ip;
|
|
|
|
if (probe->config->init_reboot && probe->last_address.s_addr != INADDR_ANY)
|
|
probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT;
|
|
else
|
|
probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT;
|
|
|
|
if (active) {
|
|
/*
|
|
* Defer the sending of DISCOVER by a random amount (by default up to 9 seconds).
|
|
*/
|
|
if (probe->state == N_DHCP4_CLIENT_PROBE_STATE_INIT)
|
|
probe->ns_deferred = ns_now + (n_dhcp4_client_probe_config_get_random(probe->config) % (probe->config->ms_start_delay * 1000000ULL));
|
|
probe->client->current_probe = probe;
|
|
} else {
|
|
r = n_dhcp4_client_probe_raise(probe,
|
|
NULL,
|
|
N_DHCP4_CLIENT_EVENT_CANCELLED);
|
|
if (r)
|
|
return r;
|
|
}
|
|
|
|
*probep = probe;
|
|
probe = NULL;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_free() - destroy a probe
|
|
* @probe: probe to operate on, or NULL
|
|
*
|
|
* This destroys a probe object and deallocates all its resources.
|
|
*
|
|
* If @probe is NULL, this is a no-op.
|
|
*
|
|
* Return: NULL is returned.
|
|
*/
|
|
_c_public_ NDhcp4ClientProbe *n_dhcp4_client_probe_free(NDhcp4ClientProbe *probe) {
|
|
NDhcp4CEventNode *node, *t_node;
|
|
NDhcp4ClientLease *lease, *t_lease;
|
|
|
|
if (!probe)
|
|
return NULL;
|
|
|
|
c_list_for_each_entry_safe(lease, t_lease, &probe->lease_list, probe_link)
|
|
n_dhcp4_client_lease_unlink(lease);
|
|
|
|
c_list_for_each_entry_safe(node, t_node, &probe->event_list, probe_link)
|
|
n_dhcp4_c_event_node_free(node);
|
|
|
|
if (probe == probe->client->current_probe)
|
|
probe->client->current_probe = NULL;
|
|
|
|
n_dhcp4_client_lease_unref(probe->current_lease);
|
|
n_dhcp4_c_connection_deinit(&probe->connection);
|
|
n_dhcp4_client_unref(probe->client);
|
|
n_dhcp4_client_probe_config_free(probe->config);
|
|
|
|
c_assert(c_list_is_empty(&probe->lease_list));
|
|
c_assert(c_list_is_empty(&probe->event_list));
|
|
free(probe);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_set_userdata() - set userdata pointer
|
|
* @probe: the probe to operate on
|
|
* @userdata: pointer to userdata
|
|
*
|
|
* Set a userdata pointer. The pointed to data is still owned by the caller, and
|
|
* is completely opaque to the probe.
|
|
*/
|
|
_c_public_ void n_dhcp4_client_probe_set_userdata(NDhcp4ClientProbe *probe, void *userdata) {
|
|
probe->userdata = userdata;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_get_userdata() - get userdata pointer
|
|
* @probe: the probe to operate on
|
|
* @userdatap: return pointer for userdata pointer
|
|
*
|
|
* Get the userdata pointer. The lifetime of the userdata and making sure it is
|
|
* still valid when accessed via the probe is the responsibility of the caller.
|
|
*/
|
|
_c_public_ void n_dhcp4_client_probe_get_userdata(NDhcp4ClientProbe *probe, void **userdatap) {
|
|
*userdatap = probe->userdata;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_raise() - XXX
|
|
*/
|
|
int n_dhcp4_client_probe_raise(NDhcp4ClientProbe *probe, NDhcp4CEventNode **nodep, unsigned int event) {
|
|
NDhcp4CEventNode *node;
|
|
int r;
|
|
|
|
r = n_dhcp4_client_raise(probe->client, &node, event);
|
|
if (r)
|
|
return r;
|
|
|
|
switch (event) {
|
|
case N_DHCP4_CLIENT_EVENT_OFFER:
|
|
node->event.offer.probe = probe;
|
|
break;
|
|
case N_DHCP4_CLIENT_EVENT_GRANTED:
|
|
node->event.granted.probe = probe;
|
|
break;
|
|
case N_DHCP4_CLIENT_EVENT_RETRACTED:
|
|
node->event.retracted.probe = probe;
|
|
break;
|
|
case N_DHCP4_CLIENT_EVENT_EXTENDED:
|
|
node->event.extended.probe = probe;
|
|
break;
|
|
case N_DHCP4_CLIENT_EVENT_EXPIRED:
|
|
node->event.expired.probe = probe;
|
|
break;
|
|
case N_DHCP4_CLIENT_EVENT_CANCELLED:
|
|
node->event.cancelled.probe = probe;
|
|
break;
|
|
default:
|
|
c_assert(0);
|
|
n_dhcp4_c_event_node_free(node);
|
|
return -ENOTRECOVERABLE;
|
|
}
|
|
|
|
if (nodep)
|
|
*nodep = node;
|
|
return 0;
|
|
}
|
|
|
|
void n_dhcp4_client_probe_get_timeout(NDhcp4ClientProbe *probe, uint64_t *timeoutp) {
|
|
uint64_t t1 = 0;
|
|
uint64_t t2 = 0;
|
|
uint64_t lifetime = 0;
|
|
uint64_t timeout = 0;
|
|
|
|
if (probe->current_lease) {
|
|
t1 = probe->current_lease->t1;
|
|
t2 = probe->current_lease->t2;
|
|
lifetime = probe->current_lease->lifetime;
|
|
}
|
|
|
|
n_dhcp4_c_connection_get_timeout(&probe->connection, &timeout);
|
|
|
|
switch (probe->state) {
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
|
|
/* send DHCP request immediately */
|
|
timeout = 1;
|
|
break;
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
|
|
if (probe->ns_reinit && (!timeout || probe->ns_reinit < timeout))
|
|
timeout = probe->ns_reinit;
|
|
break;
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT:
|
|
if (probe->ns_deferred && (!timeout || probe->ns_deferred < timeout))
|
|
timeout = probe->ns_deferred;
|
|
|
|
break;
|
|
case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
|
|
if (t1 && (!timeout || t1 < timeout))
|
|
timeout = t1;
|
|
|
|
/* fall-through */
|
|
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
|
|
if (t2 && (!timeout || t2 < timeout))
|
|
timeout = t2;
|
|
|
|
/* fall-through */
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
|
|
if (lifetime && (!timeout || lifetime < timeout))
|
|
timeout = lifetime;
|
|
break;
|
|
default:
|
|
/* ignore */
|
|
break;
|
|
}
|
|
|
|
*timeoutp = timeout;
|
|
}
|
|
|
|
static int n_dhcp4_client_probe_outgoing_append_options(NDhcp4ClientProbe *probe, NDhcp4Outgoing *outgoing) {
|
|
int r;
|
|
|
|
for (unsigned int i = 0; i <= UINT8_MAX; ++i) {
|
|
if (!probe->config->options[i])
|
|
break;
|
|
|
|
r = n_dhcp4_outgoing_append(outgoing,
|
|
probe->config->options[i]->option,
|
|
probe->config->options[i]->data,
|
|
probe->config->options[i]->n_data);
|
|
if (r) {
|
|
if (r == N_DHCP4_E_NO_SPACE)
|
|
/* XXX */
|
|
break;
|
|
|
|
return r;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int n_dhcp4_client_probe_transition_reboot(NDhcp4ClientProbe *probe, uint64_t ns_now) {
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL;
|
|
int r;
|
|
|
|
switch (probe->state) {
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
|
|
r = n_dhcp4_c_connection_listen(&probe->connection);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_c_connection_reboot_new(&probe->connection, &request, &probe->last_address);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_client_probe_outgoing_append_options(probe, request);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now);
|
|
if (r)
|
|
return r;
|
|
else
|
|
request = NULL; /* consumed */
|
|
|
|
probe->state = N_DHCP4_CLIENT_PROBE_STATE_REBOOTING;
|
|
probe->ns_reinit = ns_now + 2000000000ULL;
|
|
|
|
break;
|
|
|
|
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
|
|
default:
|
|
abort();
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int n_dhcp4_client_probe_transition_deferred(NDhcp4ClientProbe *probe, uint64_t ns_now) {
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL;
|
|
int r;
|
|
|
|
switch (probe->state) {
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT:
|
|
r = n_dhcp4_c_connection_listen(&probe->connection);
|
|
if (r)
|
|
return r;
|
|
/* fall-through */
|
|
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
|
|
r = n_dhcp4_c_connection_discover_new(&probe->connection, &request);
|
|
if (r)
|
|
return r;
|
|
|
|
if (probe->last_address.s_addr != INADDR_ANY) {
|
|
r = n_dhcp4_outgoing_append_requested_ip(request, probe->last_address);
|
|
if (r)
|
|
return r;
|
|
}
|
|
|
|
r = n_dhcp4_client_probe_outgoing_append_options(probe, request);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now);
|
|
if (r)
|
|
return r;
|
|
else
|
|
request = NULL; /* consumed */
|
|
|
|
probe->state = N_DHCP4_CLIENT_PROBE_STATE_SELECTING;
|
|
probe->ns_deferred = 0;
|
|
|
|
break;
|
|
|
|
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
|
|
default:
|
|
abort();
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int n_dhcp4_client_probe_transition_t1(NDhcp4ClientProbe *probe, uint64_t ns_now) {
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL;
|
|
int r;
|
|
|
|
switch (probe->state) {
|
|
case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
|
|
r = n_dhcp4_c_connection_renew_new(&probe->connection, &request);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_client_probe_outgoing_append_options(probe, request);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now);
|
|
if (r)
|
|
return r;
|
|
else
|
|
request = NULL; /* consumed */
|
|
|
|
probe->state = N_DHCP4_CLIENT_PROBE_STATE_RENEWING;
|
|
|
|
break;
|
|
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
|
|
default:
|
|
abort();
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int n_dhcp4_client_probe_transition_t2(NDhcp4ClientProbe *probe, uint64_t ns_now) {
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL;
|
|
int r;
|
|
|
|
switch (probe->state) {
|
|
case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
|
|
r = n_dhcp4_c_connection_listen(&probe->connection);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_c_connection_rebind_new(&probe->connection, &request);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_client_probe_outgoing_append_options(probe, request);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now);
|
|
if (r)
|
|
return r;
|
|
else
|
|
request = NULL; /* consumed */
|
|
|
|
probe->state = N_DHCP4_CLIENT_PROBE_STATE_REBINDING;
|
|
|
|
break;
|
|
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
|
|
default:
|
|
abort();
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int n_dhcp4_client_probe_transition_lifetime(NDhcp4ClientProbe *probe) {
|
|
int r;
|
|
|
|
switch (probe->state) {
|
|
case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
|
|
|
|
/* XXX */
|
|
|
|
r = n_dhcp4_client_probe_raise(probe,
|
|
NULL,
|
|
N_DHCP4_CLIENT_EVENT_EXPIRED);
|
|
if (r)
|
|
return r;
|
|
|
|
c_assert(probe->client->current_probe == probe);
|
|
|
|
probe->current_lease = n_dhcp4_client_lease_unref(probe->current_lease);
|
|
|
|
probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT;
|
|
probe->ns_deferred = n_dhcp4_gettime(CLOCK_BOOTTIME) + UINT64_C(1);
|
|
|
|
break;
|
|
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
|
|
default:
|
|
abort();
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp4Incoming *message_take) {
|
|
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = message_take;
|
|
_c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL;
|
|
NDhcp4CEventNode *node;
|
|
int r;
|
|
|
|
switch (probe->state) {
|
|
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
|
|
|
|
r = n_dhcp4_client_probe_raise(probe,
|
|
&node,
|
|
N_DHCP4_CLIENT_EVENT_OFFER);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_client_lease_new(&lease, message);
|
|
if (r)
|
|
return r;
|
|
|
|
message = NULL; /* consumed */
|
|
|
|
n_dhcp4_client_lease_link(lease, probe);
|
|
|
|
node->event.offer.lease = n_dhcp4_client_lease_ref(lease);
|
|
|
|
break;
|
|
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
|
|
default:
|
|
/* ignore */
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4Incoming *message_take) {
|
|
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = message_take;
|
|
_c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL;
|
|
NDhcp4CEventNode *node;
|
|
struct in_addr client = {};
|
|
struct in_addr server = {};
|
|
int r;
|
|
|
|
switch (probe->state) {
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
|
|
n_dhcp4_incoming_get_yiaddr(message, &client);
|
|
|
|
r = n_dhcp4_incoming_query_server_identifier(message, &server);
|
|
if (r)
|
|
return r;
|
|
r = n_dhcp4_c_connection_connect(&probe->connection, &client, &server);
|
|
if (r)
|
|
return r;
|
|
/* fall-through */
|
|
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
|
|
|
|
r = n_dhcp4_client_probe_raise(probe,
|
|
&node,
|
|
N_DHCP4_CLIENT_EVENT_EXTENDED);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_client_lease_new(&lease, message);
|
|
if (r)
|
|
return r;
|
|
|
|
message = NULL; /* consumed */
|
|
|
|
n_dhcp4_client_lease_link(lease, probe);
|
|
|
|
node->event.extended.lease = n_dhcp4_client_lease_ref(lease);
|
|
n_dhcp4_client_lease_unref(probe->current_lease);
|
|
probe->current_lease = n_dhcp4_client_lease_ref(lease);
|
|
probe->state = N_DHCP4_CLIENT_PROBE_STATE_BOUND;
|
|
n_dhcp4_client_lease_get_yiaddr(lease, &probe->last_address);
|
|
probe->ns_nak_restart_delay = 0;
|
|
break;
|
|
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
|
|
|
|
r = n_dhcp4_client_probe_raise(probe,
|
|
&node,
|
|
N_DHCP4_CLIENT_EVENT_GRANTED);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_client_lease_new(&lease, message);
|
|
if (r)
|
|
return r;
|
|
|
|
message = NULL; /* consumed */
|
|
|
|
n_dhcp4_client_lease_link(lease, probe);
|
|
|
|
node->event.granted.lease = n_dhcp4_client_lease_ref(lease);
|
|
probe->current_lease = n_dhcp4_client_lease_ref(lease);
|
|
probe->state = N_DHCP4_CLIENT_PROBE_STATE_GRANTED;
|
|
probe->ns_nak_restart_delay = 0;
|
|
break;
|
|
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
|
|
default:
|
|
/* ignore */
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int n_dhcp4_client_probe_transition_nak(NDhcp4ClientProbe *probe) {
|
|
int r;
|
|
|
|
switch (probe->state) {
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
|
|
|
|
/* XXX */
|
|
|
|
r = n_dhcp4_client_probe_raise(probe,
|
|
NULL,
|
|
N_DHCP4_CLIENT_EVENT_RETRACTED);
|
|
if (r)
|
|
return r;
|
|
|
|
probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT;
|
|
probe->ns_deferred = n_dhcp4_gettime(CLOCK_BOOTTIME) + probe->ns_nak_restart_delay;
|
|
probe->ns_nak_restart_delay = C_CLAMP(probe->ns_nak_restart_delay * 2u,
|
|
UINT64_C(2) * UINT64_C(1000000000),
|
|
UINT64_C(300) * UINT64_C(1000000000));
|
|
break;
|
|
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
|
|
default:
|
|
/* ignore */
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int n_dhcp4_client_probe_transition_select(NDhcp4ClientProbe *probe, NDhcp4Incoming *offer, uint64_t ns_now) {
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL;
|
|
int r;
|
|
|
|
switch (probe->state) {
|
|
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
|
|
r = n_dhcp4_c_connection_select_new(&probe->connection, &request, offer);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_client_probe_outgoing_append_options(probe, request);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now);
|
|
if (r)
|
|
return r;
|
|
else
|
|
request = NULL; /* consumed */
|
|
|
|
/* XXX: ignore other offers */
|
|
|
|
probe->state = N_DHCP4_CLIENT_PROBE_STATE_REQUESTING;
|
|
|
|
break;
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
|
|
default:
|
|
/* ignore */
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_transition_accept() - XXX
|
|
*/
|
|
int n_dhcp4_client_probe_transition_accept(NDhcp4ClientProbe *probe, NDhcp4Incoming *ack) {
|
|
struct in_addr client = {};
|
|
struct in_addr server = {};
|
|
int r;
|
|
|
|
switch (probe->state) {
|
|
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
|
|
n_dhcp4_incoming_get_yiaddr(ack, &client);
|
|
|
|
r = n_dhcp4_incoming_query_server_identifier(ack, &server);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_c_connection_connect(&probe->connection, &client, &server);
|
|
if (r)
|
|
return r;
|
|
|
|
probe->state = N_DHCP4_CLIENT_PROBE_STATE_BOUND;
|
|
|
|
n_dhcp4_client_arm_timer(probe->client);
|
|
|
|
break;
|
|
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
|
|
default:
|
|
/* ignore */
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* n_dhc4_client_probe_transition_decline() - XXX
|
|
*/
|
|
int n_dhcp4_client_probe_transition_decline(NDhcp4ClientProbe *probe, NDhcp4Incoming *offer, const char *error, uint64_t ns_now) {
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL;
|
|
int r;
|
|
|
|
switch (probe->state) {
|
|
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
|
|
r = n_dhcp4_c_connection_decline_new(&probe->connection, &request, offer, error);
|
|
if (r)
|
|
return r;
|
|
|
|
r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now);
|
|
if (r)
|
|
return r;
|
|
else
|
|
request = NULL; /* consumed */
|
|
|
|
/* XXX: what state to transition to? */
|
|
|
|
break;
|
|
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
|
|
default:
|
|
/* ignore */
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_dispatch_timer() - XXX
|
|
*/
|
|
int n_dhcp4_client_probe_dispatch_timer(NDhcp4ClientProbe *probe, uint64_t ns_now) {
|
|
int r;
|
|
|
|
switch (probe->state) {
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
|
|
r = n_dhcp4_client_probe_transition_reboot(probe, ns_now);
|
|
if (r)
|
|
return r;
|
|
break;
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
|
|
if (ns_now >= probe->ns_reinit) {
|
|
r = n_dhcp4_client_probe_transition_deferred(probe, ns_now);
|
|
if (r)
|
|
return r;
|
|
}
|
|
|
|
break;
|
|
case N_DHCP4_CLIENT_PROBE_STATE_INIT:
|
|
if (ns_now >= probe->ns_deferred) {
|
|
r = n_dhcp4_client_probe_transition_deferred(probe, ns_now);
|
|
if (r)
|
|
return r;
|
|
}
|
|
|
|
break;
|
|
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
|
|
if (ns_now >= probe->current_lease->lifetime) {
|
|
r = n_dhcp4_client_probe_transition_lifetime(probe);
|
|
if (r)
|
|
return r;
|
|
}
|
|
|
|
break;
|
|
case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
|
|
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
|
|
if (ns_now >= probe->current_lease->lifetime) {
|
|
r = n_dhcp4_client_probe_transition_lifetime(probe);
|
|
if (r)
|
|
return r;
|
|
} else if (ns_now >= probe->current_lease->t2 &&
|
|
probe->state != N_DHCP4_CLIENT_PROBE_STATE_REBINDING) {
|
|
r = n_dhcp4_client_probe_transition_t2(probe, ns_now);
|
|
if (r)
|
|
return r;
|
|
} else if (ns_now >= probe->current_lease->t1 &&
|
|
probe->state == N_DHCP4_CLIENT_PROBE_STATE_BOUND) {
|
|
r = n_dhcp4_client_probe_transition_t1(probe, ns_now);
|
|
if (r)
|
|
return r;
|
|
}
|
|
break;
|
|
default:
|
|
/* ignore */
|
|
break;
|
|
}
|
|
|
|
r = n_dhcp4_c_connection_dispatch_timer(&probe->connection, ns_now);
|
|
if (r)
|
|
return r;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_dispatch_connection() - XXX
|
|
*/
|
|
int n_dhcp4_client_probe_dispatch_io(NDhcp4ClientProbe *probe, uint32_t events) {
|
|
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL;
|
|
uint8_t type;
|
|
int r;
|
|
|
|
r = n_dhcp4_c_connection_dispatch_io(&probe->connection, &message);
|
|
if (r) {
|
|
if (r == N_DHCP4_E_AGAIN)
|
|
return 0;
|
|
else if (r == N_DHCP4_E_MALFORMED || r == N_DHCP4_E_UNEXPECTED) {
|
|
/*
|
|
* We fetched something from the sockets, which we
|
|
* discarded. We don't know whether there is more data
|
|
* to fetch, so we set the preempted flag to notify the
|
|
* caller we want to be called again.
|
|
*/
|
|
probe->client->preempted = true;
|
|
return 0;
|
|
}
|
|
|
|
abort();
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* We fetched something from the sockets, which we will handle below.
|
|
* We don't know whether there is more data to fetch, so we set the
|
|
* preempted flag to notify the caller we want to be called again.
|
|
*/
|
|
probe->client->preempted = true;
|
|
|
|
r = n_dhcp4_incoming_query_message_type(message, &type);
|
|
if (r == N_DHCP4_E_UNSET || r == N_DHCP4_E_MALFORMED)
|
|
/*
|
|
* XXX: this can never happen as we already queried the message
|
|
* type.
|
|
*/
|
|
return 0;
|
|
|
|
switch (type) {
|
|
case N_DHCP4_MESSAGE_OFFER:
|
|
r = n_dhcp4_client_probe_transition_offer(probe, message);
|
|
message = NULL; /* consumed */
|
|
if (r)
|
|
return r;
|
|
break;
|
|
case N_DHCP4_MESSAGE_ACK:
|
|
r = n_dhcp4_client_probe_transition_ack(probe, message);
|
|
message = NULL; /* consumed */
|
|
if (r)
|
|
return r;
|
|
break;
|
|
case N_DHCP4_MESSAGE_NAK:
|
|
r = n_dhcp4_client_probe_transition_nak(probe);
|
|
if (r)
|
|
return r;
|
|
break;
|
|
default:
|
|
/*
|
|
* We receiveda message type we do not support, simply discard
|
|
* it.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* n_dhcp4_client_probe_update_mtu() - XXX
|
|
*/
|
|
int n_dhcp4_client_probe_update_mtu(NDhcp4ClientProbe *probe, uint16_t mtu) {
|
|
return 0;
|
|
}
|