[systemd-devel] [PATCH 09/24] sd-dhcp6-client: Add DHCPv6 client Solicitation timeout handling

Patrik Flykt patrik.flykt at linux.intel.com
Fri Jun 13 06:44:59 PDT 2014


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



More information about the systemd-devel mailing list