summary refs log tree commit diff
path: root/src/dhcp
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2020-06-28 18:58:14 +0200
committerMichael Biebl <biebl@debian.org>2020-06-28 18:58:14 +0200
commita54ac63bbf9b2c71026ac9028a8ffaf186cf3c82 (patch)
tree6a32883bd916c4096357b35298beff6db8bcd0b5 /src/dhcp
parent45e8e1149027529194982212c804c0468aa01d98 (diff)
New upstream version 1.25.90 upstream/1.25.90
Diffstat (limited to 'src/dhcp')
-rw-r--r--src/dhcp/nm-dhcp-client.c21
-rw-r--r--src/dhcp/nm-dhcp-client.h2
-rw-r--r--src/dhcp/nm-dhcp-dhclient-utils.c22
-rw-r--r--src/dhcp/nm-dhcp-dhclient-utils.h1
-rw-r--r--src/dhcp/nm-dhcp-dhclient.c6
-rw-r--r--src/dhcp/nm-dhcp-helper.c47
-rw-r--r--src/dhcp/nm-dhcp-manager.c6
-rw-r--r--src/dhcp/nm-dhcp-manager.h2
-rw-r--r--src/dhcp/nm-dhcp-nettools.c96
-rw-r--r--src/dhcp/nm-dhcp-options.c2
-rw-r--r--src/dhcp/nm-dhcp-options.h2
-rw-r--r--src/dhcp/nm-dhcp-systemd.c23
-rw-r--r--src/dhcp/tests/test-dhcp-dhclient.c59
13 files changed, 230 insertions, 59 deletions
diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c
index 90a64aca..f7324baf 100644
--- a/src/dhcp/nm-dhcp-client.c
+++ b/src/dhcp/nm-dhcp-client.c
@@ -50,6 +50,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDhcpClient,
 	PROP_IAID_EXPLICIT,
 	PROP_HOSTNAME,
 	PROP_HOSTNAME_FLAGS,
+	PROP_MUD_URL,
 );
 
 typedef struct _NMDhcpClientPrivate {
@@ -60,6 +61,7 @@ typedef struct _NMDhcpClientPrivate {
 	char *       uuid;
 	GBytes *     client_id;
 	char *       hostname;
+	char *       mud_url;
 	pid_t        pid;
 	guint        timeout_id;
 	guint        watch_id;
@@ -312,6 +314,14 @@ nm_dhcp_client_get_use_fqdn (NMDhcpClient *self)
 	return NM_DHCP_CLIENT_GET_PRIVATE (self)->use_fqdn;
 }
 
+const char *
+nm_dhcp_client_get_mud_url (NMDhcpClient *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->mud_url;
+}
+
 /*****************************************************************************/
 
 static const char *state_table[NM_DHCP_STATE_MAX + 1] = {
@@ -1052,6 +1062,10 @@ set_property (GObject *object, guint prop_id,
 		/* construct-only */
 		priv->hostname_flags = g_value_get_uint (value);
 		break;
+	case PROP_MUD_URL:
+		/* construct-only */
+		priv->mud_url = g_value_dup_string (value);
+		break;
 	case PROP_ROUTE_TABLE:
 		priv->route_table = g_value_get_uint (value);
 		break;
@@ -1102,6 +1116,7 @@ dispose (GObject *object)
 	nm_clear_g_free (&priv->iface);
 	nm_clear_g_free (&priv->hostname);
 	nm_clear_g_free (&priv->uuid);
+	nm_clear_g_free (&priv->mud_url);
 	nm_clear_pointer (&priv->client_id, g_bytes_unref);
 	nm_clear_pointer (&priv->hwaddr, g_bytes_unref);
 	nm_clear_pointer (&priv->bcast_hwaddr, g_bytes_unref);
@@ -1191,6 +1206,12 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class)
 	                       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
 	                       G_PARAM_STATIC_STRINGS);
 
+	obj_properties[PROP_MUD_URL] =
+	    g_param_spec_string (NM_DHCP_CLIENT_MUD_URL, "", "",
+	                         NULL,
+	                         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 1ff09067..479ea000 100644
--- a/src/dhcp/nm-dhcp-client.h
+++ b/src/dhcp/nm-dhcp-client.h
@@ -30,6 +30,7 @@
 #define NM_DHCP_CLIENT_INTERFACE        "iface"
 #define NM_DHCP_CLIENT_MULTI_IDX        "multi-idx"
 #define NM_DHCP_CLIENT_HOSTNAME         "hostname"
+#define NM_DHCP_CLIENT_MUD_URL          "mud-url"
 #define NM_DHCP_CLIENT_ROUTE_METRIC     "route-metric"
 #define NM_DHCP_CLIENT_ROUTE_TABLE      "route-table"
 #define NM_DHCP_CLIENT_TIMEOUT          "timeout"
@@ -141,6 +142,7 @@ 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);
+const char *nm_dhcp_client_get_mud_url (NMDhcpClient *self);
 
 NMDhcpHostnameFlags nm_dhcp_client_get_hostname_flags (NMDhcpClient *self);
 
diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c
index da28abad..76fc1a89 100644
--- a/src/dhcp/nm-dhcp-dhclient-utils.c
+++ b/src/dhcp/nm-dhcp-dhclient-utils.c
@@ -33,6 +33,13 @@
 #define ALSOREQ_TAG "also request "
 #define REQ_TAG "request "
 
+#define MUDURLv4_DEF    "option mudurl code 161 = text;\n"
+#define MUDURLv4_FMT    "send mudurl \"%s\";\n"
+
+#define MUDURLv6_DEF    "option dhcp6.mudurl code 112 = text;\n"
+#define MUDURLv6_FMT    "send dhcp6.mudurl \"%s\";\n"
+
+
 static void
 add_request (GPtrArray *array, const char *item)
 {
@@ -183,6 +190,19 @@ add_hostname6 (GString *str,
 	}
 }
 
+static void add_mud_url_config(GString *str, const char *mud_url, int addr_family)
+{
+	if (mud_url) {
+		if (addr_family == AF_INET) {
+			g_string_append (str, MUDURLv4_DEF);
+			g_string_append_printf (str, MUDURLv4_FMT, mud_url);
+		} else {
+			g_string_append (str, MUDURLv6_DEF);
+			g_string_append_printf (str, MUDURLv6_FMT, mud_url);
+		}
+	}
+}
+
 static GBytes *
 read_client_id (const char *str)
 {
@@ -286,6 +306,7 @@ nm_dhcp_dhclient_create_config (const char *interface,
                                 guint32 timeout,
                                 gboolean use_fqdn,
                                 NMDhcpHostnameFlags hostname_flags,
+                                const char *mud_url,
                                 const char *orig_path,
                                 const char *orig_contents,
                                 GBytes **out_new_client_id)
@@ -451,6 +472,7 @@ nm_dhcp_dhclient_create_config (const char *interface,
 		g_string_append_printf (new_contents, "timeout %u;\n", timeout);
 	}
 
+	add_mud_url_config (new_contents, mud_url, addr_family);
 	if (addr_family == AF_INET) {
 		add_ip4_config (new_contents, client_id, hostname, use_fqdn, hostname_flags);
 		add_request (reqs, "rfc3442-classless-static-routes");
diff --git a/src/dhcp/nm-dhcp-dhclient-utils.h b/src/dhcp/nm-dhcp-dhclient-utils.h
index 0cf53887..5094d614 100644
--- a/src/dhcp/nm-dhcp-dhclient-utils.h
+++ b/src/dhcp/nm-dhcp-dhclient-utils.h
@@ -17,6 +17,7 @@ char *nm_dhcp_dhclient_create_config (const char *interface,
                                       guint32 timeout,
                                       gboolean use_fqdn,
                                       NMDhcpHostnameFlags hostname_flags,
+                                      const char *mud_url,
                                       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 617ce236..4ad056e5 100644
--- a/src/dhcp/nm-dhcp-dhclient.c
+++ b/src/dhcp/nm-dhcp-dhclient.c
@@ -147,6 +147,7 @@ merge_dhclient_config (NMDhcpDhclient *self,
                        guint32 timeout,
                        gboolean use_fqdn,
                        NMDhcpHostnameFlags hostname_flags,
+                       const char *mud_url,
                        const char *orig_path,
                        GBytes **out_new_client_id,
                        GError **error)
@@ -176,6 +177,7 @@ merge_dhclient_config (NMDhcpDhclient *self,
 	                                      timeout,
 	                                      use_fqdn,
 	                                      hostname_flags,
+	                                      mud_url,
 	                                      orig_path,
 	                                      orig,
 	                                      out_new_client_id);
@@ -268,6 +270,7 @@ create_dhclient_config (NMDhcpDhclient *self,
                         guint32 timeout,
                         gboolean use_fqdn,
                         NMDhcpHostnameFlags hostname_flags,
+                        const char *mud_url,
                         GBytes **out_new_client_id)
 {
 	gs_free char *orig = NULL;
@@ -296,6 +299,7 @@ create_dhclient_config (NMDhcpDhclient *self,
 	                            timeout,
 	                            use_fqdn,
 	                            hostname_flags,
+	                            mud_url,
 	                            orig,
 	                            out_new_client_id,
 	                            &error)) {
@@ -496,6 +500,7 @@ ip4_start (NMDhcpClient *client,
 	                                          nm_dhcp_client_get_timeout (client),
 	                                          nm_dhcp_client_get_use_fqdn (client),
 	                                          nm_dhcp_client_get_hostname_flags (client),
+	                                          nm_dhcp_client_get_mud_url (client),
 	                                          &new_client_id);
 	if (!priv->conf_file) {
 		nm_utils_error_set_literal (error,
@@ -540,6 +545,7 @@ ip6_start (NMDhcpClient *client,
 	                                          nm_dhcp_client_get_timeout (client),
 	                                          TRUE,
 	                                          nm_dhcp_client_get_hostname_flags (client),
+	                                          nm_dhcp_client_get_mud_url (client),
 	                                          NULL);
 	if (!priv->conf_file) {
 		nm_utils_error_set_literal (error,
diff --git a/src/dhcp/nm-dhcp-helper.c b/src/dhcp/nm-dhcp-helper.c
index 365e32e3..071d331b 100644
--- a/src/dhcp/nm-dhcp-helper.c
+++ b/src/dhcp/nm-dhcp-helper.c
@@ -46,33 +46,46 @@
 
 /*****************************************************************************/
 
-static const char * ignore[] = {"PATH", "SHLVL", "_", "PWD", "dhc_dbus", NULL};
-
 static GVariant *
 build_signal_parameters (void)
 {
-	char **item;
+	const char *const*environ_iter;
 	GVariantBuilder builder;
 
 	g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
 
 	/* List environment and format for dbus dict */
-	for (item = environ; *item; item++) {
-		char *name, *val, **p;
-
-		/* Split on the = */
-		name = g_strdup (*item);
-		val = strchr (name, '=');
-		if (!val || val == name)
-			goto next;
-		*val++ = '\0';
-
-		/* Ignore non-DCHP-related environment variables */
-		for (p = (char **) ignore; *p; p++) {
+	for (environ_iter = (const char *const*) environ; *environ_iter; environ_iter++) {
+		static const char *const ignore_with_prefix_list[] = {
+			"PATH",
+			"SHLVL",
+			"_",
+			"PWD",
+			"dhc_dbus",
+			NULL
+		};
+		const char *item = *environ_iter;
+		gs_free char *name = NULL;
+		const char *val;
+		const char *const*p;
+
+		val = strchr (item, '=');
+		if (   !val
+		    || item == val)
+			continue;
+
+		name = g_strndup (item, val - item);
+		val += 1;
+
+		/* Ignore non-DHCP-related environment variables */
+		for (p = ignore_with_prefix_list; *p; p++) {
 			if (strncmp (name, *p, strlen (*p)) == 0)
 				goto next;
 		}
 
+		if (!g_utf8_validate (name, -1, NULL))
+			continue;
+
 		/* Value passed as a byte array rather than a string, because there are
 		 * no character encoding guarantees with DHCP, and D-Bus requires
 		 * strings to be UTF-8.
@@ -85,8 +98,8 @@ build_signal_parameters (void)
 		                       g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
 		                                                  val, strlen (val), 1));
 
-	next:
-		g_free (name);
+next:
+		;
 	}
 
 	return g_variant_ref_sink (g_variant_new ("(a{sv})", &builder));
diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c
index 333744dd..7565bfc8 100644
--- a/src/dhcp/nm-dhcp-manager.c
+++ b/src/dhcp/nm-dhcp-manager.c
@@ -220,6 +220,7 @@ client_start (NMDhcpManager *self,
               const char *hostname,
               gboolean hostname_use_fqdn,
               NMDhcpHostnameFlags hostname_flags,
+              const char *mud_url,
               gboolean info_only,
               NMSettingIP6ConfigPrivacy privacy,
               const char *last_ip4_address,
@@ -303,6 +304,7 @@ client_start (NMDhcpManager *self,
 	                       NM_DHCP_CLIENT_IAID, (guint) iaid,
 	                       NM_DHCP_CLIENT_IAID_EXPLICIT, iaid_explicit,
 	                       NM_DHCP_CLIENT_HOSTNAME, hostname,
+	                       NM_DHCP_CLIENT_MUD_URL, mud_url,
 	                       NM_DHCP_CLIENT_ROUTE_TABLE, (guint) route_table,
 	                       NM_DHCP_CLIENT_ROUTE_METRIC, (guint) route_metric,
 	                       NM_DHCP_CLIENT_TIMEOUT, (guint) timeout,
@@ -382,6 +384,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
                            const char *dhcp_hostname,
                            const char *dhcp_fqdn,
                            NMDhcpHostnameFlags hostname_flags,
+                           const char *mud_url,
                            GBytes *dhcp_client_id,
                            guint32 timeout,
                            const char *dhcp_anycast_addr,
@@ -440,6 +443,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
 	                     hostname,
 	                     use_fqdn,
 	                     hostname_flags,
+	                     mud_url,
 	                     FALSE,
 	                     0,
 	                     last_ip_address,
@@ -462,6 +466,7 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
                            gboolean send_hostname,
                            const char *dhcp_hostname,
                            NMDhcpHostnameFlags hostname_flags,
+                           const char *mud_url,
                            GBytes *duid,
                            gboolean enforce_duid,
                            guint32 iaid,
@@ -503,6 +508,7 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
 	                     hostname,
 	                     TRUE,
 	                     hostname_flags,
+	                     mud_url,
 	                     info_only,
 	                     privacy,
 	                     NULL,
diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h
index 1b793c22..88435282 100644
--- a/src/dhcp/nm-dhcp-manager.h
+++ b/src/dhcp/nm-dhcp-manager.h
@@ -43,6 +43,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip4     (NMDhcpManager *manager,
                                               const char *dhcp_hostname,
                                               const char *dhcp_fqdn,
                                               NMDhcpHostnameFlags hostname_flags,
+                                              const char *mud_url,
                                               GBytes *dhcp_client_id,
                                               guint32 timeout,
                                               const char *dhcp_anycast_addr,
@@ -62,6 +63,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip6     (NMDhcpManager *manager,
                                               gboolean send_hostname,
                                               const char *dhcp_hostname,
                                               NMDhcpHostnameFlags hostname_flags,
+                                              const char *mud_url,
                                               GBytes *duid,
                                               gboolean enforce_duid,
                                               guint32 iaid,
diff --git a/src/dhcp/nm-dhcp-nettools.c b/src/dhcp/nm-dhcp-nettools.c
index 00b416da..fe6a4582 100644
--- a/src/dhcp/nm-dhcp-nettools.c
+++ b/src/dhcp/nm-dhcp-nettools.c
@@ -68,6 +68,20 @@ G_DEFINE_TYPE (NMDhcpNettools, nm_dhcp_nettools, NM_TYPE_DHCP_CLIENT)
 
 /*****************************************************************************/
 
+static void
+set_error_nettools (GError **error, int r, const char *message)
+{
+	/* the error code returned from n_dhcp4_* API is either a negative
+	 * errno, or a positive internal error code. Generate different messages
+	 * for these. */
+	if (r < 0)
+		nm_utils_error_set_errno (error, r, "%s: %s", message);
+	else
+		nm_utils_error_set (error, r, "%s (code %d)", message, r);
+}
+
+/*****************************************************************************/
+
 #define DHCP_MAX_FQDN_LENGTH 255
 
 enum {
@@ -681,11 +695,10 @@ lease_parse_metered (NDhcp4ClientLease *lease,
 	int r;
 
 	r = n_dhcp4_client_lease_query (lease, NM_DHCP_OPTION_DHCP4_VENDOR_SPECIFIC, &data, &n_data);
-	if (r) {
+	if (r)
 		metered = FALSE;
-	} else {
+	else
 		metered = !!memmem (data, n_data, "ANDROID_METERED", NM_STRLEN ("ANDROID_METERED"));
-	}
 
 	/* TODO: expose the vendor specific option when present */
 	nm_ip4_config_set_metered (ip4_config, metered);
@@ -1023,9 +1036,8 @@ dhcp4_event_handle (NMDhcpNettools *self,
 	case N_DHCP4_CLIENT_EVENT_OFFER:
 		/* always accept the first lease */
 		r = n_dhcp4_client_lease_select (event->offer.lease);
-		if (r) {
+		if (r)
 			_LOGW ("selecting lease failed: %d", r);
-		}
 		break;
 	case N_DHCP4_CLIENT_EVENT_RETRACTED:
 	case N_DHCP4_CLIENT_EVENT_EXPIRED:
@@ -1044,6 +1056,18 @@ dhcp4_event_handle (NMDhcpNettools *self,
 	case N_DHCP4_CLIENT_EVENT_DOWN:
 		/* ignore down events, they are purely informational */
 		break;
+	case N_DHCP4_CLIENT_EVENT_LOG: {
+			NMLogLevel nm_level;
+
+			nm_level = nm_log_level_from_syslog (event->log.level);
+			if (nm_logging_enabled (nm_level, LOGD_DHCP4)) {
+				nm_log (nm_level, LOGD_DHCP4, NULL , NULL,
+				        "dhcp4 (%s): %s",
+				        nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self)),
+				        event->log.message);
+			}
+		}
+		break;
 	default:
 		_LOGW ("unhandled DHCP event %d", event->event);
 		break;
@@ -1084,27 +1108,6 @@ dhcp4_event_cb (int fd,
 	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,
@@ -1170,12 +1173,10 @@ nettools_create (NMDhcpNettools *self,
 
 	r = n_dhcp4_client_config_new (&config);
 	if (r) {
-		nm_utils_error_set_errno (error, r, "failed to create client-config: %s");
+		set_error_nettools (error, r, "failed to create client-config");
 		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);
@@ -1184,19 +1185,21 @@ nettools_create (NMDhcpNettools *self,
 	                                         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");
+		set_error_nettools (error, r, "failed to set client-id");
 		return FALSE;
 	}
 
 	r = n_dhcp4_client_new (&client, config);
 	if (r) {
-		nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, "failed to create client: error %d", r);
+		set_error_nettools (error, r, "failed to create client");
 		return FALSE;
 	}
 
 	priv->client = client;
 	client = NULL;
 
+	n_dhcp4_client_set_log_level (priv->client, nm_log_level_to_syslog (nm_logging_get_level (LOGD_DHCP4)));
+
 	n_dhcp4_client_get_fd (priv->client, &fd);
 
 	priv->event_source = nm_g_unix_fd_source_new (fd,
@@ -1224,7 +1227,7 @@ _accept (NMDhcpClient *client,
 
 	r = n_dhcp4_client_lease_accept (priv->lease);
 	if (r) {
-		nm_utils_error_set_errno (error, r, "failed to accept lease: %s");
+		set_error_nettools (error, r, "failed to accept lease");
 		return FALSE;
 	}
 
@@ -1248,7 +1251,7 @@ decline (NMDhcpClient *client,
 
 	r = n_dhcp4_client_lease_decline (priv->lease, error_message);
 	if (r) {
-		nm_utils_error_set_errno (error, r, "failed to decline lease: %s");
+		set_error_nettools (error, r, "failed to decline lease");
 		return FALSE;
 	}
 
@@ -1285,6 +1288,7 @@ ip4_start (NMDhcpClient *client,
 	gs_free char *lease_file = NULL;
 	struct in_addr last_addr = { 0 };
 	const char *hostname;
+	const char *mud_url;
 	int r, i;
 
 	g_return_val_if_fail (!priv->probe, FALSE);
@@ -1294,7 +1298,7 @@ ip4_start (NMDhcpClient *client,
 
 	r = n_dhcp4_client_probe_config_new (&config);
 	if (r) {
-		nm_utils_error_set_errno (error, r, "failed to create dhcp-client-probe-config: %s");
+		set_error_nettools (error, r, "failed to create dhcp-client-probe-config");
 		return FALSE;
 	}
 
@@ -1341,6 +1345,17 @@ ip4_start (NMDhcpClient *client,
 		}
 	}
 
+	mud_url = nm_dhcp_client_get_mud_url (client);
+	if (mud_url) {
+		r = n_dhcp4_client_probe_config_append_option (config,
+		                                               NM_DHCP_OPTION_DHCP4_MUD_URL,
+		                                               mud_url,
+		                                               strlen (mud_url));
+		if (r) {
+			set_error_nettools (error, r, "failed to set MUD URL");
+			return FALSE;
+		}
+	}
 	hostname = nm_dhcp_client_get_hostname (client);
 	if (hostname) {
 		if (nm_dhcp_client_get_use_fqdn (client)) {
@@ -1359,7 +1374,10 @@ ip4_start (NMDhcpClient *client,
 				                                   sizeof (buffer) - 3,
 				                                   FALSE);
 				if (r <= 0) {
-					nm_utils_error_set_errno (error, r, "failed to convert DHCP FQDN: %s");
+					if (r < 0)
+						nm_utils_error_set_errno (error, r, "failed to convert DHCP FQDN: %s");
+					else
+						nm_utils_error_set (error, r, "failed to convert DHCP FQDN");
 					return FALSE;
 				}
 				fqdn_len = r;
@@ -1377,7 +1395,7 @@ ip4_start (NMDhcpClient *client,
 			                                               buffer,
 			                                               3 + fqdn_len);
 			if (r) {
-				nm_utils_error_set_errno (error, r, "failed to set DHCP FQDN: %s");
+				set_error_nettools (error, r, "failed to set DHCP FQDN");
 				return FALSE;
 			}
 		} else {
@@ -1386,7 +1404,7 @@ ip4_start (NMDhcpClient *client,
 			                                               hostname,
 			                                               strlen (hostname));
 			if (r) {
-				nm_utils_error_set_errno (error, r, "failed to set DHCP hostname: %s");
+				set_error_nettools (error, r, "failed to set DHCP hostname");
 				return FALSE;
 			}
 		}
@@ -1397,7 +1415,7 @@ ip4_start (NMDhcpClient *client,
 
 	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");
+		set_error_nettools (error, r, "failed to start DHCP client");
 		return FALSE;
 	}
 
@@ -1434,7 +1452,7 @@ dispose (GObject *object)
 {
 	NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (object);
 
-	nm_clear_pointer (&priv->lease_file, g_free);
+	nm_clear_g_free (&priv->lease_file);
 	nm_clear_g_source_inst (&priv->event_source);
 	nm_clear_pointer (&priv->lease, n_dhcp4_client_lease_unref);
 	nm_clear_pointer (&priv->probe, n_dhcp4_client_probe_free);
diff --git a/src/dhcp/nm-dhcp-options.c b/src/dhcp/nm-dhcp-options.c
index 1d391f3e..b10635fc 100644
--- a/src/dhcp/nm-dhcp-options.c
+++ b/src/dhcp/nm-dhcp-options.c
@@ -126,6 +126,7 @@ const NMDhcpOption _nm_dhcp_option_dhcp4_options[] = {
 	REQ (NM_DHCP_OPTION_DHCP4_TFTP_SERVER_ADDRESS,               "tftp_server_address",             FALSE ),
 	REQ (NM_DHCP_OPTION_DHCP4_V4_PORTPARAMS,                     "v4_portparams",                   FALSE ),
 	REQ (NM_DHCP_OPTION_DHCP4_V4_CAPTIVE_PORTAL,                 "v4_captive_portal",               FALSE ),
+	REQ (NM_DHCP_OPTION_DHCP4_MUD_URL,                           "mud_url",                         FALSE ),
 	REQ (NM_DHCP_OPTION_DHCP4_LOADER_CONFIGFILE,                 "loader_configfile",               FALSE ),
 	REQ (NM_DHCP_OPTION_DHCP4_LOADER_PATHPREFIX,                 "loader_pathprefix",               FALSE ),
 	REQ (NM_DHCP_OPTION_DHCP4_LOADER_REBOOTTIME,                 "loader_reboottime",               FALSE ),
@@ -182,6 +183,7 @@ const NMDhcpOption _nm_dhcp_option_dhcp6_options[] = {
 	REQ (NM_DHCP_OPTION_DHCP6_DNS_SERVERS,                      "dhcp6_name_servers",  TRUE ),
 	REQ (NM_DHCP_OPTION_DHCP6_DOMAIN_LIST,                      "dhcp6_domain_search", TRUE ),
 	REQ (NM_DHCP_OPTION_DHCP6_SNTP_SERVERS,                     "dhcp6_sntp_servers",  TRUE ),
+	REQ (NM_DHCP_OPTION_DHCP6_MUD_URL,                          "dhcp6_mud_url",       FALSE ),
 
 	/* Internal values */
 	REQ (NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS,                    "ip6_address",         FALSE ),
diff --git a/src/dhcp/nm-dhcp-options.h b/src/dhcp/nm-dhcp-options.h
index bf9ccd57..7c012170 100644
--- a/src/dhcp/nm-dhcp-options.h
+++ b/src/dhcp/nm-dhcp-options.h
@@ -110,6 +110,7 @@ typedef enum {
 	NM_DHCP_OPTION_DHCP4_TFTP_SERVER_ADDRESS            = 150,
 	NM_DHCP_OPTION_DHCP4_V4_PORTPARAMS                  = 159,
 	NM_DHCP_OPTION_DHCP4_V4_CAPTIVE_PORTAL              = 160,
+	NM_DHCP_OPTION_DHCP4_MUD_URL                        = 161,
 	NM_DHCP_OPTION_DHCP4_LOADER_CONFIGFILE              = 209,
 	NM_DHCP_OPTION_DHCP4_LOADER_PATHPREFIX              = 210,
 	NM_DHCP_OPTION_DHCP4_LOADER_REBOOTTIME              = 211,
@@ -159,6 +160,7 @@ typedef enum {
 	NM_DHCP_OPTION_DHCP6_DNS_SERVERS       = 23,
 	NM_DHCP_OPTION_DHCP6_DOMAIN_LIST       = 24,
 	NM_DHCP_OPTION_DHCP6_SNTP_SERVERS      = 31,
+	NM_DHCP_OPTION_DHCP6_MUD_URL           = 112,
 	/* Internal values */
 	NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS     = 1026,
 	NM_DHCP_OPTION_DHCP6_NM_PREFIXLEN      = 1027,
diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c
index 23862e9e..f65937d8 100644
--- a/src/dhcp/nm-dhcp-systemd.c
+++ b/src/dhcp/nm-dhcp-systemd.c
@@ -573,6 +573,7 @@ ip4_start (NMDhcpClient *client,
 	size_t client_id_len;
 	struct in_addr last_addr = { 0 };
 	const char *hostname;
+	const char *mud_url;
 	int r, i;
 
 	g_return_val_if_fail (!priv->client4, FALSE);
@@ -688,6 +689,15 @@ ip4_start (NMDhcpClient *client,
 		}
 	}
 
+	mud_url = nm_dhcp_client_get_mud_url (client);
+	if (mud_url) {
+		r = sd_dhcp_client_set_mud_url (sd_client, mud_url);
+		if (r < 0) {
+			nm_utils_error_set_errno (error, r, "failed to set DHCP MUDURL: %s");
+			return FALSE;
+		}
+	}
+
 	r = sd_dhcp_client_set_callback (sd_client, dhcp_event_cb, client);
 	if (r < 0) {
 		nm_utils_error_set_errno (error, r, "failed to set callback: %s");
@@ -725,7 +735,8 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
 {
 	gs_unref_object NMIP6Config *ip6_config = NULL;
 	gs_unref_hashtable GHashTable *options = NULL;
-	struct in6_addr tmp_addr, *dns;
+	struct in6_addr tmp_addr;
+	const struct in6_addr *dns;
 	uint32_t lft_pref, lft_valid;
 	char addr_str[NM_UTILS_INET_ADDRSTRLEN];
 	char **domains;
@@ -894,6 +905,7 @@ ip6_start (NMDhcpClient *client,
 	nm_auto (sd_dhcp6_client_unrefp) sd_dhcp6_client *sd_client = NULL;
 	GBytes *hwaddr;
 	const char *hostname;
+	const char *mud_url;
 	int r, i;
 	const guint8 *duid_arr;
 	gsize duid_len;
@@ -976,6 +988,15 @@ ip6_start (NMDhcpClient *client,
 		}
 	}
 
+	mud_url = nm_dhcp_client_get_mud_url (client);
+	if (mud_url) {
+		r = sd_dhcp6_client_set_request_mud_url (sd_client, mud_url);
+		if (r < 0) {
+			nm_utils_error_set_errno (error, r, "failed to set mud-url: %s");
+			return FALSE;
+		}
+	}
+
 	if (needed_prefixes > 0) {
 		if (needed_prefixes > 1)
 			_LOGW ("dhcp-client6: only one prefix request is supported");
diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c
index 761f9cdb..d51bcae5 100644
--- a/src/dhcp/tests/test-dhcp-dhclient.c
+++ b/src/dhcp/tests/test-dhcp-dhclient.c
@@ -21,6 +21,7 @@
 #include "nm-test-utils-core.h"
 
 #define TEST_DIR             NM_BUILD_SRCDIR"/src/dhcp/tests"
+#define TEST_MUDURL          "https://example.com/mud.json"
 
 static void
 test_config (const char *orig,
@@ -33,7 +34,8 @@ test_config (const char *orig,
              const char *dhcp_client_id,
              GBytes *expected_new_client_id,
              const char *iface,
-             const char *anycast_addr)
+             const char *anycast_addr,
+             const char *mud_url)
 {
 	gs_free char *new = NULL;
 	gs_unref_bytes GBytes *client_id = NULL;
@@ -52,6 +54,7 @@ test_config (const char *orig,
 	                                      timeout,
 	                                      use_fqdn,
 	                                      hostname_flags,
+	                                      mud_url,
 	                                      "/path/to/dhclient.conf",
 	                                      orig,
 	                                      &new_client_id);
@@ -100,7 +103,37 @@ test_orig_missing (void)
 	             orig_missing_expected,
 	             AF_INET, NULL, 0, FALSE,
 	             NM_DHCP_HOSTNAME_FLAG_NONE,
-	             NULL, NULL, "eth0", NULL);
+	             NULL, NULL, "eth0", NULL, NULL);
+}
+
+/*****************************************************************************/
+
+
+static const char *orig_missing_add_mud_url_expected = \
+	"# Created by NetworkManager\n"
+	"\n"
+	"option mudurl code 161 = text;\n"
+	"send mudurl \"https://example.com/mud.json\";\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"
+	"option wpad code 252 = string;\n"
+	"\n"
+	"also request rfc3442-classless-static-routes;\n"
+	"also request ms-classless-static-routes;\n"
+	"also request static-routes;\n"
+	"also request wpad;\n"
+	"also request ntp-servers;\n"
+	"also request root-path;\n"
+	"\n";
+
+static void
+test_orig_missing_add_mud_url (void)
+{
+	test_config (NULL,
+	             orig_missing_add_mud_url_expected,
+	             AF_INET, NULL, 0, FALSE,
+	             NM_DHCP_HOSTNAME_FLAG_NONE,
+	             NULL, NULL, "eth0", NULL, TEST_MUDURL);
 }
 
 /*****************************************************************************/
@@ -135,6 +168,7 @@ test_override_client_id (void)
 	             "11:22:33:44:55:66",
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -166,6 +200,7 @@ test_quote_client_id (void)
 	             "abcd",
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -197,6 +232,7 @@ test_quote_client_id_2 (void)
 	             "a\\bc",
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -228,6 +264,7 @@ test_hex_zero_client_id (void)
 	             "00:11:22:33",
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -259,6 +296,7 @@ test_ascii_client_id (void)
 	             "qb:cd:ef:12:34:56",
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -290,6 +328,7 @@ test_hex_single_client_id (void)
 	             "ab:cd:e:12:34:56",
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -329,6 +368,7 @@ test_existing_hex_client_id (void)
 	             NULL,
 	             new_client_id,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -367,6 +407,7 @@ test_existing_escaped_client_id (void)
 	             NULL,
 	             new_client_id,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -409,6 +450,7 @@ test_existing_ascii_client_id (void)
 	             NULL,
 	             new_client_id,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 /*****************************************************************************/
@@ -443,6 +485,7 @@ test_fqdn (void)
 	             NULL,
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -486,6 +529,7 @@ test_fqdn_options_override (void)
 	             TRUE, NULL,
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -521,6 +565,7 @@ test_override_hostname (void)
 	             NULL,
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -550,6 +595,7 @@ test_override_hostname6 (void)
 	             NULL,
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -576,6 +622,7 @@ test_nonfqdn_hostname6 (void)
 	             NULL,
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -613,6 +660,7 @@ test_existing_alsoreq (void)
 	             NULL,
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -653,6 +701,7 @@ test_existing_req (void)
 	             NULL,
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -694,6 +743,7 @@ test_existing_multiline_alsoreq (void)
 	             NULL,
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -934,6 +984,7 @@ test_interface1 (void)
 	             NULL,
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -981,6 +1032,7 @@ test_interface2 (void)
 	             NULL,
 	             NULL,
 	             "eth1",
+	             NULL,
 	             NULL);
 }
 
@@ -1093,6 +1145,7 @@ test_structured (void)
 	             NULL,
 	             new_client_id,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -1149,6 +1202,7 @@ test_config_req_intf (void)
 	             NULL,
 	             NULL,
 	             "eth0",
+	             NULL,
 	             NULL);
 }
 
@@ -1162,6 +1216,7 @@ main (int argc, char **argv)
 	nmtst_init_with_logging (&argc, &argv, NULL, "DEFAULT");
 
 	g_test_add_func ("/dhcp/dhclient/orig_missing", test_orig_missing);
+	g_test_add_func ("/dhcp/dhclient/orig_missing_add_mud_url", test_orig_missing_add_mud_url);
 	g_test_add_func ("/dhcp/dhclient/override_client_id", test_override_client_id);
 	g_test_add_func ("/dhcp/dhclient/quote_client_id/1", test_quote_client_id);
 	g_test_add_func ("/dhcp/dhclient/quote_client_id/2", test_quote_client_id_2);