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,
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;
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),
+};
#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"
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;
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);
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:
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:
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:
sd_dhcp6_client_detach_event(client);
free(client->req_opts);
+ free(client->fqdn);
return mfree(client);
}
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);
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;
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;
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;
}
}
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);
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;
}
}
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);
#include "sd-dhcp6-client.h"
+#include "hostname-util.h"
#include "network-internal.h"
#include "networkd-link.h"
#include "networkd-manager.h"
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;
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;
/* 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 */
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);