diff options
author | Stefan Agner <falstaff@deheime.ch> | 2017-11-16 10:07:07 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2017-11-16 10:07:07 +0100 |
commit | 8006aa32ee29538ef1d7978d60d6427a0bf31e80 (patch) | |
tree | 2389d79f6d7acd7043c800182cc3dc3e69b1e98e /src/libsystemd-network | |
parent | sd-dhcp-client: validate hostnames stricter (#7308) (diff) | |
download | systemd-8006aa32ee29538ef1d7978d60d6427a0bf31e80.tar.xz systemd-8006aa32ee29538ef1d7978d60d6427a0bf31e80.zip |
sd-dhcp6-client: Implement FQDN Option (#7309)
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.
Diffstat (limited to 'src/libsystemd-network')
-rw-r--r-- | src/libsystemd-network/dhcp6-internal.h | 1 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp6-option.c | 27 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp6-protocol.h | 6 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp6-client.c | 36 | ||||
-rw-r--r-- | src/libsystemd-network/test-dhcp6-client.c | 36 |
5 files changed, 104 insertions, 2 deletions
diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 945c3b9721..f64382c3ca 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -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, diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index f8056dbc4b..0d4f404a3e 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -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; diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index 2487c470ab..975d35023f 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -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), +}; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index d55d0c80cc..d7252524cd 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -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); } diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index bd289fa802..8949864e29 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -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; @@ -467,6 +478,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); @@ -545,6 +565,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); |