]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-dhcp6-client: Implement FQDN Option (#7309)
authorStefan Agner <falstaff@deheime.ch>
Thu, 16 Nov 2017 09:07:07 +0000 (10:07 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 16 Nov 2017 09:07:07 +0000 (10:07 +0100)
Implement DHCPv6 option to exchange information about the Fully
Qualified Domain Name (FQDN) according to RFC 4704.

The RFC 4704 describes two models of operations in section 3,
currently only the second model is supported (DHCPv6 server
updates both the AAAA and the PTR RRs).

The existing DHCP Section Options SendHostname and Hostname are
sent as FQDN to the server. According to section 4.2 sending
only parts of its FQDN is allowed.

Fixes #4682.

src/libsystemd-network/dhcp6-internal.h
src/libsystemd-network/dhcp6-option.c
src/libsystemd-network/dhcp6-protocol.h
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/test-dhcp6-client.c
src/network/networkd-dhcp6.c
src/systemd/sd-dhcp6-client.h

index 945c3b972140b52521f86e657904c9499ba1e3a4..f64382c3ca0f61af6c4f1710904565ff8c1b0b16 100644 (file)
@@ -61,6 +61,7 @@ typedef struct DHCP6IA DHCP6IA;
 int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
                         size_t optlen, const void *optval);
 int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia);
+int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
 int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
                        size_t *optlen, uint8_t **optvalue);
 int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype,
index f8056dbc4b7e30f751690d60ed8d9e5d0851a782..0d4f404a3e9ffeb1c67b29417d564a06fedfe166 100644 (file)
@@ -136,6 +136,33 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) {
         return 0;
 }
 
+int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
+        uint8_t buffer[1 + DNS_WIRE_FOMAT_HOSTNAME_MAX];
+        int r;
+
+        assert_return(buf && *buf && buflen && fqdn, -EINVAL);
+
+        buffer[0] = DHCP6_FQDN_FLAG_S; /* Request server to perform AAAA RR DNS updates */
+
+        /* Store domain name after flags field */
+        r = dns_name_to_wire_format(fqdn, buffer + 1, sizeof(buffer) - 1,  false);
+        if (r <= 0)
+                return r;
+
+        /*
+         * According to RFC 4704, chapter 4.2 only add terminating zero-length
+         * label in case a FQDN is provided. Since dns_name_to_wire_format
+         * always adds terminating zero-length label remove if only a hostname
+         * is provided.
+         */
+        if (dns_name_is_single_label(fqdn))
+                r--;
+
+        r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_FQDN, 1 + r, buffer);
+
+        return r;
+}
+
 
 static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
         DHCP6Option *option = (DHCP6Option*) *buf;
index 2487c470ab7123b62a4ed25e77a196ff224f8620..975d35023fa1d34bedd8312a97d07e860f7c38b0 100644 (file)
@@ -104,3 +104,9 @@ enum {
         DHCP6_STATUS_USE_MULTICAST              = 5,
         _DHCP6_STATUS_MAX                       = 6,
 };
+
+enum {
+        DHCP6_FQDN_FLAG_S = (1 << 0),
+        DHCP6_FQDN_FLAG_O = (1 << 1),
+        DHCP6_FQDN_FLAG_N = (1 << 2),
+};
index d55d0c80cca9667bdf3f2d2c24a15e47442c80d2..d7252524cdc04aa1c10707b4a7582072dcf135dc 100644 (file)
@@ -29,7 +29,9 @@
 #include "dhcp6-internal.h"
 #include "dhcp6-lease-internal.h"
 #include "dhcp6-protocol.h"
+#include "dns-domain.h"
 #include "fd-util.h"
+#include "hostname-util.h"
 #include "in-addr-util.h"
 #include "network-internal.h"
 #include "random-util.h"
@@ -59,6 +61,7 @@ struct sd_dhcp6_client {
         be16_t *req_opts;
         size_t req_opts_allocated;
         size_t req_opts_len;
+        char *fqdn;
         sd_event_source *receive_message;
         usec_t retransmit_time;
         uint8_t retransmit_count;
@@ -231,6 +234,20 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
         return 0;
 }
 
+int sd_dhcp6_client_set_fqdn(
+                sd_dhcp6_client *client,
+                const char *fqdn) {
+
+        assert_return(client, -EINVAL);
+
+        /* Make sure FQDN qualifies as DNS and as Linux hostname */
+        if (fqdn &&
+            !(hostname_is_valid(fqdn, false) && dns_name_is_valid(fqdn) > 0))
+                return -EINVAL;
+
+        return free_and_strdup(&client->fqdn, fqdn);
+}
+
 int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
         assert_return(client, -EINVAL);
         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
@@ -389,6 +406,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                 if (r < 0)
                         return r;
 
+                if (client->fqdn) {
+                        r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
+                        if (r < 0)
+                                return r;
+                }
+
                 break;
 
         case DHCP6_STATE_REQUEST:
@@ -409,6 +432,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                 if (r < 0)
                         return r;
 
+                if (client->fqdn) {
+                        r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
+                        if (r < 0)
+                                return r;
+                }
+
                 break;
 
         case DHCP6_STATE_REBIND:
@@ -418,6 +447,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                 if (r < 0)
                         return r;
 
+                if (client->fqdn) {
+                        r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
+                        if (r < 0)
+                                return r;
+                }
+
                 break;
 
         case DHCP6_STATE_STOPPED:
@@ -1300,6 +1335,7 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
         sd_dhcp6_client_detach_event(client);
 
         free(client->req_opts);
+        free(client->fqdn);
         return mfree(client);
 }
 
index bd289fa80219b87de18d44d40b742c65b4e1f9ed..8949864e29a33dfe5fa528d2e3ccf948bb3e4258 100644 (file)
@@ -68,6 +68,12 @@ static int test_client_basic(sd_event *e) {
                                           sizeof (mac_addr),
                                           ARPHRD_ETHER) >= 0);
 
+        assert_se(sd_dhcp6_client_set_fqdn(client, "host") == 1);
+        assert_se(sd_dhcp6_client_set_fqdn(client, "host.domain") == 1);
+        assert_se(sd_dhcp6_client_set_fqdn(client, NULL) == 1);
+        assert_se(sd_dhcp6_client_set_fqdn(client, "~host") == -EINVAL);
+        assert_se(sd_dhcp6_client_set_fqdn(client, "~host.domain") == -EINVAL);
+
         assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CLIENTID) == -EINVAL);
         assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EEXIST);
         assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NTP_SERVER) == -EEXIST);
@@ -202,6 +208,11 @@ static uint8_t msg_reply[173] = {
         0x00, 0x00, 0x00, 0x00, 0x01
 };
 
+static uint8_t fqdn_wire[16] = {
+        0x04, 'h', 'o', 's', 't', 0x03, 'l', 'a', 'b',
+        0x05, 'i', 'n', 't', 'r', 'a', 0x00
+};
+
 static int test_advertise_option(sd_event *e) {
         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
         DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
@@ -410,7 +421,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
         uint16_t optcode;
         size_t optlen;
         bool found_clientid = false, found_iana = false, found_serverid = false,
-                found_elapsed_time = false;
+                found_elapsed_time = false, found_fqdn = false;
         int r;
         struct in6_addr addr;
         be32_t val;
@@ -466,6 +477,15 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
 
                         assert_se(optlen == 2);
 
+                        break;
+                case SD_DHCP6_OPTION_FQDN:
+                        assert_se(!found_fqdn);
+                        found_fqdn = true;
+
+                        assert_se(optlen == 17);
+
+                        assert_se(optval[0] == 0x01);
+                        assert_se(!memcmp(optval + 1, fqdn_wire, sizeof(fqdn_wire)));
                         break;
                 }
         }
@@ -511,7 +531,7 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
         uint16_t optcode;
         size_t optlen;
         bool found_clientid = false, found_iana = false,
-                found_elapsed_time = false;
+                found_elapsed_time = false, found_fqdn = false;
         int r;
 
         assert_se(solicit->type == DHCP6_SOLICIT);
@@ -544,6 +564,17 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
 
                         assert_se(optlen == 2);
 
+                        break;
+
+                case SD_DHCP6_OPTION_FQDN:
+                        assert_se(!found_fqdn);
+                        found_fqdn = true;
+
+                        assert_se(optlen == 17);
+
+                        assert_se(optval[0] == 0x01);
+                        assert_se(!memcmp(optval + 1, fqdn_wire, sizeof(fqdn_wire)));
+
                         break;
                 }
         }
@@ -716,6 +747,7 @@ static int test_client_solicit(sd_event *e) {
         assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
                                           sizeof (mac_addr),
                                           ARPHRD_ETHER) >= 0);
+        assert_se(sd_dhcp6_client_set_fqdn(client, "host.lab.intra") == 1);
 
         assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0);
         assert_se(val == false);
index 6ba2d170e793f5f69d84038cb103bc02a61e442e..ee28d7ac39e95d339b63ad15c56cc3fe719aa039 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "sd-dhcp6-client.h"
 
+#include "hostname-util.h"
 #include "network-internal.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
@@ -211,6 +212,28 @@ int dhcp6_request_address(Link *link, int ir) {
         return 0;
 }
 
+static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
+        _cleanup_free_ char *hostname = NULL;
+        const char *hn;
+        int r;
+
+        assert(link);
+
+        if (!link->network->dhcp_send_hostname)
+                hn = NULL;
+        else if (link->network->dhcp_hostname)
+                hn = link->network->dhcp_hostname;
+        else {
+                r = gethostname_strict(&hostname);
+                if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
+                        return r;
+
+                hn = hostname;
+        }
+
+        return sd_dhcp6_client_set_fqdn(client, hn);
+}
+
 int dhcp6_configure(Link *link) {
         sd_dhcp6_client *client = NULL;
         int r;
@@ -247,6 +270,11 @@ int dhcp6_configure(Link *link) {
         if (r < 0)
                 goto error;
 
+        r = dhcp6_set_hostname(client, link);
+        log_link_warning(link, "dhcp6_set_hostname: %d", r);
+        if (r < 0)
+                return r;
+
         r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
         if (r < 0)
                 goto error;
index 7819f0d2de58c758d07b75a17dd9d09f3fc51a5c..0749c05c511093382f1b5a9352f0046b2ba8692a 100644 (file)
@@ -68,6 +68,8 @@ enum {
 
         /* option code 35 is unassigned */
 
+        SD_DHCP6_OPTION_FQDN                       = 39,  /* RFC 4704 */
+
         SD_DHCP6_OPTION_NTP_SERVER                 = 56,  /* RFC 5908 */
 
         /* option codes 89-142 are unassigned */
@@ -101,6 +103,9 @@ int sd_dhcp6_client_set_duid(
 int sd_dhcp6_client_set_iaid(
                 sd_dhcp6_client *client,
                 uint32_t iaid);
+int sd_dhcp6_client_set_fqdn(
+                sd_dhcp6_client *client,
+                const char *fqdn);
 int sd_dhcp6_client_set_information_request(
                 sd_dhcp6_client *client,
                 int enabled);