summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSusant Sahani <ssahani@vmware.com>2019-09-26 20:06:02 +0200
committerYu Watanabe <watanabe.yu+github@gmail.com>2019-10-15 00:14:02 +0900
commitcb29c1560515f758e6122d111e82ab302b805671 (patch)
treebf08bf944ab07d4ecb0db448ca17594c44ba239c
parent349176ae6c095725ddc11599dd03c7f16a5bb099 (diff)
downloadsystemd-cb29c1560515f758e6122d111e82ab302b805671.tar.gz
systemd-cb29c1560515f758e6122d111e82ab302b805671.tar.bz2
systemd-cb29c1560515f758e6122d111e82ab302b805671.zip
network: DHCPv4 client: add support to send arbitary option and data
-rw-r--r--man/systemd.network.xml9
-rw-r--r--src/libsystemd-network/dhcp-client-internal.h4
-rw-r--r--src/libsystemd-network/meson.build1
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c79
-rw-r--r--src/network/networkd-dhcp4.c93
-rw-r--r--src/network/networkd-dhcp4.h1
-rw-r--r--src/network/networkd-network-gperf.gperf1
-rw-r--r--src/network/networkd-network.c2
-rw-r--r--src/network/networkd-network.h5
-rw-r--r--src/systemd/sd-dhcp-client.h7
-rw-r--r--test/fuzz/fuzz-network-parser/directives.network1
11 files changed, 201 insertions, 2 deletions
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 657ba66245..58bc7e140b 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -1586,6 +1586,15 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>SendOptions=</varname></term>
+ <listitem>
+ <para>Send a raw option with value via DHCPv4 client. Takes a DHCP option and base64 encoded
+ data separated with a colon (option:value). The option ranges [1-254]. This option can be
+ specified multiple times. If an empty string is specified, then all options specified earlier
+ are cleared. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/src/libsystemd-network/dhcp-client-internal.h b/src/libsystemd-network/dhcp-client-internal.h
new file mode 100644
index 0000000000..2c48d095f4
--- /dev/null
+++ b/src/libsystemd-network/dhcp-client-internal.h
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+extern const struct hash_ops dhcp_option_hash_ops;
diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build
index 56d470ff68..7fa0c67956 100644
--- a/src/libsystemd-network/meson.build
+++ b/src/libsystemd-network/meson.build
@@ -3,6 +3,7 @@
sources = files('''
sd-dhcp-client.c
sd-dhcp-server.c
+ dhcp-client-internal.h
dhcp-network.c
dhcp-option.c
dhcp-packet.c
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 02f3569edc..550f614d0c 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -35,6 +35,14 @@
#define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC)
#define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE)
+struct sd_dhcp_option {
+ unsigned n_ref;
+
+ uint8_t option;
+ void *data;
+ size_t length;
+};
+
struct sd_dhcp_client {
unsigned n_ref;
@@ -90,6 +98,7 @@ struct sd_dhcp_client {
usec_t start_time;
uint64_t attempt;
uint64_t max_attempts;
+ OrderedHashmap *options;
usec_t request_sent;
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
@@ -530,6 +539,66 @@ int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempt
return 0;
}
+static sd_dhcp_option* dhcp_option_free(sd_dhcp_option *i) {
+ if (!i)
+ return NULL;
+
+ free(i->data);
+ return mfree(i);
+}
+
+int sd_dhcp_option_new(uint8_t option, void *data, size_t length, sd_dhcp_option **ret) {
+ _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *p = NULL;
+ _cleanup_free_ void *q = NULL;
+
+ assert(ret);
+
+ q = memdup(data, length);
+ if (!q)
+ return -ENOMEM;
+
+ p = new(sd_dhcp_option, 1);
+ if (!p)
+ return -ENOMEM;
+
+ *p = (sd_dhcp_option) {
+ .n_ref = 1,
+ .option = option,
+ .length = length,
+ .data = TAKE_PTR(q),
+ };
+
+ *ret = TAKE_PTR(p);
+ return 0;
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_option, sd_dhcp_option, dhcp_option_free);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ dhcp_option_hash_ops,
+ void,
+ trivial_hash_func,
+ trivial_compare_func,
+ sd_dhcp_option,
+ sd_dhcp_option_unref);
+
+int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v) {
+ int r;
+
+ assert_return(client, -EINVAL);
+ assert_return(v, -EINVAL);
+
+ r = ordered_hashmap_ensure_allocated(&client->options, &dhcp_option_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = ordered_hashmap_put(client->options, UINT_TO_PTR(v->option), v);
+ if (r < 0)
+ return r;
+
+ sd_dhcp_option_ref(v);
+ return 0;
+}
+
int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
assert_return(client, -EINVAL);
@@ -791,6 +860,8 @@ static int dhcp_client_send_raw(
static int client_send_discover(sd_dhcp_client *client) {
_cleanup_free_ DHCPPacket *discover = NULL;
size_t optoffset, optlen;
+ sd_dhcp_option *j;
+ Iterator i;
int r;
assert(client);
@@ -852,6 +923,13 @@ static int client_send_discover(sd_dhcp_client *client) {
return r;
}
+ ORDERED_HASHMAP_FOREACH(j, client->options, i) {
+ r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ j->option, j->length, j->data);
+ if (r < 0)
+ return r;
+ }
+
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_END, 0, NULL);
if (r < 0)
@@ -1991,6 +2069,7 @@ static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) {
free(client->hostname);
free(client->vendor_class_identifier);
client->user_class = strv_free(client->user_class);
+ ordered_hashmap_free(client->options);
return mfree(client);
}
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 6b85428758..70ab9b2a54 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -5,6 +5,8 @@
#include <linux/if_arp.h>
#include "alloc-util.h"
+#include "dhcp-client-internal.h"
+#include "hexdecoct.h"
#include "hostname-util.h"
#include "parse-util.h"
#include "network-internal.h"
@@ -1186,6 +1188,7 @@ int dhcp4_set_client_identifier(Link *link) {
}
int dhcp4_configure(Link *link) {
+ sd_dhcp_option *send_option;
void *request_options;
Iterator i;
int r;
@@ -1292,6 +1295,12 @@ int dhcp4_configure(Link *link) {
return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for '%u': %m", option);
}
+ ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_send_options, i) {
+ r = sd_dhcp_client_set_dhcp_option(link->dhcp_client, send_option);
+ if (r < 0)
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m");
+ }
+
r = dhcp4_set_hostname(link);
if (r < 0)
return r;
@@ -1557,6 +1566,90 @@ int config_parse_dhcp_request_options(
return 0;
}
+int config_parse_dhcp_send_options(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *opt = NULL, *old = NULL;
+ _cleanup_free_ char *word = NULL;
+ _cleanup_free_ void *q = NULL;
+ Network *network = data;
+ const char *p;
+ uint8_t u;
+ size_t sz;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ network->dhcp_send_options = ordered_hashmap_free(network->dhcp_send_options);
+ return 0;
+ }
+
+ p = rvalue;
+ r = extract_first_word(&p, &word, ":", 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Invalid DHCP send option, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ r = safe_atou8(word, &u);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Invalid DHCP send option, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+ if (u < 1 || u >= 255) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Invalid DHCP send option, valid range is 1-254, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ r = unbase64mem(p, (size_t) -1, &q, &sz);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to decode base64 data, ignoring assignment: %s", p);
+ return 0;
+ }
+
+ r = sd_dhcp_option_new(u, q, sz, &opt);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to store DHCP send option '%s', ignoring assignment: %m", rvalue);
+ return 0;
+ }
+
+ r = ordered_hashmap_ensure_allocated(&network->dhcp_send_options, &dhcp_option_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ /* Overwrite existing option */
+ old = ordered_hashmap_remove(network->dhcp_send_options, UINT_TO_PTR(u));
+ r = ordered_hashmap_put(network->dhcp_send_options, UINT_TO_PTR(u), opt);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to store DHCP send option '%s'", rvalue);
+ return 0;
+ }
+
+ TAKE_PTR(opt);
+ return 0;
+}
+
static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
[DHCP_CLIENT_ID_MAC] = "mac",
[DHCP_CLIENT_ID_DUID] = "duid",
diff --git a/src/network/networkd-dhcp4.h b/src/network/networkd-dhcp4.h
index fce11ef671..dbaec18781 100644
--- a/src/network/networkd-dhcp4.h
+++ b/src/network/networkd-dhcp4.h
@@ -27,3 +27,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_black_listed_ip_address);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_request_options);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_send_options);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 689b1a123e..220564c5eb 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -168,6 +168,7 @@ DHCPv4.ListenPort, config_parse_uint16,
DHCPv4.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release)
DHCPv4.BlackList, config_parse_dhcp_black_listed_ip_address, 0, 0
DHCPv4.IPServiceType, config_parse_ip_service_type, 0, offsetof(Network, ip_service_type)
+DHCPv4.SendOptions, config_parse_dhcp_send_options, 0, 0
DHCPv6.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp6_use_dns)
DHCPv6.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp6_use_ntp)
DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 3ea76a034a..f18d611b36 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -619,6 +619,8 @@ static Network *network_free(Network *network) {
set_free_free(network->dnssec_negative_trust_anchors);
+ ordered_hashmap_free(network->dhcp_send_options);
+
return mfree(network);
}
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 35469c05ed..86135c62e5 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -86,10 +86,11 @@ struct Network {
unsigned dhcp_route_metric;
uint32_t dhcp_route_table;
uint16_t dhcp_client_port;
+ int dhcp_critical;
+ int ip_service_type;
bool dhcp_anonymize;
bool dhcp_send_hostname;
bool dhcp_broadcast;
- int dhcp_critical;
bool dhcp_use_dns;
bool dhcp_routes_to_dns;
bool dhcp_use_ntp;
@@ -104,7 +105,7 @@ struct Network {
DHCPUseDomains dhcp_use_domains;
Set *dhcp_black_listed_ip;
Set *dhcp_request_options;
- int ip_service_type;
+ OrderedHashmap *dhcp_send_options;
/* DHCPv6 Client support*/
bool dhcp6_use_dns;
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 98e3281397..a44e6e35a2 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -99,6 +99,7 @@ enum {
};
typedef struct sd_dhcp_client sd_dhcp_client;
+typedef struct sd_dhcp_option sd_dhcp_option;
typedef int (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event, void *userdata);
int sd_dhcp_client_set_callback(
@@ -178,6 +179,11 @@ int sd_dhcp_client_set_service_type(
sd_dhcp_client *client,
int type);
+int sd_dhcp_option_new(uint8_t option, void *data, size_t length, sd_dhcp_option **ret);
+sd_dhcp_option* sd_dhcp_option_ref(sd_dhcp_option *i);
+sd_dhcp_option* sd_dhcp_option_unref(sd_dhcp_option *i);
+int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v);
+
int sd_dhcp_client_stop(sd_dhcp_client *client);
int sd_dhcp_client_start(sd_dhcp_client *client);
int sd_dhcp_client_send_release(sd_dhcp_client *client);
@@ -198,6 +204,7 @@ int sd_dhcp_client_detach_event(sd_dhcp_client *client);
sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_client, sd_dhcp_client_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_option, sd_dhcp_option_unref);
_SD_END_DECLARATIONS;
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index 24d94033fc..63dc8bbdf8 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -94,6 +94,7 @@ RequestOptions=
SendRelease=
MaxAttempts=
IPServiceType=
+SendOptions=
[DHCPv6]
UseNTP=
UseDNS=