Merge pull request #15884 from ssahani/dhcpv6-vendor

DHCPv6: Introduce vendor specific
This commit is contained in:
Yu Watanabe 2020-06-01 12:25:54 +09:00 committed by GitHub
commit f3e4b1e07c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 136 additions and 7 deletions

View File

@ -1765,6 +1765,22 @@
</listitem>
</varlistentry>
<varlistentry>
<term><varname>SendVendorOption=</varname></term>
<listitem>
<para>Send an arbitrary vendor option in the DHCPv6 request. Takes an enterprise identifier, DHCP option number,
data type, and data separated with a colon
(<literal><replaceable>enterprise identifier</replaceable>:<replaceable>option</replaceable>:<replaceable>type</replaceable>:
<replaceable>value</replaceable></literal>). 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 <literal>uint8</literal>,
<literal>uint16</literal>, <literal>uint32</literal>, <literal>ipv4address</literal>, <literal>ipv6address</literal>, or
<literal>string</literal>. Special characters in the data string may be escaped using
<ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C-style
escapes</ulink>. This setting can be specified multiple times. If an empty string is specified,
then all options specified earlier are cleared. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ForceDHCPv6PDOtherInformation=</varname></term>
<listitem>

View File

@ -11,12 +11,14 @@
#include "sd-event.h"
#include "list.h"
#include "hashmap.h"
#include "macro.h"
#include "sparse-endian.h"
typedef struct sd_dhcp6_option {
unsigned n_ref;
uint32_t enterprise_identifier;
uint16_t option;
void *data;
size_t length;
@ -99,6 +101,7 @@ int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Add
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class);
int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **user_class);
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options);
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
size_t *optlen, uint8_t **optvalue);
int dhcp6_option_parse_status(DHCP6Option *option, size_t len);

View File

@ -79,6 +79,39 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
return 0;
}
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) {
sd_dhcp6_option *options;
Iterator i;
int r;
assert(buf);
assert(*buf);
assert(buflen);
assert(vendor_options);
ORDERED_HASHMAP_FOREACH(options, vendor_options, i) {
_cleanup_free_ uint8_t *p = NULL;
size_t total;
total = 4 + 2 + 2 + options->length;
p = malloc(total);
if (!p)
return -ENOMEM;
unaligned_write_be32(p, options->enterprise_identifier);
unaligned_write_be16(p + 4, options->option);
unaligned_write_be16(p + 6, options->length);
memcpy(p + 8, options->data, options->length);
r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_OPTS, total, p);
if (r < 0)
return r;
}
return 0;
}
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
uint16_t len;
uint8_t *ia_hdr;
@ -683,7 +716,7 @@ static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) {
return mfree(i);
}
int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, sd_dhcp6_option **ret) {
int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret) {
assert_return(ret, -EINVAL);
assert_return(length == 0 || data, -EINVAL);
@ -698,6 +731,7 @@ int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, sd_dhc
*p = (sd_dhcp6_option) {
.n_ref = 1,
.option = option,
.enterprise_identifier = enterprise_identifier,
.length = length,
.data = TAKE_PTR(q),
};

View File

@ -81,6 +81,7 @@ struct sd_dhcp6_client {
usec_t information_request_time_usec;
usec_t information_refresh_time_usec;
OrderedHashmap *extra_options;
OrderedHashmap *vendor_options;
};
static const uint16_t default_req_opts[] = {
@ -225,6 +226,25 @@ int sd_dhcp6_client_set_prefix_delegation_hint(
return 0;
}
int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
int r;
assert_return(client, -EINVAL);
assert_return(v, -EINVAL);
r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp6_option_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_put(client->vendor_options, v, v);
if (r < 0)
return r;
sd_dhcp6_option_ref(v);
return 1;
}
static int client_ensure_duid(sd_dhcp6_client *client) {
if (client->duid_len != 0)
return 0;
@ -622,6 +642,13 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
if (!ordered_hashmap_isempty(client->vendor_options)) {
r = dhcp6_option_append_vendor_option(&opt, &optlen,
client->vendor_options);
if (r < 0)
return r;
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix);
if (r < 0)
@ -680,6 +707,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
if (!ordered_hashmap_isempty(client->vendor_options)) {
r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
if (r < 0)
return r;
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
if (r < 0)
@ -726,6 +759,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
if (!ordered_hashmap_isempty(client->vendor_options)) {
r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
if (r < 0)
return r;
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
if (r < 0)

View File

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

View File

@ -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");

View File

@ -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

View File

@ -752,6 +752,8 @@ 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_options);
ordered_hashmap_free(network->dhcp6_client_send_vendor_options);
return mfree(network);
}

View File

@ -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 */

View File

@ -24,6 +24,7 @@ _not_installed_headers = '''
sd-dhcp-client.h
sd-dhcp-lease.h
sd-dhcp-option.h
sd-dhcp6-option.h
sd-dhcp-server.h
sd-ipv4acd.h
sd-ipv4ll.h

View File

@ -143,7 +143,10 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client,
int *request);
int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
int request);
int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client,
uint32_t transaction_id);
int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
sd_dhcp6_option *v);
int sd_dhcp6_client_get_lease(
sd_dhcp6_client *client,

View File

@ -26,7 +26,7 @@ _SD_BEGIN_DECLARATIONS;
typedef struct sd_dhcp6_option sd_dhcp6_option;
int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, sd_dhcp6_option **ret);
int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret);
sd_dhcp6_option *sd_dhcp6_option_ref(sd_dhcp6_option *ra);
sd_dhcp6_option *sd_dhcp6_option_unref(sd_dhcp6_option *ra);

View File

@ -120,6 +120,7 @@ RequestOptions=
UserClass=
VendorClass=
AssignAcquiredDelegatedPrefixAddress=
SendVendorOption=
[Route]
Destination=
Protocol=