diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 8b0f7363df..bdffcf5518 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -997,6 +997,27 @@
Defaults to false.
+
+ Anonymize=
+
+ Takes a boolean argument. When true, the options sent to the DHCP server will
+ follow the RFC 7844
+ (Anonymity Profiles for DHCP Clients) to minimize disclosure of identifying information.
+ Defaults to false.
+
+ This option should only be set to true when
+ MACAddressPolicy= is set to random
+ (see systemd.link5).
+
+ Note that this configuration will overwrite others.
+ In concrete, the following variables will be ignored:
+ SendHostname=, ClientIdentifier=,
+ UseRoutes=, SendHostname=,
+ UseMTU=, VendorClassIdentifier=,
+ UseTimezone=.
+
+
SendHostname=
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index e20d339bd5..8426f602a9 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -62,6 +62,7 @@ struct sd_dhcp_client {
uint8_t *req_opts;
size_t req_opts_allocated;
size_t req_opts_size;
+ bool anonymize;
be32_t last_addr;
uint8_t mac_addr[MAX_MAC_ADDR_LEN];
size_t mac_addr_len;
@@ -116,6 +117,32 @@ static const uint8_t default_req_opts[] = {
SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
};
+/* RFC7844 section 3:
+ MAY contain the Parameter Request List option.
+ RFC7844 section 3.6:
+ The client intending to protect its privacy SHOULD only request a
+ minimal number of options in the PRL and SHOULD also randomly shuffle
+ the ordering of option codes in the PRL. If this random ordering
+ cannot be implemented, the client MAY order the option codes in the
+ PRL by option code number (lowest to highest).
+*/
+/* NOTE: using PRL options that Windows 10 RFC7844 implementation uses */
+static const uint8_t default_req_opts_anonymize[] = {
+ SD_DHCP_OPTION_SUBNET_MASK, /* 1 */
+ SD_DHCP_OPTION_ROUTER, /* 3 */
+ SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */
+ SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */
+ SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */
+ SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */
+ SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */
+ SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */
+ SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */
+ SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */
+ SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */
+ SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */
+ SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, /* 252 */
+};
+
static int client_receive_message_raw(
sd_event_source *s,
int fd,
@@ -588,11 +615,18 @@ static int client_message_init(
it MUST include that list in any subsequent DHCPREQUEST
messages.
*/
- r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_PARAMETER_REQUEST_LIST,
- client->req_opts_size, client->req_opts);
- if (r < 0)
- return r;
+
+ /* RFC7844 section 3:
+ MAY contain the Parameter Request List option. */
+ /* NOTE: in case that there would be an option to do not send
+ * any PRL at all, the size should be checked before sending */
+ if (client->req_opts_size > 0) {
+ r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
+ SD_DHCP_OPTION_PARAMETER_REQUEST_LIST,
+ client->req_opts_size, client->req_opts);
+ if (r < 0)
+ return r;
+ }
/* RFC2131 section 3.5:
The client SHOULD include the ’maximum DHCP message size’ option to
@@ -616,12 +650,16 @@ static int client_message_init(
Maximum DHCP Message Size option is the total maximum packet size,
including IP and UDP headers.)
*/
- max_size = htobe16(size);
- r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
- SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
- 2, &max_size);
- if (r < 0)
- return r;
+ /* RFC7844 section 3:
+ SHOULD NOT contain any other option. */
+ if (!client->anonymize) {
+ max_size = htobe16(size);
+ r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
+ SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
+ 2, &max_size);
+ if (r < 0)
+ return r;
+ }
*_optlen = optlen;
*_optoffset = optoffset;
@@ -1783,7 +1821,14 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
if (r < 0)
return r;
- if (client->last_addr)
+ /* RFC7844 section 3.3:
+ SHOULD perform a complete four-way handshake, starting with a
+ DHCPDISCOVER, to obtain a new address lease. If the client can
+ ascertain that this is exactly the same network to which it was
+ previously connected, and if the link-layer address did not change,
+ the client MAY issue a DHCPREQUEST to try to reclaim the current
+ address. */
+ if (client->last_addr && !client->anonymize)
client->state = DHCP_STATE_INIT_REBOOT;
r = client_start(client);
@@ -1875,7 +1920,7 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
return mfree(client);
}
-int sd_dhcp_client_new(sd_dhcp_client **ret) {
+int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
_cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL;
assert_return(ret, -EINVAL);
@@ -1892,8 +1937,15 @@ int sd_dhcp_client_new(sd_dhcp_client **ret) {
client->mtu = DHCP_DEFAULT_MIN_SIZE;
client->port = DHCP_PORT_CLIENT;
- client->req_opts_size = ELEMENTSOF(default_req_opts);
- client->req_opts = memdup(default_req_opts, client->req_opts_size);
+ client->anonymize = !!anonymize;
+ /* NOTE: this could be moved to a function. */
+ if (anonymize) {
+ client->req_opts_size = ELEMENTSOF(default_req_opts_anonymize);
+ client->req_opts = memdup(default_req_opts_anonymize, client->req_opts_size);
+ } else {
+ client->req_opts_size = ELEMENTSOF(default_req_opts);
+ client->req_opts = memdup(default_req_opts, client->req_opts_size);
+ }
if (!client->req_opts)
return -ENOMEM;
diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c
index f5f1284e6d..5c3ae170ed 100644
--- a/src/libsystemd-network/test-dhcp-client.c
+++ b/src/libsystemd-network/test-dhcp-client.c
@@ -56,7 +56,8 @@ static void test_request_basic(sd_event *e) {
if (verbose)
printf("* %s\n", __FUNCTION__);
- r = sd_dhcp_client_new(&client);
+ /* Initialize client without Anonymize settings. */
+ r = sd_dhcp_client_new(&client, false);
assert_se(r >= 0);
assert_se(client);
@@ -78,6 +79,8 @@ static void test_request_basic(sd_event *e) {
SD_DHCP_OPTION_SUBNET_MASK) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
SD_DHCP_OPTION_ROUTER) == -EEXIST);
+ /* This PRL option is not set when using Anonymize, but in this test
+ * Anonymize settings are not being used. */
assert_se(sd_dhcp_client_set_request_option(client,
SD_DHCP_OPTION_HOST_NAME) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
@@ -97,10 +100,49 @@ static void test_request_basic(sd_event *e) {
SD_DHCP_OPTION_PARAMETER_REQUEST_LIST)
== -EINVAL);
- assert_se(sd_dhcp_client_set_request_option(client, 33) == 0);
- assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
- assert_se(sd_dhcp_client_set_request_option(client, 44) == 0);
- assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
+ /* RFC7844: option 33 (SD_DHCP_OPTION_STATIC_ROUTE) is set in the
+ * default PRL when using Anonymize, so it is changed to other option
+ * that is not set by default, to check that it succed setting it.
+ * Ooptions not set by default (using or not anonymize) are option 17
+ * (SD_DHCP_OPTION_ROOT_PATH) and 42 (SD_DHCP_OPTION_NTP_SERVER) */
+ assert_se(sd_dhcp_client_set_request_option(client, 17) == 0);
+ assert_se(sd_dhcp_client_set_request_option(client, 17) == -EEXIST);
+ assert_se(sd_dhcp_client_set_request_option(client, 42) == 0);
+ assert_se(sd_dhcp_client_set_request_option(client, 17) == -EEXIST);
+
+ sd_dhcp_client_unref(client);
+}
+
+static void test_request_anonymize(sd_event *e) {
+ int r;
+
+ sd_dhcp_client *client;
+
+ if (verbose)
+ printf("* %s\n", __FUNCTION__);
+
+ /* Initialize client with Anonymize settings. */
+ r = sd_dhcp_client_new(&client, true);
+
+ assert_se(r >= 0);
+ assert_se(client);
+
+ r = sd_dhcp_client_attach_event(client, e, 0);
+ assert_se(r >= 0);
+
+ assert_se(sd_dhcp_client_set_request_option(client,
+ SD_DHCP_OPTION_NETBIOS_NAMESERVER) == -EEXIST);
+ /* This PRL option is not set when using Anonymize */
+ assert_se(sd_dhcp_client_set_request_option(client,
+ SD_DHCP_OPTION_HOST_NAME) == 0);
+ assert_se(sd_dhcp_client_set_request_option(client,
+ SD_DHCP_OPTION_PARAMETER_REQUEST_LIST)
+ == -EINVAL);
+
+ /* RFC7844: option 101 (SD_DHCP_OPTION_NEW_TZDB_TIMEZONE) is not set in the
+ * default PRL when using Anonymize, */
+ assert_se(sd_dhcp_client_set_request_option(client, 101) == 0);
+ assert_se(sd_dhcp_client_set_request_option(client, 101) == -EEXIST);
sd_dhcp_client_unref(client);
}
@@ -236,7 +278,7 @@ static void test_discover_message(sd_event *e) {
if (verbose)
printf("* %s\n", __FUNCTION__);
- r = sd_dhcp_client_new(&client);
+ r = sd_dhcp_client_new(&client, false);
assert_se(r >= 0);
assert_se(client);
@@ -451,7 +493,7 @@ static void test_addr_acq(sd_event *e) {
if (verbose)
printf("* %s\n", __FUNCTION__);
- r = sd_dhcp_client_new(&client);
+ r = sd_dhcp_client_new(&client, false);
assert_se(r >= 0);
assert_se(client);
@@ -497,6 +539,7 @@ int main(int argc, char *argv[]) {
assert_se(sd_event_new(&e) >= 0);
test_request_basic(e);
+ test_request_anonymize(e);
test_checksum();
test_discover_message(e);
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 546c90b6bb..9c69979c7b 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -583,7 +583,7 @@ int dhcp4_configure(Link *link) {
assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
if (!link->dhcp_client) {
- r = sd_dhcp_client_new(&link->dhcp_client);
+ r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
if (r < 0)
return r;
}
@@ -624,7 +624,12 @@ int dhcp4_configure(Link *link) {
return r;
}
- if (link->network->dhcp_use_routes) {
+ /* NOTE: when using Anonymity Profiles, routes PRL options are sent
+ * by default, so they should not be added again here. */
+ /* NOTE: even if this variable is called "use", it also "sends" PRL
+ * options, maybe there should be a different configuration variable
+ * to send or not route options?. */
+ if (link->network->dhcp_use_routes && !link->network->dhcp_anonymize) {
r = sd_dhcp_client_set_request_option(link->dhcp_client,
SD_DHCP_OPTION_STATIC_ROUTE);
if (r < 0)
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 2ad10d2743..2fb1dd67ab 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -2916,7 +2916,7 @@ network_file_fail:
goto dhcp4_address_fail;
}
- r = sd_dhcp_client_new(&link->dhcp_client);
+ r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
if (r < 0)
return log_link_error_errno(link, r, "Failed to create DHCPv4 client: %m");
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 54385b59d7..7dab421392 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -101,6 +101,7 @@ DHCP.UseMTU, config_parse_bool,
DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
DHCP.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes)
+DHCP.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 77acee10f0..4a997b76d7 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -79,6 +79,46 @@ void network_config_section_free(NetworkConfigSection *cs) {
free(cs);
}
+/* Set defaults following RFC7844 */
+void network_apply_anonymize_if_set(Network *network) {
+ if (!network->dhcp_anonymize)
+ return;
+ /* RFC7844 3.7
+ SHOULD NOT send the Host Name option */
+ network->dhcp_send_hostname = false;
+ /* RFC7844 section 3.:
+ MAY contain the Client Identifier option
+ Section 3.5:
+ clients MUST use client identifiers based solely
+ on the link-layer address */
+ /* NOTE: Using MAC, as it does not reveal extra information,
+ * and some servers might not answer if this option is not sent */
+ network->dhcp_client_identifier = DHCP_CLIENT_ID_MAC;
+ /* RFC 7844 3.10:
+ SHOULD NOT use the Vendor Class Identifier option */
+ /* NOTE: it was not initiallized to any value in network_load_one. */
+ network->dhcp_vendor_class_identifier = false;
+ /* RFC7844 section 3.6.:
+ The client intending to protect its privacy SHOULD only request a
+ minimal number of options in the PRL and SHOULD also randomly shuffle
+ the ordering of option codes in the PRL. If this random ordering
+ cannot be implemented, the client MAY order the option codes in the
+ PRL by option code number (lowest to highest).
+ */
+ /* NOTE: dhcp_use_mtu is false by default,
+ * though it was not initiallized to any value in network_load_one.
+ * Maybe there should be another var called *send*?
+ * (to use the MTU sent by the server but to do not send
+ * the option in the PRL). */
+ network->dhcp_use_mtu = false;
+ /* RFC7844 section 3.6.
+ * same comments as previous option */
+ network->dhcp_use_routes = false;
+ /* RFC7844 section 3.6.
+ * same comments as previous option */
+ network->dhcp_use_timezone = false;
+}
+
static int network_load_one(Manager *manager, const char *filename) {
_cleanup_network_free_ Network *network = NULL;
_cleanup_fclose_ FILE *file = NULL;
@@ -161,11 +201,24 @@ static int network_load_one(Manager *manager, const char *filename) {
network->dhcp_use_ntp = true;
network->dhcp_use_dns = true;
network->dhcp_use_hostname = true;
+ /* NOTE: this var might be overwriten by network_apply_anonymize_if_set */
network->dhcp_use_routes = true;
+ /* NOTE: this var might be overwriten by network_apply_anonymize_if_set */
network->dhcp_send_hostname = true;
+ /* To enable/disable RFC7844 Anonymity Profiles */
+ network->dhcp_anonymize = false;
network->dhcp_route_metric = DHCP_ROUTE_METRIC;
+ /* NOTE: this var might be overwrite by network_apply_anonymize_if_set */
network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
network->dhcp_route_table = RT_TABLE_MAIN;
+ /* NOTE: the following vars were not set to any default,
+ * even if they are commented in the man?
+ * These vars might be overwriten by network_apply_anonymize_if_set */
+ network->dhcp_vendor_class_identifier = false;
+ /* NOTE: from man: UseMTU=... Defaults to false*/
+ network->dhcp_use_mtu = false;
+ /* NOTE: from man: UseTimezone=... Defaults to "no".*/
+ network->dhcp_use_timezone = false;
network->dhcp_server_emit_dns = true;
network->dhcp_server_emit_ntp = true;
@@ -220,6 +273,8 @@ static int network_load_one(Manager *manager, const char *filename) {
if (r < 0)
return r;
+ network_apply_anonymize_if_set(network);
+
/* IPMasquerade=yes implies IPForward=yes */
if (network->ip_masquerade)
network->ip_forward |= ADDRESS_FAMILY_IPV4;
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index d55e6f73f9..4025e0fa2f 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -128,6 +128,7 @@ struct Network {
unsigned dhcp_route_metric;
uint32_t dhcp_route_table;
uint16_t dhcp_client_port;
+ bool dhcp_anonymize;
bool dhcp_send_hostname;
bool dhcp_broadcast;
bool dhcp_critical;
@@ -251,6 +252,7 @@ int network_load(Manager *manager);
int network_get_by_name(Manager *manager, const char *name, Network **ret);
int network_get(Manager *manager, struct udev_device *device, const char *ifname, const struct ether_addr *mac, Network **ret);
int network_apply(Network *network, Link *link);
+void network_apply_anonymize_if_set(Network *network);
bool network_has_static_ipv6_addresses(Network *network);
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index f731fdcbd4..5e46d8d0e8 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -58,9 +58,17 @@ enum {
SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24,
SD_DHCP_OPTION_INTERFACE_MTU = 26,
SD_DHCP_OPTION_BROADCAST = 28,
+ /* Windows 10 option to send when Anonymize=true */
+ SD_DHCP_OPTION_ROUTER_DISCOVER = 31,
SD_DHCP_OPTION_STATIC_ROUTE = 33,
SD_DHCP_OPTION_NTP_SERVER = 42,
SD_DHCP_OPTION_VENDOR_SPECIFIC = 43,
+ /* Windows 10 option to send when Anonymize=true */
+ SD_DHCP_OPTION_NETBIOS_NAMESERVER = 44,
+ /* Windows 10 option to send when Anonymize=true */
+ SD_DHCP_OPTION_NETBIOS_NODETYPE = 46,
+ /* Windows 10 option to send when Anonymize=true */
+ SD_DHCP_OPTION_NETBIOS_SCOPE = 47,
SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50,
SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51,
SD_DHCP_OPTION_OVERLOAD = 52,
@@ -79,6 +87,10 @@ enum {
SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119,
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
SD_DHCP_OPTION_PRIVATE_BASE = 224,
+ /* Windows 10 option to send when Anonymize=true */
+ SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249,
+ /* Windows 10 option to send when Anonymize=true */
+ SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252,
SD_DHCP_OPTION_PRIVATE_LAST = 254,
SD_DHCP_OPTION_END = 255,
};
@@ -146,7 +158,9 @@ int sd_dhcp_client_start(sd_dhcp_client *client);
sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client);
sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
-int sd_dhcp_client_new(sd_dhcp_client **ret);
+/* NOTE: anonymize parameter is used to initialize PRL memory with different
+ * options when using RFC7844 Anonymity Profiles */
+int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize);
int sd_dhcp_client_attach_event(
sd_dhcp_client *client,