summary refs log tree commit diff
path: root/src/dhcp
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2019-12-18 18:29:24 +0100
committerMichael Biebl <biebl@debian.org>2019-12-18 18:29:24 +0100
commit28028b26b3371756811e95d894f709f4b1207c00 (patch)
tree6fe7316fd743b51042db47601a8ef8814b3134ac /src/dhcp
parente22609983008e1a669196ad64ba3a59ae8c76e0d (diff)
New upstream version 1.22.0 upstream/1.22.0
Diffstat (limited to 'src/dhcp')
-rw-r--r--src/dhcp/meson.build6
-rw-r--r--src/dhcp/nm-dhcp-client-logging.h18
-rw-r--r--src/dhcp/nm-dhcp-client.c120
-rw-r--r--src/dhcp/nm-dhcp-client.h35
-rw-r--r--src/dhcp/nm-dhcp-dhclient-utils.c74
-rw-r--r--src/dhcp/nm-dhcp-dhclient-utils.h17
-rw-r--r--src/dhcp/nm-dhcp-dhclient.c57
-rw-r--r--src/dhcp/nm-dhcp-dhcpcanon.c20
-rw-r--r--src/dhcp/nm-dhcp-dhcpcd.c21
-rw-r--r--src/dhcp/nm-dhcp-helper-api.h21
-rw-r--r--src/dhcp/nm-dhcp-helper.c18
-rw-r--r--src/dhcp/nm-dhcp-listener.c22
-rw-r--r--src/dhcp/nm-dhcp-listener.h18
-rw-r--r--src/dhcp/nm-dhcp-manager.c107
-rw-r--r--src/dhcp/nm-dhcp-manager.h24
-rw-r--r--src/dhcp/nm-dhcp-nettools.c378
-rw-r--r--src/dhcp/nm-dhcp-options.c19
-rw-r--r--src/dhcp/nm-dhcp-options.h18
-rw-r--r--src/dhcp/nm-dhcp-systemd.c225
-rw-r--r--src/dhcp/nm-dhcp-utils.c70
-rw-r--r--src/dhcp/nm-dhcp-utils.h24
-rw-r--r--src/dhcp/tests/meson.build3
-rw-r--r--src/dhcp/tests/test-dhcp-dhclient.c57
-rw-r--r--src/dhcp/tests/test-dhcp-utils.c17
24 files changed, 696 insertions, 693 deletions
diff --git a/src/dhcp/meson.build b/src/dhcp/meson.build
index c1f28be0..609fe663 100644
--- a/src/dhcp/meson.build
+++ b/src/dhcp/meson.build
@@ -1,6 +1,6 @@
 name = 'nm-dhcp-helper'
 
-cflags = [
+c_flags = [
   '-DG_LOG_DOMAIN="@0@"'.format(name),
   '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_GLIB',
 ]
@@ -8,8 +8,8 @@ cflags = [
 executable(
   name,
   name + '.c',
-  dependencies: libnm_core_dep,
-  c_args: cflags,
+  dependencies: glib_nm_default_dep,
+  c_args: c_flags,
   link_args: ldflags_linker_script_binary,
   link_depends: linker_script_binary,
   install: true,
diff --git a/src/dhcp/nm-dhcp-client-logging.h b/src/dhcp/nm-dhcp-client-logging.h
index d9b53f58..4e69c698 100644
--- a/src/dhcp/nm-dhcp-client-logging.h
+++ b/src/dhcp/nm-dhcp-client-logging.h
@@ -1,19 +1,5 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c
index 9f585c5d..0a07b26c 100644
--- a/src/dhcp/nm-dhcp-client.c
+++ b/src/dhcp/nm-dhcp-client.c
@@ -1,19 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2010 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
@@ -59,7 +46,10 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDhcpClient,
 	PROP_ROUTE_TABLE,
 	PROP_TIMEOUT,
 	PROP_UUID,
+	PROP_IAID,
+	PROP_IAID_EXPLICIT,
 	PROP_HOSTNAME,
+	PROP_HOSTNAME_FLAGS,
 );
 
 typedef struct _NMDhcpClientPrivate {
@@ -78,12 +68,15 @@ typedef struct _NMDhcpClientPrivate {
 	guint32      route_table;
 	guint32      route_metric;
 	guint32      timeout;
+	guint32      iaid;
 	NMDhcpState  state;
+	NMDhcpHostnameFlags hostname_flags;
 	bool         info_only:1;
 	bool         use_fqdn:1;
+	bool         iaid_explicit:1;
 } NMDhcpClientPrivate;
 
-G_DEFINE_TYPE_EXTENDED (NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {})
+G_DEFINE_ABSTRACT_TYPE (NMDhcpClient, nm_dhcp_client, G_TYPE_OBJECT)
 
 #define NM_DHCP_CLIENT_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMDhcpClient, NM_IS_DHCP_CLIENT)
 
@@ -199,6 +192,22 @@ nm_dhcp_client_get_timeout (NMDhcpClient *self)
 	return NM_DHCP_CLIENT_GET_PRIVATE (self)->timeout;
 }
 
+guint32
+nm_dhcp_client_get_iaid (NMDhcpClient *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), 0);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->iaid;
+}
+
+gboolean
+nm_dhcp_client_get_iaid_explicit (NMDhcpClient *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->iaid_explicit;
+}
+
 GBytes *
 nm_dhcp_client_get_client_id (NMDhcpClient *self)
 {
@@ -279,6 +288,14 @@ nm_dhcp_client_get_hostname (NMDhcpClient *self)
 	return NM_DHCP_CLIENT_GET_PRIVATE (self)->hostname;
 }
 
+NMDhcpHostnameFlags
+nm_dhcp_client_get_hostname_flags (NMDhcpClient *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NM_DHCP_HOSTNAME_FLAG_NONE);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->hostname_flags;
+}
+
 gboolean
 nm_dhcp_client_get_info_only (NMDhcpClient *self)
 {
@@ -419,6 +436,17 @@ nm_dhcp_client_set_state (NMDhcpClient *self,
 	if ((priv->state == new_state) && (new_state != NM_DHCP_STATE_BOUND))
 		return;
 
+	if (_LOGI_ENABLED ()) {
+		gs_free const char **keys = NULL;
+		guint i, nkeys;
+
+		keys = nm_utils_strdict_get_keys (options, TRUE, &nkeys);
+		for (i = 0; i < nkeys; i++) {
+			_LOGI ("option %-20s => '%s'", keys[i],
+		               (char *) g_hash_table_lookup (options, keys[i]));
+		}
+	}
+
 	if (   priv->addr_family == AF_INET6
 	    && new_state == NM_DHCP_STATE_BOUND) {
 		char *start, *iaid;
@@ -686,7 +714,7 @@ nm_dhcp_client_stop (NMDhcpClient *self, gboolean release)
 		_LOGI ("canceled DHCP transaction, DHCP client pid %d", old_pid);
 	else
 		_LOGI ("canceled DHCP transaction");
-	g_assert (priv->pid == -1);
+	nm_assert (priv->pid == -1);
 
 	nm_dhcp_client_set_state (self, NM_DHCP_STATE_DONE, NULL, NULL);
 }
@@ -807,6 +835,15 @@ maybe_add_option (NMDhcpClient *self,
 	}
 }
 
+void
+nm_dhcp_client_emit_ipv6_prefix_delegated (NMDhcpClient *self,
+                                           const NMPlatformIP6Address *prefix)
+{
+	g_signal_emit (G_OBJECT (self),
+	               signals[SIGNAL_PREFIX_DELEGATED], 0,
+	               prefix);
+}
+
 gboolean
 nm_dhcp_client_handle_event (gpointer unused,
                              const char *iface,
@@ -853,15 +890,6 @@ nm_dhcp_client_handle_event (gpointer unused,
 			g_variant_unref (value);
 		}
 
-		if (nm_logging_enabled (LOGL_DEBUG, LOGD_DHCP6)) {
-			GHashTableIter hash_iter;
-			gpointer key, val;
-
-			g_hash_table_iter_init (&hash_iter, str_options);
-			while (g_hash_table_iter_next (&hash_iter, &key, &val))
-				_LOGD ("option '%s'=>'%s'", (const char *) key, (const char *) val);
-		}
-
 		/* Create the IP config */
 		if (g_hash_table_size (str_options) > 0) {
 			if (priv->addr_family == AF_INET) {
@@ -887,9 +915,7 @@ nm_dhcp_client_handle_event (gpointer unused,
 		/* If we got an IPv6 prefix to delegate, we don't change the state
 		 * of the DHCP client instance. Instead, we just signal the prefix
 		 * to the device. */
-		g_signal_emit (G_OBJECT (self),
-		               signals[SIGNAL_PREFIX_DELEGATED], 0,
-		               &prefix);
+		nm_dhcp_client_emit_ipv6_prefix_delegated (self, &prefix);
 	} else {
 		/* Fail if no valid IP config was received */
 		if (   new_state == NM_DHCP_STATE_BOUND
@@ -932,6 +958,12 @@ get_property (GObject *object, guint prop_id,
 	case PROP_UUID:
 		g_value_set_string (value, priv->uuid);
 		break;
+	case PROP_IAID:
+		g_value_set_uint (value, priv->iaid);
+		break;
+	case PROP_IAID_EXPLICIT:
+		g_value_set_boolean (value, priv->iaid_explicit);
+		break;
 	case PROP_HOSTNAME:
 		g_value_set_string (value, priv->hostname);
 		break;
@@ -1001,10 +1033,22 @@ set_property (GObject *object, guint prop_id,
 		/* construct-only */
 		priv->uuid = g_value_dup_string (value);
 		break;
+	case PROP_IAID:
+		/* construct-only */
+		priv->iaid = g_value_get_uint (value);
+		break;
+	case PROP_IAID_EXPLICIT:
+		/* construct-only */
+		priv->iaid_explicit = g_value_get_boolean (value);
+		break;
 	case PROP_HOSTNAME:
 		/* construct-only */
 		priv->hostname = g_value_dup_string (value);
 		break;
+	case PROP_HOSTNAME_FLAGS:
+		/* construct-only */
+		priv->hostname_flags = g_value_get_uint (value);
+		break;
 	case PROP_ROUTE_TABLE:
 		priv->route_table = g_value_get_uint (value);
 		break;
@@ -1120,12 +1164,30 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class)
 	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 	                         G_PARAM_STATIC_STRINGS);
 
+	obj_properties[PROP_IAID] =
+	    g_param_spec_uint (NM_DHCP_CLIENT_IAID, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                       G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_IAID_EXPLICIT] =
+	    g_param_spec_boolean (NM_DHCP_CLIENT_IAID_EXPLICIT, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_STATIC_STRINGS);
+
 	obj_properties[PROP_HOSTNAME] =
 	    g_param_spec_string (NM_DHCP_CLIENT_HOSTNAME, "", "",
 	                         NULL,
 	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
 	                         G_PARAM_STATIC_STRINGS);
 
+	obj_properties[PROP_HOSTNAME_FLAGS] =
+	    g_param_spec_uint (NM_DHCP_CLIENT_HOSTNAME_FLAGS, "", "",
+	                       0, G_MAXUINT32, NM_DHCP_HOSTNAME_FLAG_NONE,
+	                       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+	                       G_PARAM_STATIC_STRINGS);
+
 	obj_properties[PROP_ROUTE_TABLE] =
 	    g_param_spec_uint (NM_DHCP_CLIENT_ROUTE_TABLE, "", "",
 	                       0, G_MAXUINT32, RT_TABLE_MAIN,
diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h
index 9eb76f33..6a431fa8 100644
--- a/src/dhcp/nm-dhcp-client.h
+++ b/src/dhcp/nm-dhcp-client.h
@@ -1,17 +1,5 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2010 Red Hat, Inc.
  */
 
@@ -46,6 +34,9 @@
 #define NM_DHCP_CLIENT_ROUTE_TABLE      "route-table"
 #define NM_DHCP_CLIENT_TIMEOUT          "timeout"
 #define NM_DHCP_CLIENT_UUID             "uuid"
+#define NM_DHCP_CLIENT_IAID             "iaid"
+#define NM_DHCP_CLIENT_IAID_EXPLICIT    "iaid-explicit"
+#define NM_DHCP_CLIENT_HOSTNAME_FLAGS   "hostname-flags"
 
 #define NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED "state-changed"
 #define NM_DHCP_CLIENT_SIGNAL_PREFIX_DELEGATED "prefix-delegated"
@@ -142,10 +133,16 @@ void nm_dhcp_client_set_route_metric (NMDhcpClient *self, guint32 route_metric);
 
 guint32 nm_dhcp_client_get_timeout (NMDhcpClient *self);
 
+guint32 nm_dhcp_client_get_iaid (NMDhcpClient *self);
+
+gboolean nm_dhcp_client_get_iaid_explicit (NMDhcpClient *self);
+
 GBytes *nm_dhcp_client_get_client_id (NMDhcpClient *self);
 
 const char *nm_dhcp_client_get_hostname (NMDhcpClient *self);
 
+NMDhcpHostnameFlags nm_dhcp_client_get_hostname_flags (NMDhcpClient *self);
+
 gboolean nm_dhcp_client_get_info_only (NMDhcpClient *self);
 
 gboolean nm_dhcp_client_get_use_fqdn (NMDhcpClient *self);
@@ -202,20 +199,28 @@ void nm_dhcp_client_set_client_id_bin (NMDhcpClient *self,
                                        const guint8 *client_id,
                                        gsize len);
 
+void nm_dhcp_client_emit_ipv6_prefix_delegated (NMDhcpClient *self,
+                                                const NMPlatformIP6Address *prefix);
+
 /*****************************************************************************
  * Client data
  *****************************************************************************/
 
 typedef struct {
-	GType (*get_type)(void);
+	GType (*get_type) (void);
+	GType (*get_type_per_addr_family) (int addr_family);
 	const char *name;
 	const char *(*get_path) (void);
+	bool experimental:1;
 } NMDhcpClientFactory;
 
+GType nm_dhcp_nettools_get_type (void);
+
 extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon;
 extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient;
 extern const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcd;
 extern const NMDhcpClientFactory _nm_dhcp_client_factory_internal;
+extern const NMDhcpClientFactory _nm_dhcp_client_factory_systemd;
 extern const NMDhcpClientFactory _nm_dhcp_client_factory_nettools;
 
 #endif /* __NETWORKMANAGER_DHCP_CLIENT_H__ */
diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c
index 98f8c13a..f31c493c 100644
--- a/src/dhcp/nm-dhcp-dhclient-utils.c
+++ b/src/dhcp/nm-dhcp-dhclient-utils.c
@@ -1,18 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2011 Red Hat, Inc.
  */
 
@@ -106,21 +93,11 @@ grab_request_options (GPtrArray *store, const char* line)
 }
 
 static void
-add_hostname4 (GString *str, const char *hostname, gboolean use_fqdn)
-{
-	if (hostname) {
-		if (use_fqdn) {
-			g_string_append_printf (str, FQDN_FORMAT "\n", hostname);
-			g_string_append (str,
-			                 "send fqdn.encoded on;\n"
-			                 "send fqdn.server-update on;\n");
-		} else
-			g_string_append_printf (str, HOSTNAME4_FORMAT "\n", hostname);
-	}
-}
-
-static void
-add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean use_fqdn)
+add_ip4_config (GString *str,
+                GBytes *client_id,
+                const char *hostname,
+                gboolean use_fqdn,
+                NMDhcpHostnameFlags hostname_flags)
 {
 	if (client_id) {
 		const char *p;
@@ -128,7 +105,7 @@ add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean
 		guint i;
 
 		p = g_bytes_get_data (client_id, &l);
-		g_assert (p);
+		nm_assert (p);
 
 		/* Allow type 0 (non-hardware address) to be represented as a string
 		 * as long as all the characters are printable.
@@ -156,7 +133,27 @@ add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean
 		g_string_append (str, "; # added by NetworkManager\n");
 	}
 
-	add_hostname4 (str, hostname, use_fqdn);
+	if (hostname) {
+		if (use_fqdn) {
+			g_string_append_printf (str, FQDN_FORMAT "\n", hostname);
+
+			g_string_append_printf (str, FQDN_TAG_PREFIX "encoded %s;\n",
+			                          (hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED)
+			                        ? "on"
+			                        : "off");
+
+			g_string_append_printf (str, FQDN_TAG_PREFIX "server-update %s;\n",
+			                          (hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE)
+			                        ? "on"
+			                        : "off");
+
+			g_string_append_printf (str, FQDN_TAG_PREFIX "no-client-update %s;\n",
+			                          (hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE)
+			                        ? "on"
+			                        : "off");
+		} else
+			g_string_append_printf (str, HOSTNAME4_FORMAT "\n", hostname);
+	}
 
 	g_string_append_c (str, '\n');
 
@@ -172,12 +169,16 @@ add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean
 }
 
 static void
-add_hostname6 (GString *str, const char *hostname)
+add_hostname6 (GString *str,
+               const char *hostname,
+               NMDhcpHostnameFlags hostname_flags)
 {
 	if (hostname) {
 		g_string_append_printf (str, FQDN_FORMAT "\n", hostname);
-		g_string_append (str,
-		                 "send fqdn.server-update on;\n");
+		if (hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE)
+			g_string_append (str, FQDN_TAG_PREFIX "server-update on;\n");
+		if (hostname_flags & NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE)
+			g_string_append (str, FQDN_TAG_PREFIX "no-client-update on;\n");
 		g_string_append_c (str, '\n');
 	}
 }
@@ -284,6 +285,7 @@ nm_dhcp_dhclient_create_config (const char *interface,
                                 const char *hostname,
                                 guint32 timeout,
                                 gboolean use_fqdn,
+                                NMDhcpHostnameFlags hostname_flags,
                                 const char *orig_path,
                                 const char *orig_contents,
                                 GBytes **out_new_client_id)
@@ -450,7 +452,7 @@ nm_dhcp_dhclient_create_config (const char *interface,
 	}
 
 	if (addr_family == AF_INET) {
-		add_ip4_config (new_contents, client_id, hostname, use_fqdn);
+		add_ip4_config (new_contents, client_id, hostname, use_fqdn, hostname_flags);
 		add_request (reqs, "rfc3442-classless-static-routes");
 		add_request (reqs, "ms-classless-static-routes");
 		add_request (reqs, "static-routes");
@@ -458,7 +460,7 @@ nm_dhcp_dhclient_create_config (const char *interface,
 		add_request (reqs, "ntp-servers");
 		add_request (reqs, "root-path");
 	} else {
-		add_hostname6 (new_contents, hostname);
+		add_hostname6 (new_contents, hostname, hostname_flags);
 		add_request (reqs, "dhcp6.name-servers");
 		add_request (reqs, "dhcp6.domain-search");
 
diff --git a/src/dhcp/nm-dhcp-dhclient-utils.h b/src/dhcp/nm-dhcp-dhclient-utils.h
index 8ca893c3..0cf53887 100644
--- a/src/dhcp/nm-dhcp-dhclient-utils.h
+++ b/src/dhcp/nm-dhcp-dhclient-utils.h
@@ -1,17 +1,5 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2010 Red Hat, Inc.
  */
 
@@ -28,6 +16,7 @@ char *nm_dhcp_dhclient_create_config (const char *interface,
                                       const char *hostname,
                                       guint32 timeout,
                                       gboolean use_fqdn,
+                                      NMDhcpHostnameFlags hostname_flags,
                                       const char *orig_path,
                                       const char *orig_contents,
                                       GBytes **out_new_client_id);
diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c
index 54b50479..869966fb 100644
--- a/src/dhcp/nm-dhcp-dhclient.c
+++ b/src/dhcp/nm-dhcp-dhclient.c
@@ -1,19 +1,5 @@
-/* nm-dhcp-dhclient.c - dhclient specific hooks for NetworkManager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2012 Red Hat, Inc.
  */
 
@@ -38,7 +24,6 @@
 #include "nm-glib-aux/nm-dedup-multi.h"
 
 #include "nm-utils.h"
-#include "nm-config.h"
 #include "nm-dhcp-dhclient-utils.h"
 #include "nm-dhcp-manager.h"
 #include "NetworkManagerUtils.h"
@@ -118,35 +103,14 @@ get_dhclient_leasefile (int addr_family,
                         const char *uuid,
                         char **out_preferred_path)
 {
-	gs_free char *rundir_path = NULL;
 	gs_free char *path = NULL;
 
-	/* First, see if the lease file is in /run */
-	rundir_path = g_strdup_printf (NMRUNDIR "/dhclient%s-%s-%s.lease",
-	                               _addr_family_to_path_part (addr_family),
-	                               uuid,
-	                               iface);
-
-	if (g_file_test (rundir_path, G_FILE_TEST_EXISTS)) {
-		NM_SET_OUT (out_preferred_path, g_strdup (rundir_path));
-		return g_steal_pointer (&rundir_path);
-	}
-
-	/* /var/lib/NetworkManager is the preferred leasefile path */
-	path = g_strdup_printf (NMSTATEDIR "/dhclient%s-%s-%s.lease",
-	                        _addr_family_to_path_part (addr_family),
-	                        uuid,
-	                        iface);
-
-	if (g_file_test (path, G_FILE_TEST_EXISTS)) {
+	if (nm_dhcp_utils_get_leasefile_path (addr_family, "dhclient", iface, uuid, &path)) {
 		NM_SET_OUT (out_preferred_path, g_strdup (path));
 		return g_steal_pointer (&path);
 	}
 
-	if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD)
-		NM_SET_OUT (out_preferred_path, g_steal_pointer (&rundir_path));
-	else
-		NM_SET_OUT (out_preferred_path, g_steal_pointer (&path));
+	NM_SET_OUT (out_preferred_path, g_steal_pointer (&path));
 
 	/* If the leasefile we're looking for doesn't exist yet in the new location
 	 * (eg, /var/lib/NetworkManager) then look in old locations to maintain
@@ -182,6 +146,7 @@ merge_dhclient_config (NMDhcpDhclient *self,
                        const char *hostname,
                        guint32 timeout,
                        gboolean use_fqdn,
+                       NMDhcpHostnameFlags hostname_flags,
                        const char *orig_path,
                        GBytes **out_new_client_id,
                        GError **error)
@@ -210,10 +175,11 @@ merge_dhclient_config (NMDhcpDhclient *self,
 	                                      hostname,
 	                                      timeout,
 	                                      use_fqdn,
+	                                      hostname_flags,
 	                                      orig_path,
 	                                      orig,
 	                                      out_new_client_id);
-	g_assert (new);
+	nm_assert (new);
 
 	return g_file_set_contents (conf_file,
 	                            new,
@@ -301,6 +267,7 @@ create_dhclient_config (NMDhcpDhclient *self,
                         const char *hostname,
                         guint32 timeout,
                         gboolean use_fqdn,
+                        NMDhcpHostnameFlags hostname_flags,
                         GBytes **out_new_client_id)
 {
 	gs_free char *orig = NULL;
@@ -328,6 +295,7 @@ create_dhclient_config (NMDhcpDhclient *self,
 	                            hostname,
 	                            timeout,
 	                            use_fqdn,
+	                            hostname_flags,
 	                            orig,
 	                            out_new_client_id,
 	                            &error)) {
@@ -527,6 +495,7 @@ ip4_start (NMDhcpClient *client,
 	                                          nm_dhcp_client_get_hostname (client),
 	                                          nm_dhcp_client_get_timeout (client),
 	                                          nm_dhcp_client_get_use_fqdn (client),
+	                                          nm_dhcp_client_get_hostname_flags (client),
 	                                          &new_client_id);
 	if (!priv->conf_file) {
 		nm_utils_error_set_literal (error,
@@ -558,6 +527,9 @@ ip6_start (NMDhcpClient *client,
 	NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client);
 	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
 
+	if (nm_dhcp_client_get_iaid_explicit (client))
+		_LOGW ("dhclient does not support specifying an IAID for DHCPv6, it will be ignored");
+
 	priv->conf_file = create_dhclient_config (self,
 	                                          AF_INET6,
 	                                          nm_dhcp_client_get_iface (client),
@@ -567,6 +539,7 @@ ip6_start (NMDhcpClient *client,
 	                                          nm_dhcp_client_get_hostname (client),
 	                                          nm_dhcp_client_get_timeout (client),
 	                                          TRUE,
+	                                          nm_dhcp_client_get_hostname_flags (client),
 	                                          NULL);
 	if (!priv->conf_file) {
 		nm_utils_error_set_literal (error,
@@ -723,7 +696,7 @@ nm_dhcp_dhclient_class_init (NMDhcpDhclientClass *dhclient_class)
 }
 
 const NMDhcpClientFactory _nm_dhcp_client_factory_dhclient = {
-	.name = "dhclient",
+	.name     = "dhclient",
 	.get_type = nm_dhcp_dhclient_get_type,
 	.get_path = nm_dhcp_dhclient_get_path,
 };
diff --git a/src/dhcp/nm-dhcp-dhcpcanon.c b/src/dhcp/nm-dhcp-dhcpcanon.c
index 2d2113cc..f9cc0f0a 100644
--- a/src/dhcp/nm-dhcp-dhcpcanon.c
+++ b/src/dhcp/nm-dhcp-dhcpcanon.c
@@ -1,19 +1,5 @@
-/* nm-dhcp-dhcpcanon.c - dhcpcanon specific hooks for NetworkManager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 juga <juga at riseup dot net>
  */
 
@@ -248,7 +234,7 @@ nm_dhcp_dhcpcanon_class_init (NMDhcpDhcpcanonClass *dhcpcanon_class)
 }
 
 const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcanon = {
-	.name = "dhcpcanon",
+	.name     = "dhcpcanon",
 	.get_type = nm_dhcp_dhcpcanon_get_type,
 	.get_path = nm_dhcp_dhcpcanon_get_path,
 };
diff --git a/src/dhcp/nm-dhcp-dhcpcd.c b/src/dhcp/nm-dhcp-dhcpcd.c
index c300bbe2..1690bce5 100644
--- a/src/dhcp/nm-dhcp-dhcpcd.c
+++ b/src/dhcp/nm-dhcp-dhcpcd.c
@@ -1,22 +1,7 @@
-/* nm-dhcp-dhcpcd.c - dhcpcd specific hooks for NetworkManager
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 Roy Marples
  * Copyright (C) 2010 Dan Williams <dcbw@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  */
 
 #include "nm-default.h"
@@ -242,7 +227,7 @@ nm_dhcp_dhcpcd_class_init (NMDhcpDhcpcdClass *dhcpcd_class)
 }
 
 const NMDhcpClientFactory _nm_dhcp_client_factory_dhcpcd = {
-	.name = "dhcpcd",
+	.name     = "dhcpcd",
 	.get_type = nm_dhcp_dhcpcd_get_type,
 	.get_path = nm_dhcp_dhcpcd_get_path,
 };
diff --git a/src/dhcp/nm-dhcp-helper-api.h b/src/dhcp/nm-dhcp-helper-api.h
index c1a3c71c..a03049aa 100644
--- a/src/dhcp/nm-dhcp-helper-api.h
+++ b/src/dhcp/nm-dhcp-helper-api.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2016 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
  */
 
 #ifndef __NM_DHCP_HELPER_API_H__
diff --git a/src/dhcp/nm-dhcp-helper.c b/src/dhcp/nm-dhcp-helper.c
index 9acc4045..17f9db7f 100644
--- a/src/dhcp/nm-dhcp-helper.c
+++ b/src/dhcp/nm-dhcp-helper.c
@@ -1,19 +1,5 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2007 - 2013 Red Hat, Inc.
  */
 
diff --git a/src/dhcp/nm-dhcp-listener.c b/src/dhcp/nm-dhcp-listener.c
index 88aafeb0..a54b9643 100644
--- a/src/dhcp/nm-dhcp-listener.c
+++ b/src/dhcp/nm-dhcp-listener.c
@@ -1,19 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2014 - 2016 Red Hat, Inc.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014 - 2016 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -38,7 +25,7 @@
 
 /*****************************************************************************/
 
-const NMDhcpClientFactory *const _nm_dhcp_manager_factories[5] = {
+const NMDhcpClientFactory *const _nm_dhcp_manager_factories[6] = {
 	/* the order here matters, as we will try the plugins in this order to find
 	 * the first available plugin. */
 
@@ -52,6 +39,7 @@ const NMDhcpClientFactory *const _nm_dhcp_manager_factories[5] = {
 	&_nm_dhcp_client_factory_dhcpcd,
 #endif
 	&_nm_dhcp_client_factory_internal,
+	&_nm_dhcp_client_factory_systemd,
 	&_nm_dhcp_client_factory_nettools,
 };
 
diff --git a/src/dhcp/nm-dhcp-listener.h b/src/dhcp/nm-dhcp-listener.h
index d9724062..c2b90dc3 100644
--- a/src/dhcp/nm-dhcp-listener.h
+++ b/src/dhcp/nm-dhcp-listener.h
@@ -1,18 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2014 Red Hat, Inc.
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_DHCP_LISTENER_H__
diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c
index 304a7b99..10ed9589 100644
--- a/src/dhcp/nm-dhcp-manager.c
+++ b/src/dhcp/nm-dhcp-manager.c
@@ -1,22 +1,7 @@
-/* nm-dhcp-manager.c - Handle the DHCP daemon for NetworkManager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2013 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
- *
  */
 
 #include "nm-default.h"
@@ -32,6 +17,7 @@
 #include <stdio.h>
 
 #include "nm-glib-aux/nm-dedup-multi.h"
+#include "systemd/nm-sd-utils-shared.h"
 
 #include "nm-config.h"
 #include "NetworkManagerUtils.h"
@@ -89,10 +75,11 @@ _client_factory_available (const NMDhcpClientFactory *client_factory)
 	return NULL;
 }
 
-static const NMDhcpClientFactory *
-_client_factory_get_effective (const NMDhcpClientFactory *client_factory,
-                               int addr_family)
+static GType
+_client_factory_get_gtype (const NMDhcpClientFactory *client_factory,
+                           int addr_family)
 {
+	GType gtype;
 	nm_auto_unref_gtypeclass NMDhcpClientClass *klass = NULL;
 
 	nm_assert (client_factory);
@@ -118,23 +105,38 @@ _client_factory_get_effective (const NMDhcpClientFactory *client_factory,
 	 * to those plugins. But we don't intend to do so. The internal plugin is the way forward and
 	 * not extending other plugins. */
 
+	if (client_factory->get_type_per_addr_family)
+		gtype = client_factory->get_type_per_addr_family (addr_family);
+	else
+		gtype = client_factory->get_type ();
+
 	if (client_factory == &_nm_dhcp_client_factory_internal) {
-		/* already using internal plugin. Nothing to do. */
-		return client_factory;
+		/* we are already using the internal plugin. Nothing to do. */
+		goto out;
 	}
 
-	klass = g_type_class_ref (client_factory->get_type ());
+	klass = g_type_class_ref (gtype);
 
 	nm_assert (NM_IS_DHCP_CLIENT_CLASS (klass));
 
 	if (addr_family == AF_INET6) {
-		return   klass->ip6_start
-		       ? client_factory
-		       : &_nm_dhcp_client_factory_internal;
+		if (!klass->ip6_start)
+			gtype = _client_factory_get_gtype (&_nm_dhcp_client_factory_internal, addr_family);
+	} else {
+		if (!klass->ip4_start)
+			gtype = _client_factory_get_gtype (&_nm_dhcp_client_factory_internal, addr_family);
 	}
-	return   klass->ip4_start
-	       ? client_factory
-	       : &_nm_dhcp_client_factory_internal;
+
+out:
+	nm_assert (g_type_is_a (gtype, NM_TYPE_DHCP_CLIENT));
+	nm_assert (({
+	               nm_auto_unref_gtypeclass NMDhcpClientClass *k = g_type_class_ref (gtype);
+
+	                  (addr_family == AF_INET6 && k->ip6_start)
+	               || (addr_family == AF_INET  && k->ip4_start);
+	            }));
+
+	return gtype;
 }
 
 /*****************************************************************************/
@@ -211,10 +213,13 @@ client_start (NMDhcpManager *self,
               const struct in6_addr *ipv6_ll_addr,
               GBytes *dhcp_client_id,
               gboolean enforce_duid,
+              guint32 iaid,
+              gboolean iaid_explicit,
               guint32 timeout,
               const char *dhcp_anycast_addr,
               const char *hostname,
               gboolean hostname_use_fqdn,
+              NMDhcpHostnameFlags hostname_flags,
               gboolean info_only,
               NMSettingIP6ConfigPrivacy privacy,
               const char *last_ip4_address,
@@ -225,7 +230,7 @@ client_start (NMDhcpManager *self,
 	NMDhcpClient *client;
 	gboolean success = FALSE;
 	gsize hwaddr_len;
-	const NMDhcpClientFactory *client_factory;
+	GType gtype;
 
 	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
 	g_return_val_if_fail (iface, NULL);
@@ -251,12 +256,21 @@ client_start (NMDhcpManager *self,
 		g_return_val_if_reached (NULL) ;
 	}
 
+	if (hostname) {
+		if (   (hostname_use_fqdn && !nm_sd_dns_name_is_valid (hostname))
+		    || (!hostname_use_fqdn && !nm_sd_hostname_is_valid (hostname, FALSE))) {
+			nm_log_warn (LOGD_DHCP , "dhcp%c: %s '%s' is invalid, will be ignored",
+			             nm_utils_addr_family_to_char (addr_family),
+			             hostname_use_fqdn ? "FQDN" : "hostname",
+			             hostname);
+			hostname = NULL;
+		}
+	}
+
 	nm_assert (g_bytes_get_size (hwaddr) == g_bytes_get_size (bcast_hwaddr));
 
 	priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
 
-	client_factory = _client_factory_get_effective (priv->client_factory, addr_family);
-
 	/* Kill any old client instance */
 	client = get_client_for_ifindex (self, addr_family, ifindex);
 	if (client) {
@@ -271,7 +285,14 @@ client_start (NMDhcpManager *self,
 		g_object_unref (client);
 	}
 
-	client = g_object_new (client_factory->get_type (),
+	gtype = _client_factory_get_gtype (priv->client_factory, addr_family);
+
+	nm_log_trace (LOGD_DHCP , "dhcp%c: creating IPv%c DHCP client of type %s",
+	              nm_utils_addr_family_to_char (addr_family),
+	              nm_utils_addr_family_to_char (addr_family),
+	              g_type_name (gtype));
+
+	client = g_object_new (gtype,
 	                       NM_DHCP_CLIENT_MULTI_IDX, multi_idx,
 	                       NM_DHCP_CLIENT_ADDR_FAMILY, addr_family,
 	                       NM_DHCP_CLIENT_INTERFACE, iface,
@@ -279,10 +300,13 @@ client_start (NMDhcpManager *self,
 	                       NM_DHCP_CLIENT_HWADDR, hwaddr,
 	                       NM_DHCP_CLIENT_BROADCAST_HWADDR, bcast_hwaddr,
 	                       NM_DHCP_CLIENT_UUID, uuid,
+	                       NM_DHCP_CLIENT_IAID, (guint) iaid,
+	                       NM_DHCP_CLIENT_IAID_EXPLICIT, iaid_explicit,
 	                       NM_DHCP_CLIENT_HOSTNAME, hostname,
 	                       NM_DHCP_CLIENT_ROUTE_TABLE, (guint) route_table,
 	                       NM_DHCP_CLIENT_ROUTE_METRIC, (guint) route_metric,
 	                       NM_DHCP_CLIENT_TIMEOUT, (guint) timeout,
+	                       NM_DHCP_CLIENT_HOSTNAME_FLAGS, (guint) hostname_flags,
 	                       NM_DHCP_CLIENT_FLAGS, (guint) (0
 	                           | (hostname_use_fqdn ? NM_DHCP_CLIENT_FLAGS_USE_FQDN  : 0)
 	                           | (info_only         ? NM_DHCP_CLIENT_FLAGS_INFO_ONLY : 0)
@@ -357,6 +381,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
                            gboolean send_hostname,
                            const char *dhcp_hostname,
                            const char *dhcp_fqdn,
+                           NMDhcpHostnameFlags hostname_flags,
                            GBytes *dhcp_client_id,
                            guint32 timeout,
                            const char *dhcp_anycast_addr,
@@ -408,10 +433,13 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
 	                     NULL,
 	                     dhcp_client_id,
 	                     FALSE,
+	                     0,
+	                     FALSE,
 	                     timeout,
 	                     dhcp_anycast_addr,
 	                     hostname,
 	                     use_fqdn,
+	                     hostname_flags,
 	                     FALSE,
 	                     0,
 	                     last_ip_address,
@@ -433,8 +461,11 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
                            guint32 route_metric,
                            gboolean send_hostname,
                            const char *dhcp_hostname,
+                           NMDhcpHostnameFlags hostname_flags,
                            GBytes *duid,
                            gboolean enforce_duid,
+                           guint32 iaid,
+                           gboolean iaid_explicit,
                            guint32 timeout,
                            const char *dhcp_anycast_addr,
                            gboolean info_only,
@@ -465,10 +496,13 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
 	                     ll_addr,
 	                     duid,
 	                     enforce_duid,
+	                     iaid,
+	                     iaid_explicit,
 	                     timeout,
 	                     dhcp_anycast_addr,
 	                     hostname,
 	                     TRUE,
+	                     hostname_flags,
 	                     info_only,
 	                     privacy,
 	                     NULL,
@@ -529,9 +563,10 @@ nm_dhcp_manager_init (NMDhcpManager *self)
 		if (!f)
 			continue;
 
-		nm_log_dbg (LOGD_DHCP, "dhcp-init: enabled DHCP client '%s' (%s)%s",
-		            f->name, g_type_name (f->get_type ()),
-		            _client_factory_available (f) ? "" : " (not available)");
+		nm_log_dbg (LOGD_DHCP, "dhcp-init: enabled DHCP client '%s'%s%s",
+		            f->name,
+		            _client_factory_available (f) ? "" : " (not available)",
+		            f->experimental ? " (undocumented internal plugin)" : "");
 	}
 
 	/* Client-specific setup */
diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h
index ff0d6f54..fb1c9834 100644
--- a/src/dhcp/nm-dhcp-manager.h
+++ b/src/dhcp/nm-dhcp-manager.h
@@ -1,19 +1,5 @@
-/* nm-dhcp-manager.c - Handle the DHCP daemon for NetworkManager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2010 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
@@ -56,6 +42,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip4     (NMDhcpManager *manager,
                                               gboolean send_hostname,
                                               const char *dhcp_hostname,
                                               const char *dhcp_fqdn,
+                                              NMDhcpHostnameFlags hostname_flags,
                                               GBytes *dhcp_client_id,
                                               guint32 timeout,
                                               const char *dhcp_anycast_addr,
@@ -74,8 +61,11 @@ NMDhcpClient * nm_dhcp_manager_start_ip6     (NMDhcpManager *manager,
                                               guint32 route_metric,
                                               gboolean send_hostname,
                                               const char *dhcp_hostname,
+                                              NMDhcpHostnameFlags hostname_flags,
                                               GBytes *duid,
                                               gboolean enforce_duid,
+                                              guint32 iaid,
+                                              gboolean iaid_explicit,
                                               guint32 timeout,
                                               const char *dhcp_anycast_addr,
                                               gboolean info_only,
@@ -86,7 +76,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip6     (NMDhcpManager *manager,
 /* For testing only */
 extern const char* nm_dhcp_helper_path;
 
-extern const NMDhcpClientFactory *const _nm_dhcp_manager_factories[5];
+extern const NMDhcpClientFactory *const _nm_dhcp_manager_factories[6];
 
 void nmtst_dhcp_manager_unget (gpointer singleton_instance);
 
diff --git a/src/dhcp/nm-dhcp-nettools.c b/src/dhcp/nm-dhcp-nettools.c
index a71a7a13..e557c004 100644
--- a/src/dhcp/nm-dhcp-nettools.c
+++ b/src/dhcp/nm-dhcp-nettools.c
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Library General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2014-2019 Red Hat, Inc.
+ * Copyright (C) 2014 - 2019 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -42,6 +29,7 @@
 #include "nm-dhcp-client-logging.h"
 #include "n-dhcp4/src/n-dhcp4.h"
 #include "systemd/nm-sd-utils-shared.h"
+#include "systemd/nm-sd-utils-dhcp.h"
 
 /*****************************************************************************/
 
@@ -55,8 +43,6 @@
 typedef struct _NMDhcpNettools NMDhcpNettools;
 typedef struct _NMDhcpNettoolsClass NMDhcpNettoolsClass;
 
-static GType nm_dhcp_nettools_get_type (void);
-
 /*****************************************************************************/
 
 typedef struct {
@@ -65,6 +51,7 @@ typedef struct {
 	NDhcp4ClientLease *lease;
 	GIOChannel *channel;
 	guint event_id;
+	char *lease_file;
 } NMDhcpNettoolsPrivate;
 
 struct _NMDhcpNettools {
@@ -85,13 +72,6 @@ G_DEFINE_TYPE (NMDhcpNettools, nm_dhcp_nettools, NM_TYPE_DHCP_CLIENT)
 #define DHCP_MAX_FQDN_LENGTH 255
 
 enum {
-	DHCP_FQDN_FLAG_S = (1 << 0),
-	DHCP_FQDN_FLAG_O = (1 << 1),
-	DHCP_FQDN_FLAG_E = (1 << 2),
-	DHCP_FQDN_FLAG_N = (1 << 3),
-};
-
-enum {
 	NM_IN_ADDR_CLASS_A,
 	NM_IN_ADDR_CLASS_B,
 	NM_IN_ADDR_CLASS_C,
@@ -354,28 +334,21 @@ lease_get_u16 (NDhcp4ClientLease *lease,
 	return TRUE;
 }
 
-#define LOG_LEASE(domain, ...) \
-    G_STMT_START { \
-        _LOG2I ((domain), (iface), "  "__VA_ARGS__); \
-    } G_STMT_END
-
 static gboolean
 lease_parse_address (NDhcp4ClientLease *lease,
-                     const char *iface,
                      NMIP4Config *ip4_config,
                      GHashTable *options,
                      GError **error)
 {
 	char addr_str[NM_UTILS_INET_ADDRSTRLEN];
-	const gint64 ts = nm_utils_get_monotonic_timestamp_ns ();
-	const gint64 ts_clock_boottime = nm_utils_monotonic_timestamp_as_boottime (ts, 1);
 	struct in_addr a_address;
 	struct in_addr a_netmask;
 	struct in_addr a_next_server;
 	guint32 a_plen;
 	guint64 nettools_lifetime;
-	gint64 a_lifetime;
-	gint64 a_expiry;
+	guint32 a_lifetime;
+	guint32 a_timestamp;
+	guint64 a_expiry;
 
 	n_dhcp4_client_lease_get_yiaddr (lease, &a_address);
 	if (a_address.s_addr == INADDR_ANY) {
@@ -383,29 +356,47 @@ lease_parse_address (NDhcp4ClientLease *lease,
 		return FALSE;
 	}
 
-	/* n_dhcp4_client_lease_get_lifetime() never fails */
 	n_dhcp4_client_lease_get_lifetime (lease, &nettools_lifetime);
-	/* FIXME: n_dhcp4_client_lease_get_lifetime() returns the time in nsec of CLOCK_BOOTTIME.
-	 * We want to retrieve the original lifetime value in seconds, so we approximate it in a_lifetime.
-	 * Use a nettools API to retrieve the original value as passed by the server.
-	 */
+
 	if (nettools_lifetime == G_MAXUINT64) {
+		a_timestamp = 0;
 		a_lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
-		a_expiry = NM_PLATFORM_LIFETIME_PERMANENT;
+		a_expiry = G_MAXUINT64;
 	} else {
-		gint64 ts_time = time (NULL);
-
-		a_lifetime = ((gint64) nettools_lifetime - ts_clock_boottime) / NM_UTILS_NS_PER_SECOND;
-		/* A lease time of 0 is allowed on some dhcp servers, so, let's accept it. */
-		if (a_lifetime < 0)
-			a_lifetime = 0;
-		else if (a_lifetime > NM_PLATFORM_LIFETIME_PERMANENT)
-			a_lifetime = NM_PLATFORM_LIFETIME_PERMANENT - 1;
-
-		if (ts_time > NM_PLATFORM_LIFETIME_PERMANENT - a_lifetime)
-			a_expiry = NM_PLATFORM_LIFETIME_PERMANENT - 1;
-		else
-			a_expiry = ts_time + a_lifetime;
+		guint64 nettools_basetime;
+		guint64 lifetime;
+		gint64 ts;
+
+		n_dhcp4_client_lease_get_basetime (lease, &nettools_basetime);
+
+		/* usually we shouldn't assert against external libraries like n-dhcp4.
+		 * Here we still do it... it seems safe enough. */
+		nm_assert (nettools_basetime > 0);
+		nm_assert (nettools_lifetime >= nettools_basetime);
+		nm_assert (((nettools_lifetime - nettools_basetime) % NM_UTILS_NS_PER_SECOND) == 0);
+		nm_assert ((nettools_lifetime - nettools_basetime) / NM_UTILS_NS_PER_SECOND <= G_MAXUINT32);
+
+		if (nettools_lifetime <= nettools_basetime) {
+			/* A lease time of 0 is allowed on some dhcp servers, so, let's accept it. */
+			lifetime = 0;
+		} else {
+			lifetime = nettools_lifetime - nettools_basetime;
+
+			/* we "ceil" the value to the next second. In practice, we don't expect any sub-second values
+			 * from n-dhcp4 anyway, so this should have no effect. */
+			lifetime += NM_UTILS_NS_PER_SECOND - 1;
+		}
+
+		ts = nm_utils_monotonic_timestamp_from_boottime (nettools_basetime, 1);
+
+		/* the timestamp must be positive, because we only started nettools DHCP client
+		 * after obtaining the first monotonic timestamp. Hence, the lease must have been
+		 * received afterwards. */
+		nm_assert (ts >= NM_UTILS_NS_PER_SECOND);
+
+		a_timestamp = ts / NM_UTILS_NS_PER_SECOND;
+		a_lifetime = NM_MIN (lifetime / NM_UTILS_NS_PER_SECOND, NM_PLATFORM_LIFETIME_PERMANENT - 1);
+		a_expiry = time (NULL) + ((lifetime - (nm_utils_clock_gettime_ns (CLOCK_BOOTTIME) - nettools_basetime)) / NM_UTILS_NS_PER_SECOND);
 	}
 
 	if (!lease_get_in_addr (lease, NM_DHCP_OPTION_DHCP4_SUBNET_MASK, &a_netmask)) {
@@ -416,7 +407,6 @@ lease_parse_address (NDhcp4ClientLease *lease,
 	nm_utils_inet4_ntop (a_address.s_addr, addr_str);
 	a_plen = nm_utils_ip4_netmask_to_prefix (a_netmask.s_addr);
 
-	LOG_LEASE (LOGD_DHCP4, "address %s/%u", addr_str, a_plen);
 	nm_dhcp_option_add_option (options,
 	                           _nm_dhcp_option_dhcp4_options,
 	                           NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS,
@@ -426,20 +416,17 @@ lease_parse_address (NDhcp4ClientLease *lease,
 	                           NM_DHCP_OPTION_DHCP4_SUBNET_MASK,
 	                           nm_utils_inet4_ntop (a_netmask.s_addr, addr_str));
 
-	LOG_LEASE (LOGD_DHCP4, "%s '%u' seconds (at %lld)",
-	           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options,
-	                                          NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME),
-	           (guint) a_lifetime,
-	           (long long) a_expiry);
 	nm_dhcp_option_add_option_u64 (options,
 	                               _nm_dhcp_option_dhcp4_options,
 	                               NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME,
 	                               (guint64) a_lifetime);
 
-	nm_dhcp_option_add_option_u64 (options,
-	                               _nm_dhcp_option_dhcp4_options,
-	                               NM_DHCP_OPTION_DHCP4_NM_EXPIRY,
-	                               (guint64) a_expiry);
+	if (a_expiry != G_MAXUINT64) {
+		nm_dhcp_option_add_option_u64 (options,
+		                               _nm_dhcp_option_dhcp4_options,
+		                               NM_DHCP_OPTION_DHCP4_NM_EXPIRY,
+		                               a_expiry);
+	}
 
 
 	n_dhcp4_client_lease_get_siaddr (lease, &a_next_server);
@@ -457,7 +444,7 @@ lease_parse_address (NDhcp4ClientLease *lease,
 	                                   .peer_address = a_address.s_addr,
 	                                   .plen         = a_plen,
 	                                   .addr_source  = NM_IP_CONFIG_SOURCE_DHCP,
-	                                   .timestamp    = ts / NM_UTILS_NS_PER_SECOND,
+	                                   .timestamp    = a_timestamp,
 	                                   .lifetime     = a_lifetime,
 	                                   .preferred    = a_lifetime,
 	                           }));
@@ -467,7 +454,6 @@ lease_parse_address (NDhcp4ClientLease *lease,
 
 static void
 lease_parse_domain_name_servers (NDhcp4ClientLease *lease,
-                                 const char *iface,
                                  NMIP4Config *ip4_config,
                                  GHashTable *options)
 {
@@ -498,7 +484,6 @@ lease_parse_domain_name_servers (NDhcp4ClientLease *lease,
 		nm_ip4_config_add_nameserver (ip4_config, addr.s_addr);
 	}
 
-	LOG_LEASE (LOGD_DHCP4, "nameserver '%s'", str->str);
 	nm_dhcp_option_add_option (options,
 	                           _nm_dhcp_option_dhcp4_options,
 	                           NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER,
@@ -507,7 +492,6 @@ lease_parse_domain_name_servers (NDhcp4ClientLease *lease,
 
 static void
 lease_parse_routes (NDhcp4ClientLease *lease,
-                    const char *iface,
                     NMIP4Config *ip4_config,
                     GHashTable *options,
                     guint32 route_table,
@@ -537,11 +521,6 @@ lease_parse_routes (NDhcp4ClientLease *lease,
 			nm_utils_inet4_ntop (dest.s_addr, dest_str);
 			nm_utils_inet4_ntop (gateway.s_addr, gateway_str);
 
-			LOG_LEASE (LOGD_DHCP4,
-			           "classless static route %s/%d gw %s",
-			           dest_str,
-			           (int) plen,
-			           gateway_str);
 			g_string_append_printf (nm_gstring_add_space_delimiter (str),
 			                        "%s/%d %s",
 			                        dest_str,
@@ -586,11 +565,6 @@ lease_parse_routes (NDhcp4ClientLease *lease,
 			nm_utils_inet4_ntop (dest.s_addr, dest_str);
 			nm_utils_inet4_ntop (gateway.s_addr, gateway_str);
 
-			LOG_LEASE (LOGD_DHCP4,
-			           "static route %s/%d gw %s",
-			           dest_str,
-			           (int) plen,
-			           gateway_str);
 			g_string_append_printf (nm_gstring_add_space_delimiter (str),
 			                        "%s/%d %s",
 			                        dest_str,
@@ -667,7 +641,6 @@ lease_parse_routes (NDhcp4ClientLease *lease,
 			                         }),
 			                         NULL);
 		}
-		LOG_LEASE (LOGD_DHCP4, "router %s", str->str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp4_options,
 		                           NM_DHCP_OPTION_DHCP4_ROUTER,
@@ -677,7 +650,6 @@ lease_parse_routes (NDhcp4ClientLease *lease,
 
 static void
 lease_parse_mtu (NDhcp4ClientLease *lease,
-                 const char *iface,
                  NMIP4Config *ip4_config,
                  GHashTable *options)
 {
@@ -689,7 +661,6 @@ lease_parse_mtu (NDhcp4ClientLease *lease,
 	if (mtu < 68)
 		return;
 
-	LOG_LEASE (LOGD_DHCP4, "mtu %u", mtu);
 	nm_dhcp_option_add_option_u64 (options,
 	                               _nm_dhcp_option_dhcp4_options,
 	                               NM_DHCP_OPTION_DHCP4_INTERFACE_MTU,
@@ -699,7 +670,6 @@ lease_parse_mtu (NDhcp4ClientLease *lease,
 
 static void
 lease_parse_metered (NDhcp4ClientLease *lease,
-                     const char *iface,
                      NMIP4Config *ip4_config,
                      GHashTable *options)
 {
@@ -715,13 +685,12 @@ lease_parse_metered (NDhcp4ClientLease *lease,
 		metered = !!memmem (data, n_data, "ANDROID_METERED", NM_STRLEN ("ANDROID_METERED"));
 	}
 
-	LOG_LEASE (LOGD_DHCP4, "%s", metered ? "metered" : "unmetered");
+	/* TODO: expose the vendor specific option when present */
 	nm_ip4_config_set_metered (ip4_config, metered);
 }
 
 static void
 lease_parse_ntps (NDhcp4ClientLease *lease,
-                  const char *iface,
                   GHashTable *options)
 {
 	nm_auto_free_gstring GString *str = NULL;
@@ -742,13 +711,14 @@ lease_parse_ntps (NDhcp4ClientLease *lease,
 		g_string_append (nm_gstring_add_space_delimiter (str), addr_str);
 	}
 
-	LOG_LEASE (LOGD_DHCP4, "ntp server '%s'", str->str);
-	nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NTP_SERVER, str->str);
+	nm_dhcp_option_add_option (options,
+	                           _nm_dhcp_option_dhcp4_options,
+	                           NM_DHCP_OPTION_DHCP4_NTP_SERVER,
+	                           str->str);
 }
 
 static void
 lease_parse_hostname (NDhcp4ClientLease *lease,
-                      const char *iface,
                       GHashTable *options)
 {
 	nm_auto_free_gstring GString *str = NULL;
@@ -765,13 +735,14 @@ lease_parse_hostname (NDhcp4ClientLease *lease,
 	if (is_localhost(str->str))
 		return;
 
-	LOG_LEASE (LOGD_DHCP4, "hostname '%s'", str->str);
-	nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_HOST_NAME, str->str);
+	nm_dhcp_option_add_option (options,
+	                           _nm_dhcp_option_dhcp4_options,
+	                           NM_DHCP_OPTION_DHCP4_HOST_NAME,
+	                           str->str);
 }
 
 static void
 lease_parse_domainname (NDhcp4ClientLease *lease,
-                        const char *iface,
                         NMIP4Config *ip4_config,
                         GHashTable *options)
 {
@@ -798,13 +769,14 @@ lease_parse_domainname (NDhcp4ClientLease *lease,
 		g_string_append (nm_gstring_add_space_delimiter (str), *d);
 		nm_ip4_config_add_domain (ip4_config, *d);
 	}
-	LOG_LEASE (LOGD_DHCP4, "domain name '%s'", str->str);
-	nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, str->str);
+	nm_dhcp_option_add_option (options,
+	                           _nm_dhcp_option_dhcp4_options,
+	                           NM_DHCP_OPTION_DHCP4_DOMAIN_NAME,
+	                           str->str);
 }
 
 static void
 lease_parse_search_domains (NDhcp4ClientLease *lease,
-                            const char *iface,
                             NMIP4Config *ip4_config,
                             GHashTable *options)
 {
@@ -832,7 +804,6 @@ lease_parse_search_domains (NDhcp4ClientLease *lease,
 		g_string_append (nm_gstring_add_space_delimiter (str), domain->str);
 		nm_ip4_config_add_search (ip4_config, domain->str);
 	}
-	LOG_LEASE (LOGD_DHCP4, "domain search '%s'", str->str);
 	nm_dhcp_option_add_option (options,
 	                           _nm_dhcp_option_dhcp4_options,
 	                           NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST,
@@ -841,7 +812,6 @@ lease_parse_search_domains (NDhcp4ClientLease *lease,
 
 static void
 lease_parse_root_path (NDhcp4ClientLease *lease,
-                       const char *iface,
                        GHashTable *options)
 {
 	nm_auto_free_gstring GString *str = NULL;
@@ -854,16 +824,17 @@ lease_parse_root_path (NDhcp4ClientLease *lease,
 		return;
 
 	str = g_string_new_len ((char *)data, n_data);
-	LOG_LEASE (LOGD_DHCP4, "root path '%s'", str->str);
-	nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROOT_PATH, str->str);
+	nm_dhcp_option_add_option (options,
+	                           _nm_dhcp_option_dhcp4_options,
+	                           NM_DHCP_OPTION_DHCP4_ROOT_PATH,
+	                           str->str);
 }
 
 static void
 lease_parse_wpad (NDhcp4ClientLease *lease,
-                  const char *iface,
                   GHashTable *options)
 {
-	nm_auto_free_gstring GString *str = NULL;
+	gs_free char *wpad = NULL;
 	uint8_t *data;
 	size_t n_data;
 	int r;
@@ -872,12 +843,44 @@ lease_parse_wpad (NDhcp4ClientLease *lease,
 	if (r)
 		return;
 
-	str = g_string_new_len ((char *)data, n_data);
-	LOG_LEASE (LOGD_DHCP4, "wpad '%s'", str->str);
+	nm_utils_buf_utf8safe_escape ((char *)data, n_data, 0, &wpad);
+	if (wpad == NULL)
+		wpad = g_strndup ((char *)data, n_data);
+
 	nm_dhcp_option_add_option (options,
 	                           _nm_dhcp_option_dhcp4_options,
 	                           NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY,
-	                           str->str);
+	                           wpad);
+}
+
+static void
+lease_parse_private_options (NDhcp4ClientLease *lease,
+                             GHashTable *options)
+{
+	int i;
+
+	for (i = NM_DHCP_OPTION_DHCP4_PRIVATE_224; i <= NM_DHCP_OPTION_DHCP4_PRIVATE_254; i++) {
+		gs_free char *option_string = NULL;
+		guint8 *data;
+		gsize n_data;
+		int r;
+
+		/* We manage private options 249 (private classless static route) and 252 (wpad) in a special
+		 * way, so skip them as we here just manage all (the other) private options as raw data */
+		if (NM_IN_SET (i, NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE,
+		                  NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY))
+			continue;
+
+		r = n_dhcp4_client_lease_query (lease, i, &data, &n_data);
+		if (r)
+			continue;
+
+		option_string = nm_utils_bin2hexstr_full (data, n_data, ':', FALSE, NULL);
+		nm_dhcp_option_take_option (options,
+		                            _nm_dhcp_option_dhcp4_options,
+		                            i,
+		                            g_steal_pointer (&option_string));
+	}
 }
 
 static NMIP4Config *
@@ -896,22 +899,23 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	g_return_val_if_fail (lease != NULL, NULL);
 
 	ip4_config = nm_ip4_config_new (multi_idx, ifindex);
-	options = out_options ? nm_dhcp_option_create_options_dict () : NULL;
+	options = nm_dhcp_option_create_options_dict ();
 
-	if (!lease_parse_address (lease, iface, ip4_config, options, error))
+	if (!lease_parse_address (lease, ip4_config, options, error))
 		return NULL;
 
-	lease_parse_routes (lease, iface, ip4_config, options, route_table, route_metric);
-	lease_parse_domain_name_servers (lease, iface, ip4_config, options);
-	lease_parse_domainname (lease, iface, ip4_config, options);
-	lease_parse_search_domains (lease, iface, ip4_config, options);
-	lease_parse_mtu (lease, iface, ip4_config, options);
-	lease_parse_metered (lease, iface, ip4_config, options);
+	lease_parse_routes (lease, ip4_config, options, route_table, route_metric);
+	lease_parse_domain_name_servers (lease, ip4_config, options);
+	lease_parse_domainname (lease, ip4_config, options);
+	lease_parse_search_domains (lease, ip4_config, options);
+	lease_parse_mtu (lease, ip4_config, options);
+	lease_parse_metered (lease, ip4_config, options);
 
-	lease_parse_hostname (lease, iface, options);
-	lease_parse_ntps (lease, iface, options);
-	lease_parse_root_path (lease, iface, options);
-	lease_parse_wpad (lease, iface, options);
+	lease_parse_hostname (lease, options);
+	lease_parse_ntps (lease, options);
+	lease_parse_root_path (lease, options);
+	lease_parse_wpad (lease, options);
+	lease_parse_private_options (lease, options);
 
 	NM_SET_OUT (out_options, g_steal_pointer (&options));
 	return g_steal_pointer (&ip4_config);
@@ -920,8 +924,34 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 /*****************************************************************************/
 
 static void
+lease_save (NDhcp4ClientLease *lease, const char *lease_file)
+{
+	struct in_addr a_address;
+	nm_auto_free_gstring GString *new_contents = NULL;
+	char sbuf[NM_UTILS_INET_ADDRSTRLEN];
+
+	nm_assert (lease);
+	nm_assert (lease_file);
+
+	new_contents = g_string_new ("# This is private data. Do not parse.\n");
+
+	n_dhcp4_client_lease_get_yiaddr (lease, &a_address);
+	if (a_address.s_addr == INADDR_ANY)
+		return;
+
+	g_string_append_printf (new_contents,
+	                        "ADDRESS=%s\n", nm_utils_inet4_ntop (a_address.s_addr, sbuf));
+
+	g_file_set_contents (lease_file,
+	                     new_contents->str,
+	                     -1,
+	                     NULL);
+}
+
+static void
 bound4_handle (NMDhcpNettools *self, NDhcp4ClientLease *lease)
 {
+	NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self);
 	const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
 	gs_unref_object NMIP4Config *ip4_config = NULL;
 	gs_unref_hashtable GHashTable *options = NULL;
@@ -945,6 +975,7 @@ bound4_handle (NMDhcpNettools *self, NDhcp4ClientLease *lease)
 	}
 
 	nm_dhcp_option_add_requests_to_options (options, _nm_dhcp_option_dhcp4_options);
+	lease_save (lease, priv->lease_file);
 
 	nm_dhcp_client_set_state (NM_DHCP_CLIENT (self),
 	                          NM_DHCP_STATE_BOUND,
@@ -1015,6 +1046,27 @@ dhcp4_event_cb (GIOChannel *source,
 	return G_SOURCE_CONTINUE;
 }
 
+G_GNUC_PRINTF (3, 4)
+static void
+nettools_log (int level, void *data, const char *fmt, ...)
+{
+	NMDhcpNettools *self = data;
+	NMLogLevel nm_level;
+	gs_free char *msg = NULL;
+	va_list ap;
+
+	nm_level = nm_log_level_from_syslog (level);
+	if (nm_logging_enabled (nm_level, LOGD_DHCP4)) {
+		va_start (ap, fmt);
+		msg = g_strdup_vprintf (fmt, ap);
+		va_end (ap);
+		nm_log (nm_level, LOGD_DHCP4, NULL , NULL,
+		        "dhcp4 (%s): %s",
+		        nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self)),
+		        msg);
+	}
+}
+
 static gboolean
 nettools_create (NMDhcpNettools *self,
                  const char *dhcp_anycast_addr,
@@ -1084,11 +1136,15 @@ nettools_create (NMDhcpNettools *self,
 		return FALSE;
 	}
 
+	n_dhcp4_client_config_set_log_level (config, nm_log_level_to_syslog (nm_logging_get_level (LOGD_DHCP4)));
+	n_dhcp4_client_config_set_log_func (config, nettools_log, self);
 	n_dhcp4_client_config_set_ifindex (config, nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)));
 	n_dhcp4_client_config_set_transport (config, transport);
 	n_dhcp4_client_config_set_mac (config, hwaddr_arr, hwaddr_len);
 	n_dhcp4_client_config_set_broadcast_mac (config, bcast_hwaddr_arr, bcast_hwaddr_len);
-	r = n_dhcp4_client_config_set_client_id (config, client_id_arr, client_id_len);
+	r = n_dhcp4_client_config_set_client_id (config,
+	                                         client_id_arr,
+	                                         NM_MIN (client_id_len, 1 + _NM_SD_MAX_CLIENT_ID_LEN));
 	if (r) {
 		nm_utils_error_set_errno (error, r, "failed to set client-id: %s");
 		return FALSE;
@@ -1157,6 +1213,22 @@ decline (NMDhcpClient *client,
 	return TRUE;
 }
 
+static guint8
+fqdn_flags_to_wire (NMDhcpHostnameFlags flags)
+{
+	guint r = 0;
+
+	/* RFC 4702 section 2.1 */
+	if (flags & NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE)
+		r |= (1 << 0);
+	if (flags & NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED)
+		r |= (1 << 2);
+	if (flags & NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE)
+		r |= (1 << 3);
+
+	return r;
+}
+
 static gboolean
 ip4_start (NMDhcpClient *client,
            const char *dhcp_anycast_addr,
@@ -1166,6 +1238,7 @@ ip4_start (NMDhcpClient *client,
 	nm_auto (n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *config = NULL;
 	NMDhcpNettools *self = NM_DHCP_NETTOOLS (client);
 	NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self);
+	gs_free char *lease_file = NULL;
 	struct in_addr last_addr = { 0 };
 	const char *hostname;
 	int r, i;
@@ -1187,11 +1260,32 @@ ip4_start (NMDhcpClient *client,
 	 */
 	n_dhcp4_client_probe_config_set_start_delay (config, 1);
 
-	if (last_ip4_address) {
+	nm_dhcp_utils_get_leasefile_path (AF_INET,
+	                                  "internal",
+	                                  nm_dhcp_client_get_iface (client),
+	                                  nm_dhcp_client_get_uuid (client),
+	                                  &lease_file);
+
+	if (last_ip4_address)
 		inet_pton (AF_INET, last_ip4_address, &last_addr);
-		n_dhcp4_client_probe_config_set_requested_ip (config, last_addr);
+	else {
+		/*
+		 * TODO: we stick to the systemd-networkd lease file format. Quite easy for now to
+		 * just use the functions in systemd code. Anyway, as in the end we just use the
+		 * ip address from all the options found in the lease, write a function that parses
+		 * the lease file just for the assigned address and returns it in &last_address.
+		 * Then drop reference to systemd-networkd structures and functions.
+		 */
+		nm_auto (sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
+
+		dhcp_lease_load (&lease, lease_file);
+		if (lease)
+			sd_dhcp_lease_get_address (lease, &last_addr);
 	}
 
+	if (last_addr.s_addr)
+		n_dhcp4_client_probe_config_set_requested_ip (config, last_addr);
+
 	/* Add requested options */
 	for (i = 0; _nm_dhcp_option_dhcp4_options[i].name; i++) {
 		if (_nm_dhcp_option_dhcp4_options[i].include) {
@@ -1204,26 +1298,38 @@ ip4_start (NMDhcpClient *client,
 	hostname = nm_dhcp_client_get_hostname (client);
 	if (hostname) {
 		if (nm_dhcp_client_get_use_fqdn (client)) {
-			uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH];
-
-			buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */
-			            DHCP_FQDN_FLAG_E;  /* Canonical wire format */
-			buffer[1] = 0;                 /* RCODE1 (deprecated) */
-			buffer[2] = 0;                 /* RCODE2 (deprecated) */
-
-			r = nm_sd_dns_name_to_wire_format (hostname,
-			                                   buffer + 3,
-			                                   sizeof (buffer) - 3,
-			                                   FALSE);
-			if (r < 0) {
-				nm_utils_error_set_errno (error, r, "failed to convert DHCP FQDN: %s");
-				return FALSE;
+			uint8_t buffer[255];
+			NMDhcpHostnameFlags flags;
+			size_t fqdn_len;
+
+			flags = nm_dhcp_client_get_hostname_flags (client);
+			buffer[0] = fqdn_flags_to_wire (flags);
+			buffer[1] = 0;   /* RCODE1 (deprecated) */
+			buffer[2] = 0;   /* RCODE2 (deprecated) */
+
+			if (flags & NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED) {
+				r = nm_sd_dns_name_to_wire_format (hostname,
+				                                   buffer + 3,
+				                                   sizeof (buffer) - 3,
+				                                   FALSE);
+				if (r <= 0) {
+					nm_utils_error_set_errno (error, r, "failed to convert DHCP FQDN: %s");
+					return FALSE;
+				}
+				fqdn_len = r;
+			} else {
+				fqdn_len = strlen (hostname);
+				if (fqdn_len > sizeof (buffer) - 3) {
+					nm_utils_error_set (error, r, "failed to set DHCP FQDN: name too long");
+					return FALSE;
+				}
+				memcpy (buffer + 3, hostname, fqdn_len);
 			}
 
 			r = n_dhcp4_client_probe_config_append_option (config,
 			                                               NM_DHCP_OPTION_DHCP4_CLIENT_FQDN,
 			                                               buffer,
-			                                               3 + r);
+			                                               3 + fqdn_len);
 			if (r) {
 				nm_utils_error_set_errno (error, r, "failed to set DHCP FQDN: %s");
 				return FALSE;
@@ -1240,6 +1346,9 @@ ip4_start (NMDhcpClient *client,
 		}
 	}
 
+	g_free (priv->lease_file);
+	priv->lease_file = g_steal_pointer (&lease_file);
+
 	r = n_dhcp4_client_probe (priv->client, &priv->probe, config);
 	if (r) {
 		nm_utils_error_set_errno (error, r, "failed to start DHCP client: %s");
@@ -1279,6 +1388,7 @@ dispose (GObject *object)
 {
 	NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE ((NMDhcpNettools *) object);
 
+	nm_clear_pointer (&priv->lease_file, g_free);
 	nm_clear_pointer (&priv->channel, g_io_channel_unref);
 	nm_clear_g_source (&priv->event_id);
 	nm_clear_pointer (&priv->lease, n_dhcp4_client_lease_unref);
@@ -1303,7 +1413,7 @@ nm_dhcp_nettools_class_init (NMDhcpNettoolsClass *class)
 }
 
 const NMDhcpClientFactory _nm_dhcp_client_factory_nettools = {
-	.name = "nettools",
-	.get_type = nm_dhcp_nettools_get_type,
-	.get_path = NULL,
+	.name         = "nettools",
+	.get_type     = nm_dhcp_nettools_get_type,
+	.experimental = TRUE,
 };
diff --git a/src/dhcp/nm-dhcp-options.c b/src/dhcp/nm-dhcp-options.c
index 22e7d90e..4c003f31 100644
--- a/src/dhcp/nm-dhcp-options.c
+++ b/src/dhcp/nm-dhcp-options.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -237,6 +223,7 @@ nm_dhcp_option_take_option (GHashTable *options,
 	nm_assert (options);
 	nm_assert (requests);
 	nm_assert (value);
+	nm_assert (g_utf8_validate (value, -1, NULL));
 
 	g_hash_table_insert (options,
 	                     (gpointer) nm_dhcp_option_request_string (requests, option),
diff --git a/src/dhcp/nm-dhcp-options.h b/src/dhcp/nm-dhcp-options.h
index f56edb19..bf9ccd57 100644
--- a/src/dhcp/nm-dhcp-options.h
+++ b/src/dhcp/nm-dhcp-options.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #ifndef __NM_DHCP_OPTIONS_H__
diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c
index 96409345..1518d465 100644
--- a/src/dhcp/nm-dhcp-systemd.c
+++ b/src/dhcp/nm-dhcp-systemd.c
@@ -1,18 +1,5 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Library General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
@@ -30,7 +17,6 @@
 #include "nm-std-aux/unaligned.h"
 
 #include "nm-utils.h"
-#include "nm-config.h"
 #include "nm-dhcp-utils.h"
 #include "nm-dhcp-options.h"
 #include "nm-core-utils.h"
@@ -81,13 +67,6 @@ G_DEFINE_TYPE (NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT)
 
 /*****************************************************************************/
 
-#define LOG_LEASE(domain, ...) \
-G_STMT_START { \
-	if (log_lease) { \
-		_LOG2D ((domain), (iface), "  "__VA_ARGS__); \
-	} \
-} G_STMT_END
-
 static NMIP4Config *
 lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
                      const char *iface,
@@ -95,7 +74,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
                      sd_dhcp_lease *lease,
                      guint32 route_table,
                      guint32 route_metric,
-                     gboolean log_lease,
                      GHashTable **out_options,
                      GError **error)
 {
@@ -151,25 +129,17 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	options = out_options ? nm_dhcp_option_create_options_dict () : NULL;
 
 	nm_utils_inet4_ntop (a_address.s_addr, addr_str);
-	LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-	           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS),
-	           addr_str);
-	nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, addr_str);
+	nm_dhcp_option_add_option (options,
+	                           _nm_dhcp_option_dhcp4_options,
+	                           NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS,
+	                           addr_str);
 
 	a_plen = nm_utils_ip4_netmask_to_prefix (a_netmask.s_addr);
-	LOG_LEASE (LOGD_DHCP4, "%s '%u'",
-	           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_SUBNET_MASK),
-	           (guint) a_plen);
 	nm_dhcp_option_add_option (options,
 	                           _nm_dhcp_option_dhcp4_options,
 	                           NM_DHCP_OPTION_DHCP4_SUBNET_MASK,
 	                           nm_utils_inet4_ntop (a_netmask.s_addr, addr_str));
 
-	LOG_LEASE (LOGD_DHCP4, "%s '%u' seconds (at %lld)",
-	           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options,
-	                                          NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME),
-	           (guint) a_lifetime,
-	           (long long) (ts_time + a_lifetime));
 	nm_dhcp_option_add_option_u64 (options,
 	                               _nm_dhcp_option_dhcp4_options,
 	                               NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME,
@@ -200,9 +170,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 
 	if (sd_dhcp_lease_get_server_identifier (lease, &server_id) >= 0) {
 		nm_utils_inet4_ntop (server_id.s_addr, addr_str);
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_SERVER_ID),
-		           addr_str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp4_options,
 		                           NM_DHCP_OPTION_DHCP4_SERVER_ID,
@@ -211,9 +178,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 
 	if (sd_dhcp_lease_get_broadcast (lease, &broadcast) >= 0) {
 		nm_utils_inet4_ntop (broadcast.s_addr, addr_str);
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_BROADCAST),
-		           addr_str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp4_options,
 		                           NM_DHCP_OPTION_DHCP4_BROADCAST,
@@ -235,9 +199,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 			}
 			nm_ip4_config_add_nameserver (ip4_config, addr_list[i].s_addr);
 		}
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER),
-		           str->str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp4_options,
 		                           NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER,
@@ -251,9 +212,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 			g_string_append (nm_gstring_add_space_delimiter (str), search_domains[i]);
 			nm_ip4_config_add_search (ip4_config, search_domains[i]);
 		}
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST),
-		           str->str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp4_options,
 		                           NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST,
@@ -264,10 +222,10 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 		gs_strfreev char **domains = NULL;
 		char **d;
 
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME),
-		           s);
-		nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, s);
+		nm_dhcp_option_add_option (options,
+		                           _nm_dhcp_option_dhcp4_options,
+		                           NM_DHCP_OPTION_DHCP4_DOMAIN_NAME,
+		                           s);
 
 		/* Multiple domains sometimes stuffed into option 15 "Domain Name".
 		 * As systemd escapes such characters, split them at \\032. */
@@ -277,10 +235,10 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	}
 
 	if (sd_dhcp_lease_get_hostname (lease, &s) >= 0) {
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_HOST_NAME),
-		           s);
-		nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_HOST_NAME, s);
+		nm_dhcp_option_add_option (options,
+		                           _nm_dhcp_option_dhcp4_options,
+		                           NM_DHCP_OPTION_DHCP4_HOST_NAME,
+		                           s);
 	}
 
 	num = sd_dhcp_lease_get_routes (lease, &routes);
@@ -333,14 +291,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 			nm_utils_inet4_ntop (network_net, network_net_str);
 			nm_utils_inet4_ntop (r_gateway.s_addr, gateway_str);
 
-			LOG_LEASE (LOGD_DHCP4,
-			           "%sstatic_route %s/%d gw %s",
-			             option == NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE
-			           ? "rfc3442_classless_"
-			           : "",
-			           network_net_str,
-			           (int) r_plen,
-			           gateway_str);
 			g_string_append_printf (nm_gstring_add_space_delimiter (  option == NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE
 			                                                        ? str_classless
 			                                                        : str_static),
@@ -442,17 +392,14 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 			                         }),
 			                         NULL);
 		}
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROUTER),
-		           str->str);
-		nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROUTER, str->str);
+		nm_dhcp_option_add_option (options,
+		                           _nm_dhcp_option_dhcp4_options,
+		                           NM_DHCP_OPTION_DHCP4_ROUTER,
+		                           str->str);
 	}
 
 	if (   sd_dhcp_lease_get_mtu (lease, &mtu) >= 0
 	    && mtu) {
-		LOG_LEASE (LOGD_DHCP4, "%s '%u'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_INTERFACE_MTU),
-		           mtu);
 		nm_dhcp_option_add_option_u64 (options,
 		                               _nm_dhcp_option_dhcp4_options,
 		                               NM_DHCP_OPTION_DHCP4_INTERFACE_MTU,
@@ -467,9 +414,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 			nm_utils_inet4_ntop (addr_list[i].s_addr, addr_str);
 			g_string_append (nm_gstring_add_space_delimiter (str), addr_str);
 		}
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NTP_SERVER),
-		           str->str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp4_options,
 		                           NM_DHCP_OPTION_DHCP4_NTP_SERVER,
@@ -477,16 +421,13 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	}
 
 	if (sd_dhcp_lease_get_root_path (lease, &s) >= 0) {
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROOT_PATH),
-		           s);
-		nm_dhcp_option_add_option (options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_ROOT_PATH, s);
+		nm_dhcp_option_add_option (options,
+		                           _nm_dhcp_option_dhcp4_options,
+		                           NM_DHCP_OPTION_DHCP4_ROOT_PATH,
+		                           s);
 	}
 
 	if (sd_dhcp_lease_get_t1 (lease, &renewal) >= 0) {
-		LOG_LEASE (LOGD_DHCP4, "%s '%u'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_RENEWAL_T1_TIME),
-		           renewal);
 		nm_dhcp_option_add_option_u64 (options,
 		                               _nm_dhcp_option_dhcp4_options,
 		                               NM_DHCP_OPTION_DHCP4_RENEWAL_T1_TIME,
@@ -494,9 +435,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	}
 
 	if (sd_dhcp_lease_get_t2 (lease, &rebinding) >= 0) {
-		LOG_LEASE (LOGD_DHCP4, "%s '%u'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_REBINDING_T2_TIME),
-		           rebinding);
 		nm_dhcp_option_add_option_u64 (options,
 		                               _nm_dhcp_option_dhcp4_options,
 		                               NM_DHCP_OPTION_DHCP4_REBINDING_T2_TIME,
@@ -504,9 +442,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	}
 
 	if (sd_dhcp_lease_get_timezone (lease, &s) >= 0) {
-		LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE),
-		           s);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp4_options,
 		                           NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE,
@@ -525,9 +460,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 			option_string = nm_utils_bin2hexstr_full (private_options[i].data,
 			                                          private_options[i].data_len,
 			                                          ':', FALSE, NULL);
-			LOG_LEASE (LOGD_DHCP4, "%s '%s'",
-			           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp4_options, private_options[i].code),
-			           option_string);
 			if (!options) {
 				g_free (option_string);
 				continue;
@@ -544,37 +476,6 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 
 /*****************************************************************************/
 
-static char *
-get_leasefile_path (int addr_family, const char *iface, const char *uuid)
-{
-	char *rundir_path;
-	char *statedir_path;
-
-	rundir_path = g_strdup_printf (NMRUNDIR "/internal%s-%s-%s.lease",
-	                               addr_family == AF_INET6 ? "6" : "",
-	                               uuid,
-	                               iface);
-
-	if (g_file_test (rundir_path, G_FILE_TEST_EXISTS))
-		return rundir_path;
-
-	statedir_path = g_strdup_printf (NMSTATEDIR "/internal%s-%s-%s.lease",
-	                                 addr_family == AF_INET6 ? "6" : "",
-	                                 uuid,
-	                                 iface);
-
-	if (   g_file_test (statedir_path, G_FILE_TEST_EXISTS)
-	    || nm_config_get_configure_and_quit (nm_config_get ()) != NM_CONFIG_CONFIGURE_AND_QUIT_INITRD) {
-		g_free (rundir_path);
-		return statedir_path;
-	} else {
-		g_free (statedir_path);
-		return rundir_path;
-	}
-}
-
-/*****************************************************************************/
-
 static void
 bound4_handle (NMDhcpSystemd *self)
 {
@@ -600,7 +501,6 @@ bound4_handle (NMDhcpSystemd *self)
 	                                  lease,
 	                                  nm_dhcp_client_get_route_table (NM_DHCP_CLIENT (self)),
 	                                  nm_dhcp_client_get_route_metric (NM_DHCP_CLIENT (self)),
-	                                  TRUE,
 	                                  &options,
 	                                  &error);
 	if (!ip4_config) {
@@ -713,9 +613,11 @@ ip4_start (NMDhcpClient *client,
 		return FALSE;
 	}
 
-	lease_file = get_leasefile_path (AF_INET,
-	                                 nm_dhcp_client_get_iface (client),
-	                                 nm_dhcp_client_get_uuid (client));
+	nm_dhcp_utils_get_leasefile_path (AF_INET,
+	                                  "internal",
+	                                  nm_dhcp_client_get_iface (client),
+	                                  nm_dhcp_client_get_uuid (client),
+	                                  &lease_file);
 
 	if (last_ip4_address)
 		inet_pton (AF_INET, last_ip4_address, &last_addr);
@@ -814,9 +716,9 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
                      const char *iface,
                      int ifindex,
                      sd_dhcp6_lease *lease,
-                     gboolean log_lease,
                      gboolean info_only,
                      GHashTable **out_options,
+                     gint32 ts,
                      GError **error)
 {
 	gs_unref_object NMIP6Config *ip6_config = NULL;
@@ -827,7 +729,6 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
 	char **domains;
 	nm_auto_free_gstring GString *str = NULL;
 	int num, i;
-	const gint32 ts = nm_utils_get_monotonic_timestamp_s ();
 
 	g_return_val_if_fail (lease, NULL);
 
@@ -838,7 +739,6 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
 	sd_dhcp6_lease_reset_address_iter (lease);
 	nm_gstring_prepare (&str);
 	while (sd_dhcp6_lease_get_address (lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) {
-		char sbuf[400];
 		const NMPlatformIP6Address address = {
 			.plen        = 128,
 			.address     = tmp_addr,
@@ -852,10 +752,6 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
 
 		nm_utils_inet6_ntop (&tmp_addr, addr_str);
 		g_string_append (nm_gstring_add_space_delimiter (str), addr_str);
-
-		LOG_LEASE (LOGD_DHCP6, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp6_options, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS),
-		           nm_platform_ip6_address_to_string (&address, sbuf, sizeof (sbuf)));
 	};
 	if (str->len)
 		nm_dhcp_option_add_option (options,
@@ -880,9 +776,6 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
 			g_string_append (nm_gstring_add_space_delimiter (str), addr_str);
 			nm_ip6_config_add_nameserver (ip6_config, &dns[i]);
 		}
-		LOG_LEASE (LOGD_DHCP6, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp6_options, NM_DHCP_OPTION_DHCP6_DNS_SERVERS),
-		           str->str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp6_options,
 		                           NM_DHCP_OPTION_DHCP6_DNS_SERVERS,
@@ -896,9 +789,6 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
 			g_string_append (nm_gstring_add_space_delimiter (str), domains[i]);
 			nm_ip6_config_add_search (ip6_config, domains[i]);
 		}
-		LOG_LEASE (LOGD_DHCP6, "%s '%s'",
-		           nm_dhcp_option_request_string (_nm_dhcp_option_dhcp6_options, NM_DHCP_OPTION_DHCP6_DOMAIN_LIST),
-		           str->str);
 		nm_dhcp_option_add_option (options,
 		                           _nm_dhcp_option_dhcp6_options,
 		                           NM_DHCP_OPTION_DHCP6_DOMAIN_LIST,
@@ -913,10 +803,12 @@ static void
 bound6_handle (NMDhcpSystemd *self)
 {
 	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
+	const gint32 ts = nm_utils_get_monotonic_timestamp_s ();
 	const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
 	gs_unref_object NMIP6Config *ip6_config = NULL;
 	gs_unref_hashtable GHashTable *options = NULL;
 	gs_free_error GError *error = NULL;
+	NMPlatformIP6Address prefix = { 0 };
 	sd_dhcp6_lease *lease;
 
 	if (   sd_dhcp6_client_get_lease (priv->client6, &lease) < 0
@@ -932,9 +824,9 @@ bound6_handle (NMDhcpSystemd *self)
 	                                  iface,
 	                                  nm_dhcp_client_get_ifindex (NM_DHCP_CLIENT (self)),
 	                                  lease,
-	                                  TRUE,
 	                                  nm_dhcp_client_get_info_only (NM_DHCP_CLIENT (self)),
 	                                  &options,
+	                                  ts,
 	                                  &error);
 
 	if (!ip6_config) {
@@ -947,6 +839,16 @@ bound6_handle (NMDhcpSystemd *self)
 	                          NM_DHCP_STATE_BOUND,
 	                          NM_IP_CONFIG_CAST (ip6_config),
 	                          options);
+
+	sd_dhcp6_lease_reset_pd_prefix_iter (lease);
+	while (!sd_dhcp6_lease_get_pd (lease,
+	                               &prefix.address,
+	                               &prefix.plen,
+	                               &prefix.preferred,
+	                               &prefix.lifetime)) {
+		prefix.timestamp = ts;
+		nm_dhcp_client_emit_ipv6_prefix_delegated (NM_DHCP_CLIENT (self), &prefix);
+	}
 }
 
 static void
@@ -990,7 +892,6 @@ ip6_start (NMDhcpClient *client,
 	nm_auto (sd_dhcp6_client_unrefp) sd_dhcp6_client *sd_client = NULL;
 	GBytes *hwaddr;
 	const char *hostname;
-	const char *iface;
 	int r, i;
 	const guint8 *duid_arr;
 	gsize duid_len;
@@ -1015,22 +916,13 @@ ip6_start (NMDhcpClient *client,
 		return FALSE;
 	}
 
-	if (needed_prefixes > 0) {
-		_LOGW ("dhcp-client6: prefix delegation not yet supported, won't supply %d prefixes",
-		       needed_prefixes);
-	}
-
 	_LOGT ("dhcp-client6: set %p", sd_client);
 
 	if (nm_dhcp_client_get_info_only (client))
 		sd_dhcp6_client_set_information_request (sd_client, 1);
 
-	iface = nm_dhcp_client_get_iface (client);
-
 	r = sd_dhcp6_client_set_iaid (sd_client,
-	                              nm_utils_create_dhcp_iaid (TRUE,
-	                                                         (const guint8 *) iface,
-	                                                         strlen (iface)));
+	                              nm_dhcp_client_get_iaid (client));
 	if (r < 0) {
 		nm_utils_error_set_errno (error, r, "failed to set IAID: %s");
 		return FALSE;
@@ -1082,6 +974,18 @@ ip6_start (NMDhcpClient *client,
 		}
 	}
 
+	if (needed_prefixes > 0) {
+		if (needed_prefixes > 1)
+			_LOGW ("dhcp-client6: only one prefix request is supported");
+		/* FIXME: systemd-networkd API only allows to request a
+		 * single prefix */
+		r = sd_dhcp6_client_set_prefix_delegation (sd_client, TRUE);
+		if (r < 0) {
+			nm_utils_error_set_errno (error, r, "failed to enable prefix delegation: %s");
+			return FALSE;
+		}
+	}
+
 	r = sd_dhcp6_client_set_local_address (sd_client, ll_addr);
 	if (r < 0) {
 		nm_utils_error_set_errno (error, r, "failed to set local address: %s");
@@ -1182,8 +1086,25 @@ nm_dhcp_systemd_class_init (NMDhcpSystemdClass *sdhcp_class)
 	client_class->stop = stop;
 }
 
+const NMDhcpClientFactory _nm_dhcp_client_factory_systemd = {
+	.name         = "systemd",
+	.get_type     = nm_dhcp_systemd_get_type,
+	.experimental = TRUE,
+};
+
+/*****************************************************************************/
+
+static GType
+_get_type_per_addr_family (int addr_family)
+{
+	nm_assert_addr_family (addr_family);
+
+	if (addr_family == AF_INET)
+		return nm_dhcp_nettools_get_type ();
+	return nm_dhcp_systemd_get_type ();
+}
+
 const NMDhcpClientFactory _nm_dhcp_client_factory_internal = {
-	.name = "internal",
-	.get_type = nm_dhcp_systemd_get_type,
-	.get_path = NULL,
+	.name                     = "internal",
+	.get_type_per_addr_family = _get_type_per_addr_family,
 };
diff --git a/src/dhcp/nm-dhcp-utils.c b/src/dhcp/nm-dhcp-utils.c
index 3f9368f2..c5da3e02 100644
--- a/src/dhcp/nm-dhcp-utils.c
+++ b/src/dhcp/nm-dhcp-utils.c
@@ -1,19 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2005 - 2010 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
@@ -25,6 +12,7 @@
 
 #include "nm-dhcp-utils.h"
 #include "nm-utils.h"
+#include "nm-config.h"
 #include "NetworkManagerUtils.h"
 #include "platform/nm-platform.h"
 #include "nm-dhcp-client-logging.h"
@@ -762,3 +750,55 @@ nm_dhcp_utils_client_id_string_to_bytes (const char *client_id)
 	return bytes;
 }
 
+/**
+ * nm_dhcp_utils_get_leasefile_path:
+ * @addr_family: the IP address family
+ * @plugin_name: the name of the plugin part of the lease file name
+ * @iface: the interface name to which the lease relates to
+ * @uuid: uuid of the connection to which the lease relates to
+ * @out_leasefile_path: will store the computed lease file path
+ *
+ * Constructs the lease file name on the basis of the calling plugin,
+ * interface name and connection uuid. Then returns in @out_leasefile_path
+ * the full path of the lease filename.
+ *
+ * Returns: TRUE if the lease file already exists, FALSE otherwise.
+ */
+gboolean
+nm_dhcp_utils_get_leasefile_path (int addr_family,
+                                  const char *plugin_name,
+                                  const char *iface,
+                                  const char *uuid,
+                                  char **out_leasefile_path)
+{
+	gs_free char *rundir_path = NULL;
+	gs_free char *statedir_path = NULL;
+
+	rundir_path = g_strdup_printf (NMRUNDIR "/%s%s-%s-%s.lease",
+	                               plugin_name,
+	                               addr_family == AF_INET6 ? "6" : "",
+	                               uuid,
+	                               iface);
+
+	if (g_file_test (rundir_path, G_FILE_TEST_EXISTS)) {
+		*out_leasefile_path = g_steal_pointer (&rundir_path);
+		return TRUE;
+	}
+
+	statedir_path = g_strdup_printf (NMSTATEDIR "/%s%s-%s-%s.lease",
+	                                 plugin_name,
+	                                 addr_family == AF_INET6 ? "6" : "",
+	                                 uuid,
+	                                 iface);
+
+	if (g_file_test (statedir_path, G_FILE_TEST_EXISTS)) {
+		*out_leasefile_path = g_steal_pointer (&statedir_path);
+		return TRUE;
+	}
+
+	if (nm_config_get_configure_and_quit (nm_config_get ()) == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD)
+		*out_leasefile_path = g_steal_pointer (&rundir_path);
+	else
+		*out_leasefile_path = g_steal_pointer (&statedir_path);
+	return FALSE;
+}
diff --git a/src/dhcp/nm-dhcp-utils.h b/src/dhcp/nm-dhcp-utils.h
index 39ae7693..e4c33314 100644
--- a/src/dhcp/nm-dhcp-utils.h
+++ b/src/dhcp/nm-dhcp-utils.h
@@ -1,17 +1,5 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 Red Hat, Inc.
  */
 
@@ -40,7 +28,13 @@ NMPlatformIP6Address nm_dhcp_utils_ip6_prefix_from_options (GHashTable *options)
 
 char *nm_dhcp_utils_duid_to_string (GBytes *duid);
 
-GBytes *     nm_dhcp_utils_client_id_string_to_bytes (const char *client_id);
+GBytes *nm_dhcp_utils_client_id_string_to_bytes (const char *client_id);
+
+gboolean nm_dhcp_utils_get_leasefile_path (int addr_family,
+                                           const char *plugin_name,
+                                           const char *iface,
+                                           const char *uuid,
+                                           char **out_leasefile_path);
 
 #endif /* __NETWORKMANAGER_DHCP_UTILS_H__ */
 
diff --git a/src/dhcp/tests/meson.build b/src/dhcp/tests/meson.build
index 43b33951..031e2efd 100644
--- a/src/dhcp/tests/meson.build
+++ b/src/dhcp/tests/meson.build
@@ -7,7 +7,8 @@ foreach test_unit: test_units
   exe = executable(
     test_unit,
     test_unit + '.c',
-    dependencies: test_nm_dep,
+    dependencies: libnetwork_manager_test_dep,
+    c_args: test_c_flags,
   )
 
   test(
diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c
index 9e51fceb..761f9cdb 100644
--- a/src/dhcp/tests/test-dhcp-dhclient.c
+++ b/src/dhcp/tests/test-dhcp-dhclient.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * Copyright (C) 2010 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"
@@ -43,6 +29,7 @@ test_config (const char *orig,
              const char *hostname,
              guint32 timeout,
              gboolean use_fqdn,
+             NMDhcpHostnameFlags hostname_flags,
              const char *dhcp_client_id,
              GBytes *expected_new_client_id,
              const char *iface,
@@ -64,6 +51,7 @@ test_config (const char *orig,
 	                                      hostname,
 	                                      timeout,
 	                                      use_fqdn,
+	                                      hostname_flags,
 	                                      "/path/to/dhclient.conf",
 	                                      orig,
 	                                      &new_client_id);
@@ -108,7 +96,11 @@ static const char *orig_missing_expected = \
 static void
 test_orig_missing (void)
 {
-	test_config (NULL, orig_missing_expected, AF_INET, NULL, 0, FALSE, NULL, NULL, "eth0", NULL);
+	test_config (NULL,
+	             orig_missing_expected,
+	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
+	             NULL, NULL, "eth0", NULL);
 }
 
 /*****************************************************************************/
@@ -139,6 +131,7 @@ test_override_client_id (void)
 {
 	test_config (override_client_id_orig, override_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             "11:22:33:44:55:66",
 	             NULL,
 	             "eth0",
@@ -169,6 +162,7 @@ test_quote_client_id (void)
 {
 	test_config (NULL, quote_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             "abcd",
 	             NULL,
 	             "eth0",
@@ -199,6 +193,7 @@ test_quote_client_id_2 (void)
 {
 	test_config (NULL, quote_client_id_expected_2,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             "a\\bc",
 	             NULL,
 	             "eth0",
@@ -229,6 +224,7 @@ test_hex_zero_client_id (void)
 {
 	test_config (NULL, hex_zero_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             "00:11:22:33",
 	             NULL,
 	             "eth0",
@@ -259,6 +255,7 @@ test_ascii_client_id (void)
 {
 	test_config (NULL, ascii_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             "qb:cd:ef:12:34:56",
 	             NULL,
 	             "eth0",
@@ -289,6 +286,7 @@ test_hex_single_client_id (void)
 {
 	test_config (NULL, hex_single_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             "ab:cd:e:12:34:56",
 	             NULL,
 	             "eth0",
@@ -327,6 +325,7 @@ test_existing_hex_client_id (void)
 	new_client_id = g_bytes_new (bytes, sizeof (bytes));
 	test_config (existing_hex_client_id_orig, existing_hex_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             new_client_id,
 	             "eth0",
@@ -364,6 +363,7 @@ test_existing_escaped_client_id (void)
 	new_client_id = g_bytes_new ("$test\xfe", 6);
 	test_config (existing_escaped_client_id_orig, existing_escaped_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             new_client_id,
 	             "eth0",
@@ -405,6 +405,7 @@ test_existing_ascii_client_id (void)
 	new_client_id = g_bytes_new (buf, sizeof (buf));
 	test_config (existing_ascii_client_id_orig, existing_ascii_client_id_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             new_client_id,
 	             "eth0",
@@ -417,7 +418,8 @@ static const char *fqdn_expected = \
 	"\n"
 	"send fqdn.fqdn \"foo.bar.com\"; # added by NetworkManager\n"
 	"send fqdn.encoded on;\n"
-	"send fqdn.server-update on;\n"
+	"send fqdn.server-update off;\n"
+	"send fqdn.no-client-update on;\n"
 	"\n"
 	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
 	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
@@ -435,7 +437,10 @@ test_fqdn (void)
 {
 	test_config (NULL, fqdn_expected,
 	             AF_INET, "foo.bar.com", 0,
-	             TRUE, NULL,
+	             TRUE,
+	               NM_DHCP_HOSTNAME_FLAG_FQDN_ENCODED
+	             | NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE,
+	             NULL,
 	             NULL,
 	             "eth0",
 	             NULL);
@@ -452,8 +457,9 @@ static const char *fqdn_options_override_expected = \
 	"# Merged from /path/to/dhclient.conf\n"
 	"\n"
 	"send fqdn.fqdn \"example2.com\"; # added by NetworkManager\n"
-	"send fqdn.encoded on;\n"
+	"send fqdn.encoded off;\n"
 	"send fqdn.server-update on;\n"
+	"send fqdn.no-client-update off;\n"
 	"\n"
 	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
 	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
@@ -476,6 +482,7 @@ test_fqdn_options_override (void)
 	test_config (fqdn_options_override_orig,
 	             fqdn_options_override_expected,
 	             AF_INET, "example2.com", 0,
+	             NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE,
 	             TRUE, NULL,
 	             NULL,
 	             "eth0",
@@ -510,6 +517,7 @@ test_override_hostname (void)
 {
 	test_config (override_hostname_orig, override_hostname_expected,
 	             AF_INET, "blahblah", 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -538,6 +546,7 @@ test_override_hostname6 (void)
 {
 	test_config (override_hostname6_orig, override_hostname6_expected,
 	             AF_INET6, "blahblah.local", 0, TRUE,
+	             NM_DHCP_HOSTNAME_FLAG_FQDN_SERV_UPDATE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -550,7 +559,7 @@ static const char *nonfqdn_hostname6_expected = \
 	"# Created by NetworkManager\n"
 	"\n"
 	"send fqdn.fqdn \"blahblah\"; # added by NetworkManager\n"
-	"send fqdn.server-update on;\n"
+	"send fqdn.no-client-update on;\n"
 	"\n"
 	"also request dhcp6.name-servers;\n"
 	"also request dhcp6.domain-search;\n"
@@ -563,6 +572,7 @@ test_nonfqdn_hostname6 (void)
 	/* Non-FQDN hostname can now be used with dhclient */
 	test_config (NULL, nonfqdn_hostname6_expected,
 	             AF_INET6, "blahblah", 0, TRUE,
+	             NM_DHCP_HOSTNAME_FLAG_FQDN_NO_UPDATE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -599,6 +609,7 @@ test_existing_alsoreq (void)
 {
 	test_config (existing_alsoreq_orig, existing_alsoreq_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -638,6 +649,7 @@ test_existing_req (void)
 {
 	test_config (existing_req_orig, existing_req_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -678,6 +690,7 @@ test_existing_multiline_alsoreq (void)
 {
 	test_config (existing_multiline_alsoreq_orig, existing_multiline_alsoreq_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -917,6 +930,7 @@ test_interface1 (void)
 {
 	test_config (interface1_orig, interface1_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -963,6 +977,7 @@ test_interface2 (void)
 {
 	test_config (interface2_orig, interface2_expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             NULL,
 	             "eth1",
@@ -1074,6 +1089,7 @@ test_structured (void)
 	new_client_id = g_bytes_new (bytes, sizeof (bytes) - 1);
 	test_config (orig, expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             new_client_id,
 	             "eth0",
@@ -1129,6 +1145,7 @@ test_config_req_intf (void)
 
 	test_config (orig, expected,
 	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
 	             NULL,
 	             NULL,
 	             "eth0",
diff --git a/src/dhcp/tests/test-dhcp-utils.c b/src/dhcp/tests/test-dhcp-utils.c
index e4d4c348..d0389069 100644
--- a/src/dhcp/tests/test-dhcp-utils.c
+++ b/src/dhcp/tests/test-dhcp-utils.c
@@ -1,19 +1,6 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 - 2014 Red Hat, Inc.
- *
  */
 
 #include "nm-default.h"