diff options
author | Patrik Flykt <patrik.flykt@linux.intel.com> | 2014-06-19 15:39:15 +0300 |
---|---|---|
committer | Patrik Flykt <patrik.flykt@linux.intel.com> | 2014-06-19 15:44:43 +0300 |
commit | d1b0afe3653b4316a6361d204169620726d468a0 (patch) | |
tree | 5e4823daed463e3e09d61329933227cf42e1ead8 | |
parent | 813e3a6ffcd094696001716480bbd68008cc54c8 (diff) |
sd-dhcp6-client: Add DHCPv6 client Solicitation timeout handling
Add the core of DHCPv6 client message retransmission and upper bound
timer and message count handling according to RFC 3315 Secions 7.1.2
and 14. Omit the DHCPv6 initial delay; for now it is assumed that
systemd-networkd will provide decent startup randomization that will
desynchronize the clients.
When reinitializing the client, clear all timers.
-rw-r--r-- | src/libsystemd-network/dhcp6-protocol.h | 5 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp6-client.c | 135 | ||||
-rw-r--r-- | src/systemd/sd-dhcp6-client.h | 2 |
3 files changed, 142 insertions, 0 deletions
diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index 0f408bce70..6ca72ec151 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -41,6 +41,10 @@ enum { DHCP6_PORT_CLIENT = 546, }; +#define DHCP6_SOL_MAX_DELAY 1 * USEC_PER_SEC +#define DHCP6_SOL_TIMEOUT 1 * USEC_PER_SEC +#define DHCP6_SOL_MAX_RT 120 * USEC_PER_SEC + enum { DHCP6_DUID_LLT = 1, DHCP6_DUID_EN = 2, @@ -51,6 +55,7 @@ enum { enum DHCP6State { DHCP6_STATE_STOPPED = 0, DHCP6_STATE_RS = 1, + DHCP6_STATE_SOLICITATION = 2, }; enum { diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 15d174beee..d98ae0294a 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -46,6 +46,10 @@ struct sd_dhcp6_client { int index; struct ether_addr mac_addr; DHCP6IA ia_na; + usec_t retransmit_time; + uint8_t retransmit_count; + sd_event_source *timeout_resend; + sd_event_source *timeout_resend_expire; sd_dhcp6_client_cb_t cb; void *userdata; @@ -109,6 +113,12 @@ static int client_initialize(sd_dhcp6_client *client) client->ia_na.timeout_t2 = sd_event_source_unref(client->ia_na.timeout_t2); + client->retransmit_time = 0; + client->retransmit_count = 0; + client->timeout_resend = sd_event_source_unref(client->timeout_resend); + client->timeout_resend_expire = + sd_event_source_unref(client->timeout_resend_expire); + client->state = DHCP6_STATE_STOPPED; return 0; @@ -124,6 +134,118 @@ static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) { return client; } +static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, + void *userdata) { + sd_dhcp6_client *client = userdata; + + assert(s); + assert(client); + assert(client->event); + + client_stop(client, DHCP6_EVENT_RESEND_EXPIRE); + + return 0; +} + +static usec_t client_timeout_compute_random(usec_t val) { + return val - val / 10 + + (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC; +} + +static int client_timeout_resend(sd_event_source *s, uint64_t usec, + void *userdata) { + int r = 0; + sd_dhcp6_client *client = userdata; + usec_t time_now, init_retransmit_time, max_retransmit_time; + usec_t max_retransmit_duration; + uint8_t max_retransmit_count; + char time_string[FORMAT_TIMESPAN_MAX]; + + assert(s); + assert(client); + assert(client->event); + + client->timeout_resend = sd_event_source_unref(client->timeout_resend); + + switch (client->state) { + case DHCP6_STATE_SOLICITATION: + init_retransmit_time = DHCP6_SOL_TIMEOUT; + max_retransmit_time = DHCP6_SOL_MAX_RT; + max_retransmit_count = 0; + max_retransmit_duration = 0; + + break; + + case DHCP6_STATE_STOPPED: + case DHCP6_STATE_RS: + return 0; + } + + if (max_retransmit_count && + client->retransmit_count >= max_retransmit_count) { + client_stop(client, DHCP6_EVENT_RETRANS_MAX); + return 0; + } + + r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now); + if (r < 0) + goto error; + + if (!client->retransmit_time) { + client->retransmit_time = + client_timeout_compute_random(init_retransmit_time); + } else { + if (max_retransmit_time && + client->retransmit_time > max_retransmit_time / 2) + client->retransmit_time = client_timeout_compute_random(max_retransmit_time); + else + client->retransmit_time += client_timeout_compute_random(client->retransmit_time); + } + + log_dhcp6_client(client, "Next retransmission in %s", + format_timespan(time_string, FORMAT_TIMESPAN_MAX, + client->retransmit_time, 0)); + + r = sd_event_add_time(client->event, &client->timeout_resend, + CLOCK_MONOTONIC, + time_now + client->retransmit_time, + 10 * USEC_PER_MSEC, client_timeout_resend, + client); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(client->timeout_resend, + client->event_priority); + if (r < 0) + goto error; + + if (max_retransmit_duration && !client->timeout_resend_expire) { + + log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs", + max_retransmit_duration / USEC_PER_SEC); + + r = sd_event_add_time(client->event, + &client->timeout_resend_expire, + CLOCK_MONOTONIC, + time_now + max_retransmit_duration, + USEC_PER_SEC, + client_timeout_resend_expire, client); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(client->timeout_resend_expire, + client->event_priority); + if (r < 0) + goto error; + } + +error: + if (r < 0) + client_stop(client, r); + + return 0; +} + static int client_ensure_iaid(sd_dhcp6_client *client) { const char *name = NULL; uint64_t id; @@ -180,6 +302,19 @@ static int client_start(sd_dhcp6_client *client) if (r < 0) return r; + client->state = DHCP6_STATE_SOLICITATION; + + r = sd_event_add_time(client->event, &client->timeout_resend, + CLOCK_MONOTONIC, 0, 0, client_timeout_resend, + client); + if (r < 0) + return r; + + r = sd_event_source_set_priority(client->timeout_resend, + client->event_priority); + if (r < 0) + return r; + return 0; } diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index 496501147a..72267f908c 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -28,6 +28,8 @@ enum { DHCP6_EVENT_STOP = 0, + DHCP6_EVENT_RESEND_EXPIRE = 10, + DHCP6_EVENT_RETRANS_MAX = 11, }; typedef struct sd_dhcp6_client sd_dhcp6_client; |