]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/libsystemd-network/sd-dhcp6-client.c
dhcp6: read OPTION_INFORMATION_REFRESH_TIME option
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp6-client.c
index 8ff4585f63c6f1dd1b0eb8dcda3ca7489fdc7709..7dab776b7295d2dc63faf0ed192fbd3e90980782 100644 (file)
@@ -6,6 +6,7 @@
 #include <errno.h>
 #include <string.h>
 #include <sys/ioctl.h>
+#include <linux/if_arp.h>
 #include <linux/if_infiniband.h>
 
 #include "sd-dhcp6-client.h"
@@ -28,6 +29,9 @@
 
 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
 
+#define IRT_DEFAULT 1 * USEC_PER_DAY
+#define IRT_MINIMUM 600 * USEC_PER_SEC
+
 /* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
 enum {
         DHCP6_REQUEST_IA_NA                     = 1,
@@ -70,6 +74,8 @@ struct sd_dhcp6_client {
         void *userdata;
         struct duid duid;
         size_t duid_len;
+        usec_t information_request_time_usec;
+        usec_t information_refresh_time_usec;
 };
 
 static const uint16_t default_req_opts[] = {
@@ -206,9 +212,13 @@ static int dhcp6_client_set_duid_internal(
         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
 
         if (duid != NULL) {
-                r = dhcp_validate_duid_len(duid_type, duid_len);
-                if (r < 0)
-                        return r;
+                r = dhcp_validate_duid_len(duid_type, duid_len, true);
+                if (r < 0) {
+                        r = dhcp_validate_duid_len(duid_type, duid_len, false);
+                        if (r < 0)
+                                return r;
+                        log_dhcp6_client(client, "Setting DUID of type %u with unexpected content", duid_type);
+                }
 
                 client->duid.type = htobe16(duid_type);
                 memcpy(&client->duid.raw.data, duid, duid_len);
@@ -815,6 +825,7 @@ static int client_parse_message(
         uint32_t lt_t1 = ~0, lt_t2 = ~0;
         bool clientid = false;
         size_t pos = 0;
+        usec_t irt = IRT_DEFAULT;
         int r;
 
         assert(client);
@@ -989,6 +1000,10 @@ static int client_parse_message(
                                 return r;
 
                         break;
+
+                case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
+                        irt = be32toh(*(be32_t *) optval) * USEC_PER_SEC;
+                        break;
                 }
 
                 pos += offsetof(DHCP6Option, data) + optlen;
@@ -1020,6 +1035,8 @@ static int client_parse_message(
                 }
         }
 
+        client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
+
         return 0;
 }
 
@@ -1108,6 +1125,12 @@ static int client_receive_message(
         assert(client->event);
 
         buflen = next_datagram_size_fd(fd);
+        if (buflen == -ENETDOWN) {
+                /* the link is down. Don't return an error or the I/O event
+                   source will be disconnected and we won't be able to receive
+                   packets again when the link comes back. */
+                return 0;
+        }
         if (buflen < 0)
                 return buflen;
 
@@ -1117,7 +1140,8 @@ static int client_receive_message(
 
         len = recv(fd, message, buflen, 0);
         if (len < 0) {
-                if (IN_SET(errno, EAGAIN, EINTR))
+                /* see comment above for why we shouldn't error out on ENETDOWN. */
+                if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
                         return 0;
 
                 return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
@@ -1413,8 +1437,15 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
                 client->fd = r;
         }
 
-        if (client->information_request)
+        if (client->information_request) {
+                usec_t t = now(CLOCK_MONOTONIC);
+
+                if (t < usec_add(client->information_request_time_usec, client->information_refresh_time_usec))
+                        return 0;
+
+                client->information_request_time_usec = t;
                 state = DHCP6_STATE_INFORMATION_REQUEST;
+        }
 
         log_dhcp6_client(client, "Started in %s mode",
                          client->information_request? "Information request":