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. --- src/libsystemd-network/dhcp6-protocol.h | 5 ++ src/libsystemd-network/sd-dhcp6-client.c | 136 +++++++++++++++++++++++++++++++ src/systemd/sd-dhcp6-client.h | 2 + 3 files changed, 143 insertions(+) diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index afe1413..442418d 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 f61442b..048b4ea 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -48,6 +48,10 @@ struct sd_dhcp6_client { struct ether_addr mac_addr; icmp6_nd *ra; 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; @@ -111,6 +115,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; @@ -126,6 +136,119 @@ 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; @@ -182,6 +305,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 c64ad16..3aa1af9 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -29,6 +29,8 @@ enum { DHCP6_EVENT_STOP = 0, DHCP6_EVENT_NO_STATEFUL_CONFIGURATION = 10, + DHCP6_EVENT_RESEND_EXPIRE = 11, + DHCP6_EVENT_RETRANS_MAX = 12, }; typedef struct sd_dhcp6_client sd_dhcp6_client; -- 1.9.1 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel