From 40422ede3f44f4018377a81db1056fb3439107b2 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 22 Jul 2020 05:03:47 +0200 Subject: [PATCH 1/4] systemd: dhcp6: remove assertions in dhcp6_option_parse_domainname() Assertions are for programming errors; here the input comes directly from the DHCP response packet. https://github.com/systemd/systemd/commit/af710b535b4ceacd0aecec6748a4f8ee57742e99 (cherry picked from commit e2248143af0d4ec61e571c4f358d5d7f1044289c) --- src/systemd/src/libsystemd-network/dhcp6-option.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c index d596752b3b91..717fcdffb815 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-option.c +++ b/src/systemd/src/libsystemd-network/dhcp6-option.c @@ -649,8 +649,10 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * _cleanup_strv_free_ char **names = NULL; int r; - assert_return(optlen > 1, -ENODATA); - assert_return(optval[optlen - 1] == '\0', -EINVAL); + if (optlen <= 1) + return -ENODATA; + if (optval[optlen - 1] != '\0') + return -EINVAL; while (pos < optlen) { _cleanup_free_ char *ret = NULL; -- 2.26.2 From ab72f05d16d641bccaa1b4870bfb91c03661f1c5 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 6 Aug 2020 10:49:07 +0200 Subject: [PATCH 2/4] systemd: dhcp6: parse the FQDN option Parse option 39 (Client Fully Qualified Domain Name, RFC 4704) from the DHCP reply, which specifies the FQDN assigned by the server to the client. https://github.com/systemd/systemd/commit/c43eea9f2effbb066901a61eafef473558d37b0f (cherry picked from commit 813fb7d64ee4cb0f935a3a15b9f5b8f5771655da) --- .../src/libsystemd-network/dhcp6-internal.h | 5 +- .../libsystemd-network/dhcp6-lease-internal.h | 2 + .../src/libsystemd-network/dhcp6-option.c | 118 ++++++++++++------ .../src/libsystemd-network/sd-dhcp6-client.c | 7 ++ .../src/libsystemd-network/sd-dhcp6-lease.c | 39 +++++- src/systemd/src/systemd/sd-dhcp6-lease.h | 1 + 6 files changed, 129 insertions(+), 43 deletions(-) diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h index b0d1216eed84..068dcade0583 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h @@ -109,8 +109,9 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, struct in6_addr **addrs, size_t count, size_t *allocated); -int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, - char ***str_arr); +int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, + char ***str_arr); +int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str); int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address); int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, diff --git a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h index e004f48b4e24..df6c95e0b360 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h @@ -35,6 +35,7 @@ struct sd_dhcp6_lease { size_t ntp_allocated; char **ntp_fqdn; size_t ntp_fqdn_count; + char *fqdn; }; int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire); @@ -57,5 +58,6 @@ int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) ; +int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_new(sd_dhcp6_lease **ret); diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c index 717fcdffb815..a6dad9340643 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-option.c +++ b/src/systemd/src/libsystemd-network/dhcp6-option.c @@ -644,61 +644,103 @@ int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, return count; } -int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) { - size_t pos = 0, idx = 0; - _cleanup_strv_free_ char **names = NULL; +static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) { + _cleanup_free_ char *ret = NULL; + size_t n = 0, allocated = 0; + const uint8_t *optval = *data; + uint16_t optlen = *len; + bool first = true; int r; if (optlen <= 1) return -ENODATA; - if (optval[optlen - 1] != '\0') - return -EINVAL; - while (pos < optlen) { - _cleanup_free_ char *ret = NULL; - size_t n = 0, allocated = 0; - bool first = true; - - for (;;) { - const char *label; - uint8_t c; + for (;;) { + const char *label; + uint8_t c; - c = optval[pos++]; + if (optlen == 0) + break; - if (c == 0) - /* End of name */ - break; - if (c > 63) - return -EBADMSG; + c = *optval; + optval++; + optlen--; - /* Literal label */ - label = (const char *)&optval[pos]; - pos += c; - if (pos >= optlen) - return -EMSGSIZE; + if (c == 0) + /* End label */ + break; + if (c > 63) + return -EBADMSG; + if (c > optlen) + return -EMSGSIZE; - if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) - return -ENOMEM; + /* Literal label */ + label = (const char *)optval; + optval += c; + optlen -= c; - if (first) - first = false; - else - ret[n++] = '.'; + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) + return -ENOMEM; - r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); - if (r < 0) - return r; + if (first) + first = false; + else + ret[n++] = '.'; - n += r; - } + r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; - if (n == 0) - continue; + n += r; + } + if (n) { if (!GREEDY_REALLOC(ret, allocated, n + 1)) return -ENOMEM; - ret[n] = 0; + } + + *out_domain = TAKE_PTR(ret); + *data = optval; + *len = optlen; + + return n; +} + +int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str) { + _cleanup_free_ char *domain = NULL; + int r; + + r = parse_domain(&optval, &optlen, &domain); + if (r < 0) + return r; + if (r == 0) + return -ENODATA; + if (optlen != 0) + return -EINVAL; + + *str = TAKE_PTR(domain); + return 0; +} + +int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, char ***str_arr) { + size_t idx = 0; + _cleanup_strv_free_ char **names = NULL; + int r; + + if (optlen <= 1) + return -ENODATA; + if (optval[optlen - 1] != '\0') + return -EINVAL; + + while (optlen > 0) { + _cleanup_free_ char *ret = NULL; + + r = parse_domain(&optval, &optlen, &ret); + if (r < 0) + return r; + if (r == 0) + continue; r = strv_extend(&names, ret); if (r < 0) diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c index d653b2571c00..b80e4e5406d9 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c @@ -1288,6 +1288,13 @@ static int client_parse_message( break; + case SD_DHCP6_OPTION_FQDN: + r = dhcp6_lease_set_fqdn(lease, optval, optlen); + if (r < 0) + return r; + + break; + case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME: if (optlen != 4) return -EINVAL; diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c index b6dc02791504..5f5a7fe616fa 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c @@ -238,7 +238,7 @@ int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, if (!optlen) return 0; - r = dhcp6_option_parse_domainname(optval, optlen, &domains); + r = dhcp6_option_parse_domainname_list(optval, optlen, &domains); if (r < 0) return 0; @@ -296,8 +296,8 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { break; case DHCP6_NTP_SUBOPTION_SRV_FQDN: - r = dhcp6_option_parse_domainname(subval, sublen, - &servers); + r = dhcp6_option_parse_domainname_list(subval, sublen, + &servers); if (r < 0) return 0; @@ -367,6 +367,38 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) { return -ENOENT; } +int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, + size_t optlen) { + int r; + char *fqdn; + + assert_return(lease, -EINVAL); + assert_return(optval, -EINVAL); + + if (optlen < 2) + return -ENODATA; + + /* Ignore the flags field, it doesn't carry any useful + information for clients. */ + r = dhcp6_option_parse_domainname(optval + 1, optlen - 1, &fqdn); + if (r < 0) + return r; + + return free_and_replace(lease->fqdn, fqdn); +} + +int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn) { + assert_return(lease, -EINVAL); + assert_return(fqdn, -EINVAL); + + if (lease->fqdn) { + *fqdn = lease->fqdn; + return 0; + } + + return -ENOENT; +} + static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { assert(lease); @@ -375,6 +407,7 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { dhcp6_lease_free_ia(&lease->pd); free(lease->dns); + free(lease->fqdn); lease->domains = strv_free(lease->domains); diff --git a/src/systemd/src/systemd/sd-dhcp6-lease.h b/src/systemd/src/systemd/sd-dhcp6-lease.h index 4301c6db878b..240df74af8c5 100644 --- a/src/systemd/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/src/systemd/sd-dhcp6-lease.h @@ -43,6 +43,7 @@ int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs) int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains); int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **addrs); int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn); +int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn); sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease); sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease); -- 2.26.2 From 98d88e272c9d49876ad2c2b1507a4fda9456531e Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 22 Jul 2020 13:56:39 +0200 Subject: [PATCH 3/4] dhcp: export the DHCPv6 FQDN option The dhclient backend already exports all the option passed by dhclient, including the FDQN. Export it also for the systemd backend. (cherry picked from commit 1621a6ddb1b3f5c51ad774012150bd56cf65fcea) (cherry picked from commit c6a7618f2be4236997362db43cf44a3fdee2d9c9) --- src/dhcp/nm-dhcp-options.c | 1 + src/dhcp/nm-dhcp-options.h | 2 ++ src/dhcp/nm-dhcp-systemd.c | 8 ++++++++ 3 files changed, 11 insertions(+) diff --git a/src/dhcp/nm-dhcp-options.c b/src/dhcp/nm-dhcp-options.c index b10635fc674a..d902c77c8c21 100644 --- a/src/dhcp/nm-dhcp-options.c +++ b/src/dhcp/nm-dhcp-options.c @@ -183,6 +183,7 @@ const NMDhcpOption _nm_dhcp_option_dhcp6_options[] = { REQ (NM_DHCP_OPTION_DHCP6_DNS_SERVERS, "dhcp6_name_servers", TRUE ), REQ (NM_DHCP_OPTION_DHCP6_DOMAIN_LIST, "dhcp6_domain_search", TRUE ), REQ (NM_DHCP_OPTION_DHCP6_SNTP_SERVERS, "dhcp6_sntp_servers", TRUE ), + REQ (NM_DHCP_OPTION_DHCP6_FQDN, "fqdn_fqdn", FALSE ), REQ (NM_DHCP_OPTION_DHCP6_MUD_URL, "dhcp6_mud_url", FALSE ), /* Internal values */ diff --git a/src/dhcp/nm-dhcp-options.h b/src/dhcp/nm-dhcp-options.h index 7c0121702dad..bc3df5acd330 100644 --- a/src/dhcp/nm-dhcp-options.h +++ b/src/dhcp/nm-dhcp-options.h @@ -160,7 +160,9 @@ typedef enum { NM_DHCP_OPTION_DHCP6_DNS_SERVERS = 23, NM_DHCP_OPTION_DHCP6_DOMAIN_LIST = 24, NM_DHCP_OPTION_DHCP6_SNTP_SERVERS = 31, + NM_DHCP_OPTION_DHCP6_FQDN = 39, NM_DHCP_OPTION_DHCP6_MUD_URL = 112, + /* Internal values */ NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS = 1026, NM_DHCP_OPTION_DHCP6_NM_PREFIXLEN = 1027, diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c index f65937d8e035..7ee101128b43 100644 --- a/src/dhcp/nm-dhcp-systemd.c +++ b/src/dhcp/nm-dhcp-systemd.c @@ -740,6 +740,7 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, uint32_t lft_pref, lft_valid; char addr_str[NM_UTILS_INET_ADDRSTRLEN]; char **domains; + const char *s; nm_auto_free_gstring GString *str = NULL; int num, i; @@ -808,6 +809,13 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx, str->str); } + if (sd_dhcp6_lease_get_fqdn (lease, &s) >= 0) { + nm_dhcp_option_add_option (options, + _nm_dhcp_option_dhcp6_options, + NM_DHCP_OPTION_DHCP6_FQDN, + s); + } + NM_SET_OUT (out_options, g_steal_pointer (&options)); return g_steal_pointer (&ip6_config); } -- 2.26.2 From bce988af55e0444a23a4c3881a075ff2387b798a Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 22 Jul 2020 13:49:42 +0200 Subject: [PATCH 4/4] policy: get the DHCPv6 hostname from the FQDN option There isn't any 'host-name' option for DHCPv6. Read instead the 'fqdn-fqdn' option that carries the FQDN assigned by the server to the client. (cherry picked from commit 1f74ea52f5818c6e7d5cacd1dffdb2e1f5ee1913) (cherry picked from commit 4e1da002a920888daf5bb3aa4bd21a2d61e3214b) --- src/nm-policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nm-policy.c b/src/nm-policy.c index e571034bc345..04cbace6019a 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -764,7 +764,7 @@ update_system_hostname (NMPolicy *self, const char *msg) /* Grab a hostname out of the device's DHCP6 config */ dhcp_config = nm_device_get_dhcp_config (get_default_device (self, AF_INET6), AF_INET6); if (dhcp_config) { - dhcp_hostname = nm_dhcp_config_get_option (dhcp_config, "host_name"); + dhcp_hostname = nm_dhcp_config_get_option (dhcp_config, "fqdn_fqdn"); if (dhcp_hostname && dhcp_hostname[0]) { p = nm_str_skip_leading_spaces (dhcp_hostname); if (p[0]) { -- 2.26.2