chiark / gitweb /
sd-dhcp-client: accept infinite lease lifetime
[elogind.git] / src / libsystemd-network / sd-dhcp-client.c
index 5374db74cb9b00e38c360f40a9ca2e2ca5f7371d..ce375dd0164b745856656a8343e1cea5287500e7 100644 (file)
@@ -178,15 +178,13 @@ static int client_notify(sd_dhcp_client *client, int event) {
         return 0;
 }
 
-static int client_stop(sd_dhcp_client *client, int error) {
+static int client_initialize(sd_dhcp_client *client) {
         assert_return(client, -EINVAL);
 
         client->receive_message =
                 sd_event_source_unref(client->receive_message);
 
-        if (client->fd >= 0)
-                close_nointr_nofail(client->fd);
-        client->fd = -1;
+        client->fd = safe_close(client->fd);
 
         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
 
@@ -196,15 +194,24 @@ static int client_stop(sd_dhcp_client *client, int error) {
 
         client->attempt = 1;
 
-        client_notify(client, error);
-
         client->start_time = 0;
         client->secs = 0;
         client->state = DHCP_STATE_INIT;
+        client->xid = 0;
 
         if (client->lease)
                 client->lease = sd_dhcp_lease_unref(client->lease);
 
+        return 0;
+}
+
+static int client_stop(sd_dhcp_client *client, int error) {
+        assert_return(client, -EINVAL);
+
+        client_notify(client, error);
+
+        client_initialize(client);
+
         log_dhcp_client(client, "STOPPED");
 
         return 0;
@@ -331,7 +338,17 @@ static int client_send_request(sd_dhcp_client *client, uint16_t secs) {
         if (err < 0)
                 return err;
 
-        if (client->state == DHCP_STATE_REQUESTING) {
+        switch (client->state) {
+
+        case DHCP_STATE_INIT_REBOOT:
+                err = dhcp_option_append(&opt, &optlen,
+                                         DHCP_OPTION_REQUESTED_IP_ADDRESS,
+                                         4, &client->last_addr);
+                if (err < 0)
+                        return err;
+                break;
+
+        case DHCP_STATE_REQUESTING:
                 err = dhcp_option_append(&opt, &optlen,
                                          DHCP_OPTION_REQUESTED_IP_ADDRESS,
                                          4, &client->lease->address);
@@ -343,6 +360,16 @@ static int client_send_request(sd_dhcp_client *client, uint16_t secs) {
                                          4, &client->lease->server_address);
                 if (err < 0)
                         return err;
+                break;
+
+        case DHCP_STATE_INIT:
+        case DHCP_STATE_SELECTING:
+        case DHCP_STATE_REBOOTING:
+        case DHCP_STATE_BOUND:
+        case DHCP_STATE_RENEWING:
+        case DHCP_STATE_REBINDING:
+
+                break;
         }
 
         err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
@@ -409,9 +436,15 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
                 next_timeout = time_now + time_left * USEC_PER_SEC;
                 break;
 
+        case DHCP_STATE_REBOOTING:
+                /* start over as we did not receive a timely ack or nak */
+                client->state = DHCP_STATE_INIT;
+                client->attempt = 1;
+                client->xid = random_u32();
+
+                /* fall through */
         case DHCP_STATE_INIT:
         case DHCP_STATE_INIT_REBOOT:
-        case DHCP_STATE_REBOOTING:
         case DHCP_STATE_SELECTING:
         case DHCP_STATE_REQUESTING:
         case DHCP_STATE_BOUND:
@@ -466,6 +499,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
 
                 break;
 
+        case DHCP_STATE_INIT_REBOOT:
         case DHCP_STATE_REQUESTING:
         case DHCP_STATE_RENEWING:
         case DHCP_STATE_REBINDING:
@@ -473,11 +507,13 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec,
                 if (r < 0 && client->attempt >= 64)
                          goto error;
 
+                if (client->state == DHCP_STATE_INIT_REBOOT)
+                        client->state = DHCP_STATE_REBOOTING;
+
                 client->request_sent = time_now;
 
                 break;
 
-        case DHCP_STATE_INIT_REBOOT:
         case DHCP_STATE_REBOOTING:
         case DHCP_STATE_BOUND:
 
@@ -531,13 +567,46 @@ error:
 
 }
 
+static int client_start(sd_dhcp_client *client) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->event, -EINVAL);
+        assert_return(client->index > 0, -EINVAL);
+        assert_return(client->fd < 0, -EBUSY);
+        assert_return(client->xid == 0, -EINVAL);
+        assert_return(client->state == DHCP_STATE_INIT ||
+                      client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
+
+        client->xid = random_u32();
+
+        r = dhcp_network_bind_raw_socket(client->index, &client->link);
+
+        if (r < 0) {
+                client_stop(client, r);
+                return r;
+        }
+
+        client->fd = r;
+        client->start_time = now(CLOCK_MONOTONIC);
+        client->secs = 0;
+
+        log_dhcp_client(client, "STARTED");
+
+        return client_initialize_events(client, client_receive_message_raw);
+}
+
 static int client_timeout_expire(sd_event_source *s, uint64_t usec,
                                  void *userdata) {
         sd_dhcp_client *client = userdata;
 
         log_dhcp_client(client, "EXPIRED");
 
-        client_stop(client, DHCP_EVENT_EXPIRED);
+        client_notify(client, DHCP_EVENT_EXPIRED);
+
+        /* start over as the lease was lost */
+        client_initialize(client);
+        client_start(client);
 
         return 0;
 }
@@ -546,12 +615,8 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
         sd_dhcp_client *client = userdata;
         int r;
 
-        if (client->fd >= 0) {
-                client->receive_message =
-                        sd_event_source_unref(client->receive_message);
-                close_nointr_nofail(client->fd);
-                client->fd = -1;
-        }
+        client->receive_message = sd_event_source_unref(client->receive_message);
+        client->fd = safe_close(client->fd);
 
         client->state = DHCP_STATE_REBINDING;
         client->attempt = 1;
@@ -682,6 +747,10 @@ static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) {
         assert(client);
         assert(client->event);
 
+        /* don't set timers for infinite leases */
+        if (client->lease->lifetime == 0xffffffff)
+                return 0;
+
         if (client->lease->lifetime < 10)
                 return -EINVAL;
 
@@ -817,20 +886,37 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
 
                 break;
 
+        case DHCP_STATE_REBOOTING:
         case DHCP_STATE_REQUESTING:
         case DHCP_STATE_RENEWING:
         case DHCP_STATE_REBINDING:
 
                 r = client_handle_ack(client, message, len);
 
-                if (r == DHCP_EVENT_NO_LEASE)
+                if (r == DHCP_EVENT_NO_LEASE) {
+
+                        client->timeout_resend =
+                                sd_event_source_unref(client->timeout_resend);
+
+                        if (client->state == DHCP_STATE_REBOOTING) {
+                                r = client_initialize(client);
+                                if (r < 0)
+                                        goto error;
+
+                                r = client_start(client);
+                                if (r < 0)
+                                        goto error;
+                        }
+
                         goto error;
+                }
 
                 if (r >= 0) {
                         client->timeout_resend =
                                 sd_event_source_unref(client->timeout_resend);
 
-                        if (client->state == DHCP_STATE_REQUESTING)
+                        if (IN_SET(client->state, DHCP_STATE_REQUESTING,
+                                   DHCP_STATE_REBOOTING))
                                 notify_event = DHCP_EVENT_IP_ACQUIRE;
                         else if (r != DHCP_EVENT_IP_ACQUIRE)
                                 notify_event = r;
@@ -849,8 +935,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
 
                         client->receive_message =
                                 sd_event_source_unref(client->receive_message);
-                        close_nointr_nofail(client->fd);
-                        client->fd = -1;
+                        client->fd = safe_close(client->fd);
                 }
 
                 r = 0;
@@ -859,7 +944,6 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
 
         case DHCP_STATE_INIT:
         case DHCP_STATE_INIT_REBOOT:
-        case DHCP_STATE_REBOOTING:
         case DHCP_STATE_BOUND:
 
                 break;
@@ -968,27 +1052,15 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
         int r;
 
         assert_return(client, -EINVAL);
-        assert_return(client->event, -EINVAL);
-        assert_return(client->index > 0, -EINVAL);
-        assert_return(client->state == DHCP_STATE_INIT ||
-                      client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
-
-        client->xid = random_u32();
 
-        r = dhcp_network_bind_raw_socket(client->index, &client->link);
-
-        if (r < 0) {
-                client_stop(client, r);
+        r = client_initialize(client);
+        if (r < 0)
                 return r;
-        }
 
-        client->fd = r;
-        client->start_time = now(CLOCK_MONOTONIC);
-        client->secs = 0;
+        if (client->last_addr)
+                client->state = DHCP_STATE_INIT_REBOOT;
 
-        log_dhcp_client(client, "STARTED");
-
-        return client_initialize_events(client, client_receive_message_raw);
+        return client_start(client);
 }
 
 int sd_dhcp_client_stop(sd_dhcp_client *client) {