summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrik Flykt <patrik.flykt@linux.intel.com>2014-06-19 15:39:15 +0300
committerPatrik Flykt <patrik.flykt@linux.intel.com>2014-06-19 15:44:43 +0300
commitd1b0afe3653b4316a6361d204169620726d468a0 (patch)
tree5e4823daed463e3e09d61329933227cf42e1ead8
parent813e3a6ffcd094696001716480bbd68008cc54c8 (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.h5
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c135
-rw-r--r--src/systemd/sd-dhcp6-client.h2
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;