1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-31 16:21:26 +03:00

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.
This commit is contained in:
Stefan Agner 2017-11-16 10:07:07 +01:00 committed by Lennart Poettering
parent 9740eae694
commit 8006aa32ee
7 changed files with 137 additions and 2 deletions

View 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,

View 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;

View 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),
};

View 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);
}

View 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);

View 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;

View 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);