diff --git a/man/systemd.network.xml b/man/systemd.network.xml index e8f3f364f1..ae82ae7e02 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1765,6 +1765,22 @@ + + SendVendorOption= + + Send an arbitrary vendor option in the DHCPv6 request. Takes an enterprise identifier, DHCP option number, + data type, and data separated with a colon + (enterprise identifier:option:type: + value). Enterprise identifier is an unsigned integer ranges 1..4294967294. + The option number must be an integer in the range 1..254. Data type takes one of uint8, + uint16, uint32, ipv4address, ipv6address, or + string. Special characters in the data string may be escaped using + C-style + escapes. This setting can be specified multiple times. If an empty string is specified, + then all options specified earlier are cleared. Defaults to unset. + + + ForceDHCPv6PDOtherInformation= diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c index 3d9dab7e3c..66dd8ea08e 100644 --- a/src/network/networkd-dhcp-common.c +++ b/src/network/networkd-dhcp-common.c @@ -437,13 +437,13 @@ int config_parse_dhcp_send_option( _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *opt4 = NULL, *old4 = NULL; _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *opt6 = NULL, *old6 = NULL; + uint32_t uint32_data, enterprise_identifier = 0; _cleanup_free_ char *word = NULL, *q = NULL; OrderedHashmap **options = data; + uint16_t u16, uint16_data; union in_addr_union addr; DHCPOptionDataType type; uint8_t u8, uint8_data; - uint16_t u16, uint16_data; - uint32_t uint32_data; const void *udata; const char *p; ssize_t sz; @@ -460,10 +460,29 @@ int config_parse_dhcp_send_option( } p = rvalue; + if (ltype == AF_INET6 && streq(lvalue, "SendVendorOption")) { + r = extract_first_word(&p, &word, ":", 0); + if (r == -ENOMEM) + return log_oom(); + if (r <= 0 || isempty(p)) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Invalid DHCP option, ignoring assignment: %s", rvalue); + return 0; + } + + r = safe_atou32(word, &enterprise_identifier); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse DHCPv6 enterprise identifier data, ignoring assignment: %s", p); + return 0; + } + word = mfree(word); + } + r = extract_first_word(&p, &word, ":", 0); if (r == -ENOMEM) return log_oom(); - if (r <= 0) { + if (r <= 0 || isempty(p)) { log_syntax(unit, LOG_ERR, filename, line, r, "Invalid DHCP option, ignoring assignment: %s", rvalue); return 0; @@ -588,7 +607,7 @@ int config_parse_dhcp_send_option( } if (ltype == AF_INET6) { - r = sd_dhcp6_option_new(u16, udata, sz, &opt6); + r = sd_dhcp6_option_new(u16, udata, sz, enterprise_identifier, &opt6); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue); diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index d0972f3593..a160fd70d9 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -756,6 +756,7 @@ static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) { int dhcp6_configure(Link *link) { _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; + sd_dhcp6_option *vendor_option; sd_dhcp6_option *send_option; void *request_options; const DUID *duid; @@ -854,6 +855,14 @@ int dhcp6_configure(Link *link) { return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor class: %m"); } + ORDERED_HASHMAP_FOREACH(vendor_option, link->network->dhcp6_client_send_vendor_options, i) { + r = sd_dhcp6_client_add_vendor_option(client, vendor_option); + if (r == -EEXIST) + continue; + if (r < 0) + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor option: %m"); + } + r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link); if (r < 0) return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m"); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index d318b7d891..4de2e5e862 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -198,6 +198,7 @@ DHCPv6.MUDURL, config_parse_dhcp6_mud_url, DHCPv6.RequestOptions, config_parse_dhcp_request_options, AF_INET6, 0 DHCPv6.UserClass, config_parse_dhcp_user_class, AF_INET6, offsetof(Network, dhcp6_user_class) DHCPv6.VendorClass, config_parse_dhcp_vendor_class, 0, offsetof(Network, dhcp6_vendor_class) +DHCPv6.SendVendorOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_vendor_options) DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information) DHCPv6.AssignAcquiredDelegatedPrefixAddress, config_parse_bool, 0, offsetof(Network, dhcp6_pd_assign_prefix) DHCPv6.PrefixDelegationHint, config_parse_dhcp6_pd_hint, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index ae466a2310..c895262a0d 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -749,6 +749,7 @@ static Network *network_free(Network *network) { ordered_hashmap_free(network->dhcp_server_send_options); ordered_hashmap_free(network->dhcp_server_send_vendor_options); ordered_hashmap_free(network->ipv6_tokens); + ordered_hashmap_free(network->dhcp6_client_send_vendor_options); return mfree(network); } diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 441bba1273..0e879be01b 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -137,6 +137,7 @@ struct Network { char **dhcp6_vendor_class; struct in6_addr dhcp6_pd_address; OrderedHashmap *dhcp6_client_send_options; + OrderedHashmap *dhcp6_client_send_vendor_options; Set *dhcp6_request_options; /* DHCP Server Support */ diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index a1a6cdd233..faa38a1c1e 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -120,6 +120,7 @@ RequestOptions= UserClass= VendorClass= AssignAcquiredDelegatedPrefixAddress= +SendVendorOption= [Route] Destination= Protocol=