]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/libsystemd-network/sd-dhcp6-lease.c
Merge pull request #28132 from rpigott/dhcp-captive-portal
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp6-lease.c
index 90c3373bd50f0e9cf9f6e185c5687d89e7bb9c81..6dcc25888da34c23a7b15e26f02ee84a73be90cf 100644 (file)
@@ -6,10 +6,9 @@
 #include <errno.h>
 
 #include "alloc-util.h"
+#include "dhcp6-internal.h"
 #include "dhcp6-lease-internal.h"
-#include "dhcp6-protocol.h"
 #include "strv.h"
-#include "util.h"
 
 #define IRT_DEFAULT (1 * USEC_PER_DAY)
 #define IRT_MINIMUM (600 * USEC_PER_SEC)
@@ -42,7 +41,6 @@ static usec_t sec2usec(uint32_t sec) {
 
 static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
         uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX;
-        DHCP6Address *a;
 
         assert(lease);
         assert(lease->ia_na || lease->ia_pd);
@@ -108,11 +106,9 @@ int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *re
 }
 
 void dhcp6_ia_clear_addresses(DHCP6IA *ia) {
-        DHCP6Address *a, *n;
-
         assert(ia);
 
-        LIST_FOREACH_SAFE(addresses, a, n, ia->addresses)
+        LIST_FOREACH(addresses, a, ia->addresses)
                 free(a);
 
         ia->addresses = NULL;
@@ -342,11 +338,11 @@ int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt
                 if (r < 0)
                         return r;
 
-                switch(subopt) {
+                switch (subopt) {
                 case DHCP6_NTP_SUBOPTION_SRV_ADDR:
                 case DHCP6_NTP_SUBOPTION_MC_ADDR:
                         if (sublen != 16)
-                                return 0;
+                                return -EINVAL;
 
                         r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
                         if (r < 0)
@@ -449,6 +445,34 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
         return 0;
 }
 
+int dhcp6_lease_set_captive_portal(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
+        _cleanup_free_ char *uri = NULL;
+        int r;
+
+        assert(lease);
+        assert(optval || optlen == 0);
+
+        r = dhcp6_option_parse_string(optval, optlen, &uri);
+        if (r < 0)
+                return r;
+
+        if (uri && !in_charset(uri, URI_VALID))
+                return -EINVAL;
+
+        return free_and_replace(lease->captive_portal, uri);
+}
+
+int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret) {
+        assert_return(lease, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (!lease->captive_portal)
+                return -ENODATA;
+
+        *ret = lease->captive_portal;
+        return 0;
+}
+
 static int dhcp6_lease_parse_message(
                 sd_dhcp6_client *client,
                 sd_dhcp6_lease *lease,
@@ -469,9 +493,16 @@ static int dhcp6_lease_parse_message(
                 size_t optlen;
                 const uint8_t *optval;
 
+                if (len - offset < offsetof(DHCP6Option, data)) {
+                        log_dhcp6_client(client, "Ignoring %zu invalid byte(s) at the end of the packet", len - offset);
+                        break;
+                }
+
                 r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
                 if (r < 0)
-                        return r;
+                        return log_dhcp6_client_errno(client, r,
+                                                      "Failed to parse option header at offset %zu of total length %zu: %m",
+                                                      offset, len);
 
                 switch (optcode) {
                 case SD_DHCP6_OPTION_CLIENTID:
@@ -481,7 +512,7 @@ static int dhcp6_lease_parse_message(
 
                         r = dhcp6_lease_set_clientid(lease, optval, optlen);
                         if (r < 0)
-                                return r;
+                                return log_dhcp6_client_errno(client, r, "Failed to set client ID: %m");
 
                         break;
 
@@ -492,17 +523,17 @@ static int dhcp6_lease_parse_message(
 
                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
                         if (r < 0)
-                                return r;
+                                return log_dhcp6_client_errno(client, r, "Failed to set server ID: %m");
 
                         break;
 
                 case SD_DHCP6_OPTION_PREFERENCE:
                         if (optlen != 1)
-                                return -EINVAL;
+                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received invalid length for preference.");
 
                         r = dhcp6_lease_set_preference(lease, optval[0]);
                         if (r < 0)
-                                return r;
+                                return log_dhcp6_client_errno(client, r, "Failed to set preference: %m");
 
                         break;
 
@@ -511,14 +542,12 @@ static int dhcp6_lease_parse_message(
 
                         r = dhcp6_option_parse_status(optval, optlen, &msg);
                         if (r < 0)
-                                return r;
-
+                                return log_dhcp6_client_errno(client, r, "Failed to parse status code: %m");
                         if (r > 0)
-                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
-                                                              "Received %s message with non-zero status: %s%s%s",
+                                return log_dhcp6_client_errno(client, dhcp6_message_status_to_errno(r),
+                                                              "Received %s message with non-zero status%s%s",
                                                               dhcp6_message_type_to_string(message->type),
-                                                              strempty(msg), isempty(msg) ? "" : ": ",
-                                                              dhcp6_message_status_to_string(r));
+                                                              isempty(msg) ? "." : ": ", strempty(msg));
                         break;
                 }
                 case SD_DHCP6_OPTION_IA_NA: {
@@ -531,9 +560,11 @@ static int dhcp6_lease_parse_message(
 
                         r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia);
                         if (r == -ENOMEM)
-                                return r;
-                        if (r < 0)
+                                return log_oom_debug();
+                        if (r < 0) {
+                                log_dhcp6_client_errno(client, r, "Failed to parse IA_NA option, ignoring: %m");
                                 continue;
+                        }
 
                         if (lease->ia_na) {
                                 log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
@@ -554,9 +585,11 @@ static int dhcp6_lease_parse_message(
 
                         r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia);
                         if (r == -ENOMEM)
-                                return r;
-                        if (r < 0)
+                                return log_oom_debug();
+                        if (r < 0) {
+                                log_dhcp6_client_errno(client, r, "Failed to parse IA_PD option, ignoring: %m");
                                 continue;
+                        }
 
                         if (lease->ia_pd) {
                                 log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
@@ -568,20 +601,23 @@ static int dhcp6_lease_parse_message(
                         break;
                 }
                 case SD_DHCP6_OPTION_RAPID_COMMIT:
+                        if (optlen != 0)
+                                log_dhcp6_client(client, "Received rapid commit option with an invalid length (%zu), ignoring.", optlen);
+
                         r = dhcp6_lease_set_rapid_commit(lease);
                         if (r < 0)
-                                return r;
+                                return log_dhcp6_client_errno(client, r, "Failed to set rapid commit flag: %m");
 
                         break;
 
-                case SD_DHCP6_OPTION_DNS_SERVERS:
+                case SD_DHCP6_OPTION_DNS_SERVER:
                         r = dhcp6_lease_add_dns(lease, optval, optlen);
                         if (r < 0)
                                 log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m");
 
                         break;
 
-                case SD_DHCP6_OPTION_DOMAIN_LIST:
+                case SD_DHCP6_OPTION_DOMAIN:
                         r = dhcp6_lease_add_domains(lease, optval, optlen);
                         if (r < 0)
                                 log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m");
@@ -595,13 +631,19 @@ static int dhcp6_lease_parse_message(
 
                         break;
 
-                case SD_DHCP6_OPTION_SNTP_SERVERS:
+                case SD_DHCP6_OPTION_SNTP_SERVER:
                         r = dhcp6_lease_add_sntp(lease, optval, optlen);
                         if (r < 0)
                                 log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m");
 
                         break;
 
+                case SD_DHCP6_OPTION_CAPTIVE_PORTAL:
+                        r = dhcp6_lease_set_captive_portal(lease, optval, optlen);
+                        if (r < 0)
+                                log_dhcp6_client_errno(client, r, "Failed to parse captive portal option, ignoring: %m");
+                        break;
+
                 case SD_DHCP6_OPTION_CLIENT_FQDN:
                         r = dhcp6_lease_set_fqdn(lease, optval, optlen);
                         if (r < 0)
@@ -611,9 +653,10 @@ static int dhcp6_lease_parse_message(
 
                 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
                         if (optlen != 4)
-                                return -EINVAL;
+                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
+                                                              "Received information refresh time option with an invalid length (%zu).", optlen);
 
-                        irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
+                        irt = unaligned_read_be32(optval) * USEC_PER_SEC;
                         break;
                 }
         }
@@ -621,28 +664,33 @@ static int dhcp6_lease_parse_message(
         uint8_t *clientid;
         size_t clientid_len;
         if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0)
-                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s message does not contain client ID. Ignoring.",
+                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
+                                              "%s message does not contain client ID. Ignoring.",
                                               dhcp6_message_type_to_string(message->type));
 
-        if (clientid_len != client->duid_len ||
-            memcmp(clientid, &client->duid, clientid_len) != 0)
-                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "The client ID in %s message does not match. Ignoring.",
+        if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0)
+                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
+                                              "The client ID in %s message does not match. Ignoring.",
                                               dhcp6_message_type_to_string(message->type));
 
-        if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
+        if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
+                client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
+                log_dhcp6_client(client, "New information request will be refused in %s.",
+                                 FORMAT_TIMESPAN(client->information_refresh_time_usec, USEC_PER_SEC));
+
+        } else {
                 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
                 if (r < 0)
                         return log_dhcp6_client_errno(client, r, "%s has no server id",
                                                       dhcp6_message_type_to_string(message->type));
 
                 if (!lease->ia_na && !lease->ia_pd)
-                        return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring.");
+                        return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
+                                                      "No IA_PD prefix or IA_NA address received. Ignoring.");
 
                 dhcp6_lease_set_lifetime(lease);
         }
 
-        client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
-
         return 0;
 }
 
@@ -656,6 +704,7 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
         dhcp6_ia_free(lease->ia_pd);
         free(lease->dns);
         free(lease->fqdn);
+        free(lease->captive_portal);
         strv_free(lease->domains);
         free(lease->ntp);
         strv_free(lease->ntp_fqdn);