]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-dhcp-server: support IPv6 only mode
authorSusant Sahani <ssahani@gmail.com>
Wed, 20 Sep 2023 00:50:02 +0000 (09:50 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 11 Oct 2023 12:42:13 +0000 (21:42 +0900)
This makes sd-dhcp-server send IPv6 only preferred option (RFC 8925).

Co-authored-by: Yu Watanabe <watanabe.yu+github@gmail.com>
src/libsystemd-network/dhcp-protocol.h
src/libsystemd-network/dhcp-server-internal.h
src/libsystemd-network/sd-dhcp-server.c
src/systemd/sd-dhcp-server.h

index dd330ae83938829a5600ac3447ccfe914f449913..4bf2296ee38686c1a6fd07ce597b4752395c879a 100644 (file)
 
 #include "macro.h"
 #include "sparse-endian.h"
+#include "time-util.h"
+
+/* RFC 8925 - IPv6-Only Preferred Option for DHCPv4 3.4.
+ * MIN_V6ONLY_WAIT: The lower boundary for V6ONLY_WAIT. Value: 300 seconds */
+#define MIN_V6ONLY_WAIT_USEC (300U * USEC_PER_SEC)
 
 struct DHCPMessage {
         uint8_t op;
index a2dae1cd4a8f1b722770a76a5eb7a3d78ac9883d..ae61cd84799ac6377d8d973ebaf6389e5acdd08f 100644 (file)
@@ -83,6 +83,7 @@ struct sd_dhcp_server {
 
         usec_t max_lease_time;
         usec_t default_lease_time;
+        usec_t ipv6_only_preferred_usec;
 
         sd_dhcp_server_callback_t callback;
         void *callback_userdata;
@@ -105,6 +106,8 @@ typedef struct DHCPRequest {
         usec_t lifetime;
         const uint8_t *agent_info_option;
         char *hostname;
+        const uint8_t *parameter_request_list;
+        size_t parameter_request_list_len;
 } DHCPRequest;
 
 extern const struct hash_ops dhcp_lease_hash_ops;
index a917406ceca984b417719f09ffa1fcdbf6ac2e39..c69572d5b08a00debf9561cf38e52cf442d17038 100644 (file)
@@ -330,6 +330,15 @@ int sd_dhcp_server_stop(sd_dhcp_server *server) {
         return 0;
 }
 
+static bool dhcp_request_contains(DHCPRequest *req, uint8_t option) {
+        assert(req);
+
+        if (!req->parameter_request_list)
+                return false;
+
+        return memchr(req->parameter_request_list, option, req->parameter_request_list_len);
+}
+
 static int dhcp_server_send_unicast_raw(
                 sd_dhcp_server *server,
                 uint8_t hlen,
@@ -649,6 +658,21 @@ static int server_send_offer_or_ack(
                         return r;
         }
 
+        /* RFC 8925 section 3.3. DHCPv4 Server Behavior
+         * The server MUST NOT include the IPv6-Only Preferred option in the DHCPOFFER or DHCPACK message if
+         * the option was not present in the Parameter Request List sent by the client. */
+        if (dhcp_request_contains(req, SD_DHCP_OPTION_IPV6_ONLY_PREFERRED) &&
+            server->ipv6_only_preferred_usec > 0) {
+                be32_t sec = usec_to_be32_sec(server->ipv6_only_preferred_usec);
+
+                r = dhcp_option_append(
+                                &packet->dhcp, req->max_optlen, &offset, 0,
+                                SD_DHCP_OPTION_IPV6_ONLY_PREFERRED,
+                                sizeof(sec), &sec);
+                if (r < 0)
+                        return r;
+        }
+
         ORDERED_SET_FOREACH(j, server->extra_options) {
                 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
                                        j->option, j->length, j->data);
@@ -778,6 +802,10 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us
                         return 0;
                 }
 
+                break;
+        case SD_DHCP_OPTION_PARAMETER_REQUEST_LIST:
+                req->parameter_request_list = option;
+                req->parameter_request_list_len = len;
                 break;
         }
 
@@ -1501,6 +1529,19 @@ int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint64_t t) {
         return 0;
 }
 
+int sd_dhcp_server_set_ipv6_only_preferred_usec(sd_dhcp_server *server, uint64_t t) {
+        assert_return(server, -EINVAL);
+
+        /* When 0 is set, disables the IPv6 only mode. */
+
+        /* Refuse too short timespan unless test mode is enabled. */
+        if (t > 0 && t < MIN_V6ONLY_WAIT_USEC && !network_test_mode_enabled())
+                 return -EINVAL;
+
+        server->ipv6_only_preferred_usec = t;
+        return 0;
+}
+
 int sd_dhcp_server_set_servers(
                 sd_dhcp_server *server,
                 sd_dhcp_lease_server_type_t what,
index 64d95258fd6cb0091f27bde9f0783376c7f11547..1256076b833bf746d599472d8c0dc528f55d325a 100644 (file)
@@ -84,6 +84,7 @@ int sd_dhcp_server_set_static_lease(sd_dhcp_server *server, const struct in_addr
 
 int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint64_t t);
 int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint64_t t);
+int sd_dhcp_server_set_ipv6_only_preferred_usec(sd_dhcp_server *server, uint64_t t);
 
 int sd_dhcp_server_forcerenew(sd_dhcp_server *server);