From 73e152af6e3fb4f5848bfb8394484026ff119003 Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Wed, 11 May 2016 17:08:25 +0200 Subject: Imported Upstream version 1.2.2 --- src/Makefile.am | 1 + src/Makefile.in | 1 + src/devices/bluetooth/nm-bluez-device.c | 2 +- src/devices/nm-device-ethernet.c | 37 ++- src/devices/nm-device-factory.c | 10 +- src/devices/nm-device.c | 139 ++++++---- src/devices/wwan/nm-modem-broadband.c | 23 +- src/devices/wwan/nm-modem.c | 3 +- src/dhcp-manager/nm-dhcp-client.c | 58 +--- src/dhcp-manager/nm-dhcp-systemd.c | 9 +- src/dns-manager/nm-dns-dnsmasq.c | 332 +++++++++++++++++------ src/dns-manager/nm-dns-plugin.c | 106 ++++---- src/dnsmasq-manager/nm-dnsmasq-manager.c | 16 +- src/nm-auth-subject.c | 15 +- src/nm-core-utils.c | 168 ++++++++---- src/nm-core-utils.h | 5 + src/org.freedesktop.NetworkManager.conf | 10 + src/platform/nm-linux-platform.c | 5 +- src/settings/nm-settings.c | 84 ++++-- src/settings/plugins/ifnet/connection_parser.c | 11 +- src/settings/plugins/ifupdown/interface_parser.c | 16 +- src/tests/Makefile.am | 1 + src/tests/Makefile.in | 1 + src/tests/test-utils.c | 6 +- 24 files changed, 702 insertions(+), 357 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index bf668d74..aa35eefb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -208,6 +208,7 @@ libNetworkManager_base_la_CPPFLAGS = \ -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \ -DNO_SYSTEMD_JOURNAL \ -DPREFIX=\"$(prefix)\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" \ -DNMSTATEDIR=\"$(nmstatedir)\" \ $(GLIB_CFLAGS) diff --git a/src/Makefile.in b/src/Makefile.in index 1c8821f8..51ded6bc 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -994,6 +994,7 @@ libsystemd_nm_la_LIBADD = \ @ENABLE_TESTS_TRUE@ -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \ @ENABLE_TESTS_TRUE@ -DNO_SYSTEMD_JOURNAL \ @ENABLE_TESTS_TRUE@ -DPREFIX=\"$(prefix)\" \ +@ENABLE_TESTS_TRUE@ -DLOCALSTATEDIR=\"$(localstatedir)\" \ @ENABLE_TESTS_TRUE@ -DNMSTATEDIR=\"$(nmstatedir)\" \ @ENABLE_TESTS_TRUE@ $(GLIB_CFLAGS) diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c index a009746d..5ee22f93 100644 --- a/src/devices/bluetooth/nm-bluez-device.c +++ b/src/devices/bluetooth/nm-bluez-device.c @@ -788,7 +788,7 @@ static void adapter5_on_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self) { NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); - GError *error; + GError *error = NULL; GVariant *v; priv->adapter5 = g_dbus_proxy_new_for_bus_finish (res, &error); diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 6225ae7b..d27f7dca 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -194,16 +194,21 @@ _update_s390_subchannels (NMDeviceEthernet *self) } else if ( !strcmp (item, "layer2") || !strcmp (item, "portname") || !strcmp (item, "portno")) { - char *path, *value; + gs_free char *path = NULL, *value = NULL; + path = g_strdup_printf ("%s/%s", parent_path, item); value = nm_platform_sysctl_get (NM_PLATFORM_GET, path); - if (value && *value) - g_hash_table_insert (priv->s390_options, g_strdup (item), g_strdup (value)); - else + + if ( !strcmp (item, "portname") + && !g_strcmp0 (value, "no portname required")) { + /* Do nothing */ + } else if (value && *value) { + g_hash_table_insert (priv->s390_options, g_strdup (item), value); + value = NULL; + } else _LOGW (LOGD_DEVICE | LOGD_HW, "error reading %s", path); - g_free (path); - g_free (value); } + if (error) { _LOGW (LOGD_DEVICE | LOGD_HW, "%s", error->message); g_clear_error (&error); @@ -1433,7 +1438,9 @@ new_default_connection (NMDevice *self) const GSList *connections; NMSetting *setting; const char *hw_address; - char *defname, *uuid; + gs_free char *defname = NULL; + gs_free char *uuid = NULL; + gs_free char *machine_id = NULL; if (nm_config_get_no_auto_default_for_device (nm_config_get (), self)) return NULL; @@ -1448,7 +1455,19 @@ new_default_connection (NMDevice *self) connections = nm_connection_provider_get_connections (nm_connection_provider_get ()); defname = nm_device_ethernet_utils_get_default_wired_name (connections); - uuid = nm_utils_uuid_generate (); + if (!defname) + return NULL; + + machine_id = nm_utils_machine_id_read (); + + /* Create a stable UUID. The UUID is also the Network_ID for stable-privacy addr-gen-mode, + * thus when it changes we will also generate different IPv6 addresses. */ + uuid = _nm_utils_uuid_generate_from_strings ("default-wired", + machine_id ?: "", + defname, + hw_address, + NULL); + g_object_set (setting, NM_SETTING_CONNECTION_ID, defname, NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, @@ -1457,8 +1476,6 @@ new_default_connection (NMDevice *self) NM_SETTING_CONNECTION_UUID, uuid, NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL), NULL); - g_free (uuid); - g_free (defname); /* Lock the connection to the device */ setting = nm_setting_wired_new (); diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c index 482eb185..7873756f 100644 --- a/src/devices/nm-device-factory.c +++ b/src/devices/nm-device-factory.c @@ -512,20 +512,20 @@ nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc call continue; } + /* after loading glib types from the plugin, we cannot unload the library anymore. + * Make it resident. */ + g_module_make_resident (plugin); + factory = create_func (&error); if (!factory) { nm_log_warn (LOGD_HW, "(%s): failed to initialize device factory: %s", item, NM_G_ERROR_MSG (error)); g_clear_error (&error); - g_module_close (plugin); continue; } g_clear_error (&error); - if (_add_factory (factory, TRUE, g_module_name (plugin), callback, user_data)) - g_module_make_resident (plugin); - else - g_module_close (plugin); + _add_factory (factory, TRUE, g_module_name (plugin), callback, user_data); g_object_unref (factory); } diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 08cf6dce..8472ff80 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1276,7 +1276,7 @@ void nm_device_update_dynamic_ip_setup (NMDevice *self) { NMDevicePrivate *priv; - GError *error; + GError *error = NULL; gconstpointer addr; size_t addr_length; @@ -1887,6 +1887,8 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink) g_return_if_fail (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)); g_return_if_fail (priv->ip_ifindex <= 0); g_return_if_fail (priv->ip_iface == NULL); + g_return_if_fail (!priv->queued_ip4_config_id); + g_return_if_fail (!priv->queued_ip6_config_id); _LOGD (LOGD_DEVICE, "start setup of %s, kernel ifindex %d", G_OBJECT_TYPE_NAME (self), plink ? plink->ifindex : 0); @@ -3382,6 +3384,15 @@ activation_source_schedule (NMDevice *self, ActivationHandleFunc func, int famil act_data->id = new_id; } +static gboolean +activation_source_is_scheduled (NMDevice *self, ActivationHandleFunc func, int family) +{ + ActivationHandleData *act_data; + + act_data = activation_source_get_by_family (self, family, NULL); + return act_data->func == func; +} + /*****************************************************************************/ static gboolean @@ -4456,6 +4467,8 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config) NULL, NULL, NULL); + + nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE); } } @@ -5187,6 +5200,8 @@ dhcp6_lease_change (NMDevice *self) settings_connection, nm_device_get_applied_connection (self), self, NULL, NULL, NULL); + + nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE); } } @@ -6827,30 +6842,6 @@ activate_stage5_ip4_config_commit (NMDevice *self) check_ip_done (self); } -static void -queued_ip4_config_change_clear (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->queued_ip4_config_id) { - _LOGD (LOGD_DEVICE, "clearing queued IP4 config change"); - g_source_remove (priv->queued_ip4_config_id); - priv->queued_ip4_config_id = 0; - } -} - -static void -queued_ip6_config_change_clear (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->queued_ip6_config_id) { - _LOGD (LOGD_DEVICE, "clearing queued IP6 config change"); - g_source_remove (priv->queued_ip6_config_id); - priv->queued_ip6_config_id = 0; - } -} - void nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *config) { @@ -6863,7 +6854,6 @@ nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *conf if (config) priv->dev_ip4_config = g_object_ref (config); - queued_ip4_config_change_clear (self); activation_source_schedule (self, activate_stage5_ip4_config_commit, AF_INET); } @@ -7109,7 +7099,9 @@ _cleanup_ip4_pre (NMDevice *self, CleanupType cleanup_type) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); priv->ip4_state = IP_NONE; - queued_ip4_config_change_clear (self); + + if (nm_clear_g_source (&priv->queued_ip4_config_id)) + _LOGD (LOGD_DEVICE, "clearing queued IP4 config change"); dhcp4_cleanup (self, cleanup_type, FALSE); arp_cleanup (self); @@ -7123,7 +7115,9 @@ _cleanup_ip6_pre (NMDevice *self, CleanupType cleanup_type) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); priv->ip6_state = IP_NONE; - queued_ip6_config_change_clear (self); + + if (nm_clear_g_source (&priv->queued_ip6_config_id)) + _LOGD (LOGD_DEVICE, "clearing queued IP6 config change"); dhcp6_cleanup (self, cleanup_type, FALSE); linklocal6_cleanup (self); @@ -7169,22 +7163,25 @@ _hash_check_invalid_keys_impl (GHashTable *hash, const char *setting_name, GErro g_hash_table_iter_init (&iter, hash); while (g_hash_table_iter_next (&iter, (gpointer *) &k, NULL)) { - for (i = 0; argv[i]; i++) { - if (!strcmp (argv[i], k)) { - first_invalid_key = k; - break; - } - } - if (first_invalid_key) + if (_nm_utils_strv_find_first ((char **) argv, -1, k) < 0) { + first_invalid_key = k; break; + } + } + if (setting_name) { + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + "Can't reapply changes to '%s.%s' setting", + setting_name, + first_invalid_key); + } else { + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + "Can't reapply any changes to '%s' setting", + first_invalid_key); } - g_set_error (error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, - "Can't reapply changes to '%s%s%s' setting", - setting_name ? : "", - setting_name ? "." : "", - first_invalid_key ? : ""); g_return_val_if_fail (first_invalid_key, FALSE); return FALSE; } @@ -7305,9 +7302,16 @@ reapply_connection (NMDevice *self, NM_SETTING_CONNECTION_SETTING_NAME)) return FALSE; + /* whitelist allowed properties from "connection" setting which are allowed to differ. + * + * This includes UUID, there is no principal problem with reapplying a connection + * and changing it's UUID. In fact, disallowing it makes it cumbersome for the user + * to reapply any connection but the original settings-connection. */ if (!_hash_check_invalid_keys (diffs ? g_hash_table_lookup (diffs, NM_SETTING_CONNECTION_SETTING_NAME) : NULL, NM_SETTING_CONNECTION_SETTING_NAME, error, + NM_SETTING_CONNECTION_ID, + NM_SETTING_CONNECTION_UUID, NM_SETTING_CONNECTION_ZONE, NM_SETTING_CONNECTION_METERED)) return FALSE; @@ -7336,6 +7340,7 @@ reapply_connection (NMDevice *self, con_old = applied_clone = nm_simple_connection_new_clone (applied); con_new = applied; nm_connection_replace_settings_from_connection (applied, connection); + nm_connection_clear_secrets (applied); } else con_old = con_new = applied; @@ -8779,6 +8784,19 @@ update_ip4_config (NMDevice *self, gboolean initial) gboolean capture_resolv_conf; NMDnsManagerResolvConfMode resolv_conf_mode; + /* If a commit is scheduled, this function would potentially interfere with + * it changing IP configurations before they are applied. Postpone the + * update in such case. + */ + if ( !initial + && activation_source_is_scheduled (self, + activate_stage5_ip4_config_commit, + AF_INET)) { + priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); + _LOGT (LOGD_DEVICE, "IP4 update was postponed"); + return; + } + ifindex = nm_device_get_ip_ifindex (self); if (!ifindex) return; @@ -8857,6 +8875,19 @@ update_ip6_config (NMDevice *self, gboolean initial) gboolean capture_resolv_conf; NMDnsManagerResolvConfMode resolv_conf_mode; + /* If a commit is scheduled, this function would potentially interfere with + * it changing IP configurations before they are applied. Postpone the + * update in such case. + */ + if ( !initial + && activation_source_is_scheduled (self, + activate_stage5_ip6_config_commit, + AF_INET6)) { + priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); + _LOGT (LOGD_DEVICE, "IP6 update was postponed"); + return; + } + ifindex = nm_device_get_ip_ifindex (self); if (!ifindex) return; @@ -8922,8 +8953,12 @@ nm_device_capture_initial_config (NMDevice *self) static gboolean queued_ip4_config_change (gpointer user_data) { - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevice *self = user_data; + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE); + + priv = NM_DEVICE_GET_PRIVATE (self); /* Wait for any queued state changes */ if (priv->queued_state.id) @@ -8942,11 +8977,15 @@ queued_ip4_config_change (gpointer user_data) static gboolean queued_ip6_config_change (gpointer user_data) { - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevice *self = user_data; + NMDevicePrivate *priv; GSList *iter; gboolean need_ipv6ll = FALSE; + g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE); + + priv = NM_DEVICE_GET_PRIVATE (self); + /* Wait for any queued state changes */ if (priv->queued_state.id) return TRUE; @@ -11201,6 +11240,10 @@ dispose (GObject *object) _LOGD (LOGD_DEVICE, "disposing"); + platform = nm_platform_get (); + g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ipx_changed), self); + g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self); + g_slist_free_full (priv->arping.dad_list, (GDestroyNotify) nm_arping_manager_destroy); priv->arping.dad_list = NULL; @@ -11241,10 +11284,6 @@ dispose (GObject *object) _clear_queued_act_request (priv); - platform = nm_platform_get (); - g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ipx_changed), self); - g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self); - nm_clear_g_source (&priv->device_link_changed_id); nm_clear_g_source (&priv->device_ip_link_changed_id); diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index 9539fefd..1a6e6b25 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -310,6 +310,10 @@ connect_ready (MMModemSimple *simple_iface, NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN; self->priv->bearer = mm_modem_simple_connect_finish (simple_iface, res, &error); + + if (!ctx) + return; + if (!self->priv->bearer) { if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) || (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) && @@ -377,9 +381,17 @@ connect_ready (MMModemSimple *simple_iface, static void send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self) { - GError *error = NULL; + gs_free_error GError *error = NULL; + + mm_sim_send_pin_finish (sim, result, &error); + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + if (!self->priv->ctx || self->priv->ctx->step != CONNECT_STEP_UNLOCK) + return; - if (!mm_sim_send_pin_finish (sim, result, &error)) { + if (error) { if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) || (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) && mm_modem_get_unlock_required (self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) { @@ -387,9 +399,8 @@ send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self) } else { g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error)); } - g_error_free (error); return; - } + } self->priv->ctx->step++; connect_context_step (self); @@ -1079,14 +1090,16 @@ simple_disconnect_ready (MMModemSimple *modem_iface, } static void -disconnect (NMModem *self, +disconnect (NMModem *modem, gboolean warn, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + NMModemBroadband *self = NM_MODEM_BROADBAND (modem); DisconnectContext *ctx; + connect_context_clear (self); ctx = g_slice_new (DisconnectContext); ctx->self = g_object_ref (self); ctx->result = g_simple_async_result_new (G_OBJECT (self), diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 804a6d9c..a6c77007 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -1187,6 +1187,7 @@ nm_modem_device_state_changed (NMModem *self, case NM_DEVICE_STATE_UNMANAGED: case NM_DEVICE_STATE_UNAVAILABLE: case NM_DEVICE_STATE_FAILED: + case NM_DEVICE_STATE_DISCONNECTED: if (priv->act_request) { cancel_get_secrets (self); g_object_unref (priv->act_request); @@ -1195,7 +1196,7 @@ nm_modem_device_state_changed (NMModem *self, if (was_connected) { /* Don't bother warning on FAILED since the modem is already gone */ - if (new_state == NM_DEVICE_STATE_FAILED) + if (new_state == NM_DEVICE_STATE_FAILED || new_state == NM_DEVICE_STATE_DISCONNECTED) warn = FALSE; /* First cleanup */ NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, NULL); diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index b8161ab5..9f4cdf3e 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -436,37 +436,10 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self, return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_anycast_addr, last_ip4_address); } -/* uuid_parse does not work for machine-id, so we use our own converter */ -static gboolean -machine_id_parse (const char *in, uuid_t uu) -{ - const char *cp; - int i; - char buf[3]; - - g_return_val_if_fail (in != NULL, FALSE); - g_return_val_if_fail (strlen (in) == 32, FALSE); - - for (i = 0; i < 32; i++) { - if (!g_ascii_isxdigit (in[i])) - return FALSE; - } - - buf[2] = 0; - cp = in; - for (i = 0; i < 16; i++) { - buf[0] = *cp++; - buf[1] = *cp++; - uu[i] = ((unsigned char) strtoul (buf, NULL, 16)) & 0xFF; - } - return TRUE; -} - static GByteArray * generate_duid_from_machine_id (void) { GByteArray *duid; - char *contents = NULL; GChecksum *sum; guint8 buffer[32]; /* SHA256 digest size */ gsize sumlen = sizeof (buffer); @@ -474,27 +447,16 @@ generate_duid_from_machine_id (void) uuid_t uuid; GRand *generator; guint i; - gboolean success = FALSE; - - /* Get the machine ID from /etc/machine-id; it's always in /etc no matter - * where our configured SYSCONFDIR is. Alternatively, it might be in - * LOCALSTATEDIR /lib/dbus/machine-id. - */ - if ( g_file_get_contents ("/etc/machine-id", &contents, NULL, NULL) - || g_file_get_contents (LOCALSTATEDIR "/lib/dbus/machine-id", &contents, NULL, NULL)) { - contents = g_strstrip (contents); - success = machine_id_parse (contents, uuid); - if (success) { - /* Hash the machine ID so it's not leaked to the network */ - sum = g_checksum_new (G_CHECKSUM_SHA256); - g_checksum_update (sum, (const guchar *) &uuid, sizeof (uuid)); - g_checksum_get_digest (sum, buffer, &sumlen); - g_checksum_free (sum); - } - g_free (contents); - } - - if (!success) { + gs_free char *machine_id_s = NULL; + + machine_id_s = nm_utils_machine_id_read (); + if (nm_utils_machine_id_parse (machine_id_s, uuid)) { + /* Hash the machine ID so it's not leaked to the network */ + sum = g_checksum_new (G_CHECKSUM_SHA256); + g_checksum_update (sum, (const guchar *) &uuid, sizeof (uuid)); + g_checksum_get_digest (sum, buffer, &sumlen); + g_checksum_free (sum); + } else { nm_log_warn (LOGD_DHCP6, "dhcp6: failed to read " SYSCONFDIR "/machine-id " "or " LOCALSTATEDIR "/lib/dbus/machine-id to generate " "DHCPv6 DUID; creating non-persistent random DUID."); diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c index 5d49c27e..82cb3350 100644 --- a/src/dhcp-manager/nm-dhcp-systemd.c +++ b/src/dhcp-manager/nm-dhcp-systemd.c @@ -279,8 +279,9 @@ lease_to_ip4_config (const char *iface, /* Domain Name */ r = sd_dhcp_lease_get_domainname (lease, &str); if (r == 0) { - /* Multiple domains sometimes stuffed into the option */ - char **domains = g_strsplit (str, " ", 0); + /* Multiple domains sometimes stuffed into option 15 "Domain Name". + * As systemd escapes such characters, split them at \\032. */ + char **domains = g_strsplit (str, "\\032", 0); char **s; for (s = domains; *s; s++) { @@ -857,6 +858,7 @@ dhcp6_event_cb (sd_dhcp6_client *client, int event, gpointer user_data) nm_dhcp_client_set_state (NM_DHCP_CLIENT (user_data), NM_DHCP_STATE_FAIL, NULL, NULL); break; case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE: + case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST: bound6_handle (self); break; default: @@ -893,6 +895,9 @@ ip6_start (NMDhcpClient *client, return FALSE; } + if (info_only) + sd_dhcp6_client_set_information_request (priv->client6, 1); + /* NM stores the entire DUID which includes the uint16 "type", while systemd * wants the type passed separately from the following data. */ diff --git a/src/dns-manager/nm-dns-dnsmasq.c b/src/dns-manager/nm-dns-dnsmasq.c index 5ead5ecf..4cee1608 100644 --- a/src/dns-manager/nm-dns-dnsmasq.c +++ b/src/dns-manager/nm-dns-dnsmasq.c @@ -32,6 +32,7 @@ #include "nm-ip4-config.h" #include "nm-ip6-config.h" #include "nm-dns-utils.h" +#include "nm-bus-manager.h" #include "NetworkManagerUtils.h" G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN) @@ -39,11 +40,17 @@ G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN) #define NM_DNS_DNSMASQ_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasqPrivate)) #define PIDFILE NMRUNDIR "/dnsmasq.pid" -#define CONFFILE NMRUNDIR "/dnsmasq.conf" #define CONFDIR NMCONFDIR "/dnsmasq.d" +#define DNSMASQ_DBUS_SERVICE "org.freedesktop.NetworkManager.dnsmasq" +#define DNSMASQ_DBUS_PATH "/uk/org/thekelleys/dnsmasq" + typedef struct { - guint32 foo; + GDBusProxy *dnsmasq; + GCancellable *dnsmasq_cancellable; + gboolean running; + + GVariant *set_server_ex_args; } NMDnsDnsmasqPrivate; /*****************************************************************************/ @@ -61,8 +68,28 @@ typedef struct { /*****************************************************************************/ +static void +add_dnsmasq_nameserver (NMDnsDnsmasq *self, + GVariantBuilder *servers, + const char *ip, + const char *domain) +{ + g_return_if_fail (ip); + + _LOGD ("adding nameserver '%s'%s%s%s", ip, + NM_PRINT_FMT_QUOTED (domain, " for domain \"", domain, "\"", "")); + + g_variant_builder_open (servers, G_VARIANT_TYPE ("as")); + + g_variant_builder_add (servers, "s", ip); + if (domain) + g_variant_builder_add (servers, "s", domain); + + g_variant_builder_close (servers); +} + static gboolean -add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split) +add_ip4_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP4Config *ip4, gboolean split) { char buf[INET_ADDRSTRLEN]; in_addr_t addr; @@ -84,9 +111,10 @@ add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split) /* searches are preferred over domains */ n = nm_ip4_config_get_num_searches (ip4); for (i = 0; i < n; i++) { - g_string_append_printf (str, "server=/%s/%s\n", - nm_ip4_config_get_search (ip4, i), - buf); + add_dnsmasq_nameserver (self, + servers, + buf, + nm_ip4_config_get_search (ip4, i)); added = TRUE; } @@ -94,9 +122,10 @@ add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split) /* If not searches, use any domains */ n = nm_ip4_config_get_num_domains (ip4); for (i = 0; i < n; i++) { - g_string_append_printf (str, "server=/%s/%s\n", - nm_ip4_config_get_domain (ip4, i), - buf); + add_dnsmasq_nameserver (self, + servers, + buf, + nm_ip4_config_get_domain (ip4, i)); added = TRUE; } } @@ -107,7 +136,7 @@ add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split) domains = nm_dns_utils_get_ip4_rdns_domains (ip4); if (domains) { for (iter = domains; iter && *iter; iter++) - g_string_append_printf (str, "server=/%s/%s\n", *iter, buf); + add_dnsmasq_nameserver (self, servers, buf, *iter); g_strfreev (domains); added = TRUE; } @@ -118,7 +147,8 @@ add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split) if (!added) { for (i = 0; i < nnameservers; i++) { addr = nm_ip4_config_get_nameserver (ip4, i); - g_string_append_printf (str, "server=%s\n", nm_utils_inet4_ntop (addr, NULL)); + add_dnsmasq_nameserver (self, servers, + nm_utils_inet4_ntop (addr, NULL), NULL); } } @@ -148,7 +178,7 @@ ip6_addr_to_string (const struct in6_addr *addr, const char *iface) } static void -add_global_config (GString *str, const NMGlobalDnsConfig *config) +add_global_config (NMDnsDnsmasq *self, GVariantBuilder *dnsmasq_servers, const NMGlobalDnsConfig *config) { guint i, j; @@ -163,16 +193,16 @@ add_global_config (GString *str, const NMGlobalDnsConfig *config) for (j = 0; servers && servers[j]; j++) { if (!strcmp (name, "*")) - g_string_append_printf (str, "server=%s\n", servers[j]); + add_dnsmasq_nameserver (self, dnsmasq_servers, servers[j], NULL); else - g_string_append_printf (str, "server=/%s/%s\n", name, servers[j]); + add_dnsmasq_nameserver (self, dnsmasq_servers, servers[j], name); } } } static gboolean -add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split) +add_ip6_config (NMDnsDnsmasq *self, GVariantBuilder *servers, NMIP6Config *ip6, gboolean split) { const struct in6_addr *addr; char *buf = NULL; @@ -196,9 +226,10 @@ add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split) /* searches are preferred over domains */ n = nm_ip6_config_get_num_searches (ip6); for (i = 0; i < n; i++) { - g_string_append_printf (str, "server=/%s/%s\n", - nm_ip6_config_get_search (ip6, i), - buf); + add_dnsmasq_nameserver (self, + servers, + buf, + nm_ip6_config_get_search (ip6, i)); added = TRUE; } @@ -206,9 +237,10 @@ add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split) /* If not searches, use any domains */ n = nm_ip6_config_get_num_domains (ip6); for (i = 0; i < n; i++) { - g_string_append_printf (str, "server=/%s/%s\n", - nm_ip6_config_get_domain (ip6, i), - buf); + add_dnsmasq_nameserver (self, + servers, + buf, + nm_ip6_config_get_domain (ip6, i)); added = TRUE; } } @@ -223,7 +255,7 @@ add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split) addr = nm_ip6_config_get_nameserver (ip6, i); buf = ip6_addr_to_string (addr, iface); if (buf) { - g_string_append_printf (str, "server=%s\n", buf); + add_dnsmasq_nameserver (self, servers, buf, NULL); g_free (buf); } } @@ -232,105 +264,220 @@ add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split) return TRUE; } -static gboolean -update (NMDnsPlugin *plugin, - const GSList *vpn_configs, - const GSList *dev_configs, - const GSList *other_configs, - const NMGlobalDnsConfig *global_config, - const char *hostname) +static void +dnsmasq_update_done (GObject *source, GAsyncResult *res, gpointer user_data) { - NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin); + NMDnsDnsmasq *self = NM_DNS_DNSMASQ (user_data); + NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *response = NULL; + + response = g_dbus_proxy_call_finish (priv->dnsmasq, res, &error); + if (!response) + _LOGW ("dnsmasq update failed: %s", error->message); + else + _LOGD ("dnsmasq update successful"); +} + +static void +send_dnsmasq_update (NMDnsDnsmasq *self) +{ + NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); + + if (!priv->set_server_ex_args) + return; + + if (priv->running) { + _LOGD ("trying to update dnsmasq nameservers"); + + g_dbus_proxy_call (priv->dnsmasq, + "SetServersEx", + priv->set_server_ex_args, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) dnsmasq_update_done, + self); + g_clear_pointer (&priv->set_server_ex_args, g_variant_unref); + } else + _LOGD ("dnsmasq not found on the bus. The nameserver update will be sent when dnsmasq appears"); +} + +static void +name_owner_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMDnsDnsmasq *self = NM_DNS_DNSMASQ (user_data); + NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); + gs_free char *owner = NULL; + + owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object)); + if (owner) { + _LOGI ("dnsmasq appeared as %s", owner); + priv->running = TRUE; + send_dnsmasq_update (self); + } else { + _LOGI ("dnsmasq disappeared"); + priv->running = FALSE; + g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED); + } +} + +static void +dnsmasq_proxy_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMDnsDnsmasq *self; + NMDnsDnsmasqPrivate *priv; + gs_free_error GError *error = NULL; + gs_free char *owner = NULL; + GDBusProxy *proxy; + + proxy = g_dbus_proxy_new_finish (res, &error); + if ( !proxy + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_DNS_DNSMASQ (user_data); + + if (!proxy) { + _LOGW ("failed to connect to dnsmasq via DBus: %s", error->message); + g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED); + return; + } + + priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); + + priv->dnsmasq = proxy; + nm_clear_g_cancellable (&priv->dnsmasq_cancellable); + + _LOGD ("dnsmasq proxy creation successful"); + + g_signal_connect (priv->dnsmasq, "notify::g-name-owner", + G_CALLBACK (name_owner_changed), self); + owner = g_dbus_proxy_get_name_owner (priv->dnsmasq); + priv->running = (owner != NULL); + + if (priv->running) + send_dnsmasq_update (self); +} + +static void +start_dnsmasq (NMDnsDnsmasq *self) +{ + NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); const char *dm_binary; - GString *conf; - GSList *iter; const char *argv[15]; - GError *error = NULL; - int ignored; GPid pid = 0; guint idx = 0; + NMBusManager *dbus_mgr; + GDBusConnection *connection; + - /* Kill the old dnsmasq; there doesn't appear to be a way to get dnsmasq - * to reread the config file using SIGHUP or similar. This is a small race - * here when restarting dnsmasq when DNS requests could go to the upstream - * servers instead of to dnsmasq. - */ - nm_dns_plugin_child_kill (plugin); + if ( priv->running + || priv->dnsmasq + || priv->dnsmasq_cancellable) + return; dm_binary = nm_utils_find_helper ("dnsmasq", DNSMASQ_PATH, NULL); if (!dm_binary) { _LOGW ("could not find dnsmasq binary"); - return FALSE; + return; } - /* Build up the new dnsmasq config file */ - conf = g_string_sized_new (150); + argv[idx++] = dm_binary; + argv[idx++] = "--no-resolv"; /* Use only commandline */ + argv[idx++] = "--keep-in-foreground"; + argv[idx++] = "--no-hosts"; /* don't use /etc/hosts to resolve */ + argv[idx++] = "--bind-interfaces"; + argv[idx++] = "--pid-file=" PIDFILE; + argv[idx++] = "--listen-address=127.0.0.1"; /* Should work for both 4 and 6 */ + argv[idx++] = "--cache-size=400"; + argv[idx++] = "--conf-file=/dev/null"; /* avoid loading /etc/dnsmasq.conf */ + argv[idx++] = "--proxy-dnssec"; /* Allow DNSSEC to pass through */ + argv[idx++] = "--enable-dbus=" DNSMASQ_DBUS_SERVICE; + + /* dnsmasq exits if the conf dir is not present */ + if (g_file_test (CONFDIR, G_FILE_TEST_IS_DIR)) + argv[idx++] = "--conf-dir=" CONFDIR; + + argv[idx++] = NULL; + nm_assert (idx <= G_N_ELEMENTS (argv)); + + /* And finally spawn dnsmasq */ + pid = nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/dnsmasq"); + if (!pid) + return; + + dbus_mgr = nm_bus_manager_get (); + g_return_if_fail (dbus_mgr); + + connection = nm_bus_manager_get_connection (dbus_mgr); + g_return_if_fail (connection); + + priv->dnsmasq_cancellable = g_cancellable_new (); + g_dbus_proxy_new (connection, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + NULL, + DNSMASQ_DBUS_SERVICE, + DNSMASQ_DBUS_PATH, + DNSMASQ_DBUS_SERVICE, + priv->dnsmasq_cancellable, + dnsmasq_proxy_cb, + self); +} + +static gboolean +update (NMDnsPlugin *plugin, + const GSList *vpn_configs, + const GSList *dev_configs, + const GSList *other_configs, + const NMGlobalDnsConfig *global_config, + const char *hostname) +{ + NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin); + NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (self); + const GSList *iter; + GVariantBuilder servers; + + start_dnsmasq (self); + + g_variant_builder_init (&servers, G_VARIANT_TYPE ("aas")); if (global_config) - add_global_config (conf, global_config); + add_global_config (self, &servers, global_config); else { /* Use split DNS for VPN configs */ - for (iter = (GSList *) vpn_configs; iter; iter = g_slist_next (iter)) { + for (iter = vpn_configs; iter; iter = g_slist_next (iter)) { if (NM_IS_IP4_CONFIG (iter->data)) - add_ip4_config (conf, NM_IP4_CONFIG (iter->data), TRUE); + add_ip4_config (self, &servers, iter->data, TRUE); else if (NM_IS_IP6_CONFIG (iter->data)) - add_ip6_config (conf, NM_IP6_CONFIG (iter->data), TRUE); + add_ip6_config (self, &servers, iter->data, TRUE); } /* Now add interface configs without split DNS */ - for (iter = (GSList *) dev_configs; iter; iter = g_slist_next (iter)) { + for (iter = dev_configs; iter; iter = g_slist_next (iter)) { if (NM_IS_IP4_CONFIG (iter->data)) - add_ip4_config (conf, NM_IP4_CONFIG (iter->data), FALSE); + add_ip4_config (self, &servers, iter->data, FALSE); else if (NM_IS_IP6_CONFIG (iter->data)) - add_ip6_config (conf, NM_IP6_CONFIG (iter->data), FALSE); + add_ip6_config (self, &servers, iter->data, FALSE); } /* And any other random configs */ - for (iter = (GSList *) other_configs; iter; iter = g_slist_next (iter)) { + for (iter = other_configs; iter; iter = g_slist_next (iter)) { if (NM_IS_IP4_CONFIG (iter->data)) - add_ip4_config (conf, NM_IP4_CONFIG (iter->data), FALSE); + add_ip4_config (self, &servers, iter->data, FALSE); else if (NM_IS_IP6_CONFIG (iter->data)) - add_ip6_config (conf, NM_IP6_CONFIG (iter->data), FALSE); + add_ip6_config (self, &servers, iter->data, FALSE); } } - /* Write out the config file */ - if (!g_file_set_contents (CONFFILE, conf->str, -1, &error)) { - _LOGW ("failed to write dnsmasq config file %s: %s", - CONFFILE, - error->message); - g_clear_error (&error); - goto out; - } - ignored = chmod (CONFFILE, 0644); - - _LOGD ("dnsmasq local caching DNS configuration:"); - _LOGD ("%s", conf->str); - - argv[idx++] = dm_binary; - argv[idx++] = "--no-resolv"; /* Use only commandline */ - argv[idx++] = "--keep-in-foreground"; - argv[idx++] = "--no-hosts"; /* don't use /etc/hosts to resolve */ - argv[idx++] = "--bind-interfaces"; - argv[idx++] = "--pid-file=" PIDFILE; - argv[idx++] = "--listen-address=127.0.0.1"; /* Should work for both 4 and 6 */ - argv[idx++] = "--conf-file=" CONFFILE; - argv[idx++] = "--cache-size=400"; - argv[idx++] = "--proxy-dnssec"; /* Allow DNSSEC to pass through */ - - /* dnsmasq exits if the conf dir is not present */ - if (g_file_test (CONFDIR, G_FILE_TEST_IS_DIR)) - argv[idx++] = "--conf-dir=" CONFDIR; + g_clear_pointer (&priv->set_server_ex_args, g_variant_unref); + priv->set_server_ex_args = g_variant_ref_sink (g_variant_new ("(aas)", &servers)); - argv[idx++] = NULL; - g_warn_if_fail (idx <= G_N_ELEMENTS (argv)); + send_dnsmasq_update (self); - /* And finally spawn dnsmasq */ - pid = nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/dnsmasq"); - -out: - g_string_free (conf, TRUE); - return pid ? TRUE : FALSE; + return TRUE; } /****************************************************************/ @@ -374,7 +521,6 @@ child_quit (NMDnsPlugin *plugin, gint status) _LOGW ("dnsmasq died with signal %d", WTERMSIG (status)); else _LOGW ("dnsmasq died from an unknown cause"); - unlink (CONFFILE); if (failed) g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED); @@ -410,7 +556,13 @@ nm_dns_dnsmasq_init (NMDnsDnsmasq *self) static void dispose (GObject *object) { - unlink (CONFFILE); + NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE (object); + + nm_clear_g_cancellable (&priv->dnsmasq_cancellable); + + g_clear_object (&priv->dnsmasq); + + g_clear_pointer (&priv->set_server_ex_args, g_variant_unref); G_OBJECT_CLASS (nm_dns_dnsmasq_parent_class)->dispose (object); } diff --git a/src/dns-manager/nm-dns-plugin.c b/src/dns-manager/nm-dns-plugin.c index 47322d2a..d5cb8822 100644 --- a/src/dns-manager/nm-dns-plugin.c +++ b/src/dns-manager/nm-dns-plugin.c @@ -48,7 +48,29 @@ enum { }; static guint signals[LAST_SIGNAL] = { 0 }; -/********************************************/ +/******************************************************************************/ + +#define _NMLOG_PREFIX_NAME "dns-plugin" +#define _NMLOG_DOMAIN LOGD_DNS +#define _NMLOG(level, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + \ + if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \ + char __prefix[20]; \ + const NMDnsPlugin *const __self = (self); \ + \ + _nm_log (__level, _NMLOG_DOMAIN, 0, \ + "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + (!__self \ + ? "" \ + : nm_sprintf_buf (__prefix, "[%p]", __self)) \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END + +/******************************************************************************/ gboolean nm_dns_plugin_update (NMDnsPlugin *self, @@ -96,8 +118,7 @@ _clear_pidfile (NMDnsPlugin *self) if (priv->pidfile) { unlink (priv->pidfile); - g_free (priv->pidfile); - priv->pidfile = NULL; + g_clear_pointer (&priv->pidfile, g_free); } } @@ -138,7 +159,7 @@ kill_existing (const char *progname, const char *pidfile, const char *kill_match if (!strstr (cmdline_contents, kill_match)) goto out; - nm_utils_kill_process_sync (pid, start_time, SIGKILL, LOGD_DNS, + nm_utils_kill_process_sync (pid, start_time, SIGKILL, _NMLOG_DOMAIN, progname ?: "", 0, 0, 1000); @@ -154,9 +175,7 @@ watch_cb (GPid pid, gint status, gpointer user_data) priv->pid = 0; priv->watch_id = 0; - g_free (priv->progname); - priv->progname = NULL; - + g_clear_pointer (&priv->progname, g_free); _clear_pidfile (self); g_signal_emit (self, signals[CHILD_QUIT], 0, status); @@ -168,43 +187,47 @@ nm_dns_plugin_child_spawn (NMDnsPlugin *self, const char *pidfile, const char *kill_match) { - NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self); + NMDnsPluginPrivate *priv; GError *error = NULL; - char *cmdline; + GPid pid; + gs_free char *cmdline = NULL; + gs_free char *progname = NULL; - g_return_val_if_fail (argv != NULL, 0); - g_return_val_if_fail (argv[0] != NULL, 0); + g_return_val_if_fail (argv && argv[0], 0); + g_return_val_if_fail (NM_IS_DNS_PLUGIN (self), 0); - g_warn_if_fail (priv->progname == NULL); - g_free (priv->progname); - priv->progname = g_path_get_basename (argv[0]); + priv = NM_DNS_PLUGIN_GET_PRIVATE (self); - kill_existing (priv->progname, pidfile, kill_match); + g_return_val_if_fail (!priv->pid, 0); + nm_assert (!priv->progname); + nm_assert (!priv->watch_id); + nm_assert (!priv->pidfile); - g_warn_if_fail (priv->pidfile == NULL); - g_clear_pointer (&priv->pidfile, g_free); - priv->pidfile = g_strdup (pidfile); + progname = g_path_get_basename (argv[0]); + kill_existing (progname, pidfile, kill_match); - nm_log_info (LOGD_DNS, "DNS: starting %s...", priv->progname); - cmdline = g_strjoinv (" ", (char **) argv); - nm_log_dbg (LOGD_DNS, "DNS: command line: %s", cmdline); - g_free (cmdline); + _LOGI ("starting %s...", progname); + _LOGD ("command line: %s", + (cmdline = g_strjoinv (" ", (char **) argv))); - priv->pid = 0; - if (g_spawn_async (NULL, (char **) argv, NULL, + if (!g_spawn_async (NULL, (char **) argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, nm_utils_setpgid, NULL, - &priv->pid, + &pid, &error)) { - nm_log_dbg (LOGD_DNS, "%s started with pid %d", priv->progname, priv->pid); - priv->watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) watch_cb, self); - } else { - nm_log_warn (LOGD_DNS, "Failed to spawn %s: %s", - priv->progname, error->message); + _LOGW ("failed to spawn %s: %s", + progname, error->message); g_clear_error (&error); + return 0; } - return priv->pid; + _LOGD ("%s started with pid %d", progname, pid); + priv->watch_id = g_child_watch_add (pid, (GChildWatchFunc) watch_cb, self); + priv->pid = pid; + priv->progname = nm_unauto (&progname); + priv->pidfile = g_strdup (pidfile); + + return pid; } gboolean @@ -213,14 +236,12 @@ nm_dns_plugin_child_kill (NMDnsPlugin *self) NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self); nm_clear_g_source (&priv->watch_id); - if (priv->pid) { - nm_utils_kill_child_sync (priv->pid, SIGTERM, LOGD_DNS, priv->progname, NULL, 1000, 0); + nm_utils_kill_child_sync (priv->pid, SIGTERM, _NMLOG_DOMAIN, + priv->progname ?: "", NULL, 1000, 0); priv->pid = 0; - g_free (priv->progname); - priv->progname = NULL; + g_clear_pointer (&priv->progname, g_free); } - _clear_pidfile (self); return TRUE; @@ -243,18 +264,6 @@ dispose (GObject *object) G_OBJECT_CLASS (nm_dns_plugin_parent_class)->dispose (object); } -static void -finalize (GObject *object) -{ - NMDnsPlugin *self = NM_DNS_PLUGIN (object); - NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self); - - g_free (priv->progname); - g_free (priv->pidfile); - - G_OBJECT_CLASS (nm_dns_plugin_parent_class)->finalize (object); -} - static void nm_dns_plugin_class_init (NMDnsPluginClass *plugin_class) { @@ -264,7 +273,6 @@ nm_dns_plugin_class_init (NMDnsPluginClass *plugin_class) /* virtual methods */ object_class->dispose = dispose; - object_class->finalize = finalize; plugin_class->is_caching = is_caching; /* signals */ diff --git a/src/dnsmasq-manager/nm-dnsmasq-manager.c b/src/dnsmasq-manager/nm-dnsmasq-manager.c index 31c1db24..764aba29 100644 --- a/src/dnsmasq-manager/nm-dnsmasq-manager.c +++ b/src/dnsmasq-manager/nm-dnsmasq-manager.c @@ -221,7 +221,7 @@ dm_watch_cb (GPid pid, gint status, gpointer user_data) static NMCmdLine * create_dm_cmd_line (const char *iface, - const NMPlatformIP4Address *listen_address, + const NMIP4Config *ip4_config, const char *pidfile, GError **error) { @@ -232,7 +232,9 @@ create_dm_cmd_line (const char *iface, char localaddr[INET_ADDRSTRLEN]; char *error_desc = NULL; const char *dm_binary; + const NMPlatformIP4Address *listen_address; + listen_address = nm_ip4_config_get_address (ip4_config, 0); g_return_val_if_fail (listen_address, NULL); dm_binary = nm_utils_find_helper ("dnsmasq", DNSMASQ_PATH, error); @@ -290,10 +292,12 @@ create_dm_cmd_line (const char *iface, nm_cmd_line_add_string (cmd, s->str); g_string_free (s, TRUE); - s = g_string_new ("--dhcp-option=option:router,"); - g_string_append (s, localaddr); - nm_cmd_line_add_string (cmd, s->str); - g_string_free (s, TRUE); + if (!nm_ip4_config_get_never_default (ip4_config)) { + s = g_string_new ("--dhcp-option=option:router,"); + g_string_append (s, localaddr); + nm_cmd_line_add_string (cmd, s->str); + g_string_free (s, TRUE); + } nm_cmd_line_add_string (cmd, "--dhcp-lease-max=50"); @@ -365,7 +369,7 @@ nm_dnsmasq_manager_start (NMDnsMasqManager *manager, kill_existing_by_pidfile (priv->pidfile); - dm_cmd = create_dm_cmd_line (priv->iface, nm_ip4_config_get_address (ip4_config, 0), priv->pidfile, error); + dm_cmd = create_dm_cmd_line (priv->iface, ip4_config, priv->pidfile, error); if (!dm_cmd) return FALSE; diff --git a/src/nm-auth-subject.c b/src/nm-auth-subject.c index 494d52d3..86b873f0 100644 --- a/src/nm-auth-subject.c +++ b/src/nm-auth-subject.c @@ -360,9 +360,18 @@ constructed (GObject *object) priv->unix_process.start_time = nm_utils_get_start_time_for_pid (priv->unix_process.pid, NULL, NULL); if (!priv->unix_process.start_time) { - /* could not detect the process start time. The subject is invalid, but don't - * assert against it. */ - _clear_private (priv); + /* Is the process already gone? Then fail creation of the auth subject + * by clearing the type. */ + if (kill (priv->unix_process.pid, 0) != 0) + _clear_private (priv); + + /* Otherwise, although we didn't detect a start_time, the process is still around. + * That could be due to procfs mounted with hidepid. So just accept the request. + * + * Polkit on the other side, will accept 0 and try to lookup /proc/$PID/stat + * itself (and if it fails to do so, assume a start-time of 0 and proceed). + * The only combination that would fail here, is when NM is able to read the + * start-time, but polkit is not. */ } return; default: diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index bbe46654..ef44a139 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -2559,6 +2559,121 @@ nm_utils_is_specific_hostname (const char *name) /******************************************************************/ +gboolean +nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid) +{ + int i; + guint8 v0, v1; + + if (!id_str) + return FALSE; + + for (i = 0; i < 32; i++) { + if (!g_ascii_isxdigit (id_str[i])) + return FALSE; + } + if (id_str[i] != '\0') + return FALSE; + + if (out_uuid) { + for (i = 0; i < 16; i++) { + v0 = g_ascii_xdigit_value (*(id_str++)); + v1 = g_ascii_xdigit_value (*(id_str++)); + out_uuid[i] = (v0 << 4) + v1; + } + } + return TRUE; +} + +char * +nm_utils_machine_id_read (void) +{ + gs_free char *contents = NULL; + int i; + + /* Get the machine ID from /etc/machine-id; it's always in /etc no matter + * where our configured SYSCONFDIR is. Alternatively, it might be in + * LOCALSTATEDIR /lib/dbus/machine-id. + */ + if ( !g_file_get_contents ("/etc/machine-id", &contents, NULL, NULL) + && !g_file_get_contents (LOCALSTATEDIR "/lib/dbus/machine-id", &contents, NULL, NULL)) + return FALSE; + + contents = g_strstrip (contents); + + for (i = 0; i < 32; i++) { + if (!g_ascii_isxdigit (contents[i])) + return FALSE; + if (contents[i] >= 'A' && contents[i] <= 'F') { + /* canonicalize to lower-case */ + contents[i] = 'a' + (contents[i] - 'A'); + } + } + if (contents[i] != '\0') + return FALSE; + + return nm_unauto (&contents); +} + +/*****************************************************************************/ + +guint8 * +nm_utils_secret_key_read (gsize *out_key_len, GError **error) +{ + guint8 *secret_key = NULL; + gsize key_len; + + /* out_key_len is not optional, because without it you cannot safely + * access the returned memory. */ + *out_key_len = 0; + + /* Let's try to load a saved secret key first. */ + if (g_file_get_contents (NMSTATEDIR "/secret_key", (char **) &secret_key, &key_len, NULL)) { + if (key_len < 16) { + g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "Key is too short to be usable"); + key_len = 0; + } + } else { + int urandom = open ("/dev/urandom", O_RDONLY); + mode_t key_mask; + + if (urandom == -1) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "Can't open /dev/urandom: %s", strerror (errno)); + key_len = 0; + goto out; + } + + /* RFC7217 mandates the key SHOULD be at least 128 bits. + * Let's use twice as much. */ + key_len = 32; + secret_key = g_malloc (key_len); + + key_mask = umask (0077); + if (read (urandom, secret_key, key_len) == key_len) { + if (!g_file_set_contents (NMSTATEDIR "/secret_key", (char *) secret_key, key_len, error)) { + g_prefix_error (error, "Can't write " NMSTATEDIR "/secret_key: "); + key_len = 0; + } + } else { + g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "Could not obtain a secret"); + key_len = 0; + } + umask (key_mask); + close (urandom); + } + +out: + if (key_len) { + *out_key_len = key_len; + return secret_key; + } + g_free (secret_key); + return NULL; +} + /* Returns the "u" (universal/local) bit value for a Modified EUI-64 */ static gboolean get_gre_eui64_u_bit (guint32 addr) @@ -2686,7 +2801,7 @@ _set_stable_privacy (struct in6_addr *addr, const char *ifname, const char *uuid, guint dad_counter, - gchar *secret_key, + guint8 *secret_key, gsize key_len, GError **error) { @@ -2744,9 +2859,8 @@ nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr, guint dad_counter, GError **error) { - gchar *secret_key = NULL; + gs_free guint8 *secret_key = NULL; gsize key_len = 0; - gboolean success = FALSE; if (dad_counter >= RFC7217_IDGEN_RETRIES) { g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, @@ -2754,50 +2868,12 @@ nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr, return FALSE; } - /* Let's try to load a saved secret key first. */ - if (g_file_get_contents (NMSTATEDIR "/secret_key", &secret_key, &key_len, NULL)) { - if (key_len < 16) { - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "Key is too short to be usable"); - key_len = 0; - } - } else { - int urandom = open ("/dev/urandom", O_RDONLY); - mode_t key_mask; - - if (urandom == -1) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "Can't open /dev/urandom: %s", strerror (errno)); - return FALSE; - } - - /* RFC7217 mandates the key SHOULD be at least 128 bits. - * Let's use twice as much. */ - key_len = 32; - secret_key = g_malloc (key_len); - - key_mask = umask (0077); - if (read (urandom, secret_key, key_len) == key_len) { - if (!g_file_set_contents (NMSTATEDIR "/secret_key", secret_key, key_len, error)) { - g_prefix_error (error, "Can't write " NMSTATEDIR "/secret_key: "); - key_len = 0; - } - } else { - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "Could not obtain a secret"); - key_len = 0; - } - umask (key_mask); - close (urandom); - } - - if (key_len) { - success = _set_stable_privacy (addr, ifname, uuid, dad_counter, - secret_key, key_len, error); - } + secret_key = nm_utils_secret_key_read (&key_len, error); + if (!secret_key) + return FALSE; - g_free (secret_key); - return success; + return _set_stable_privacy (addr, ifname, uuid, dad_counter, + secret_key, key_len, error); } /** diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 528288c3..ad0f995f 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -306,6 +306,11 @@ const char *nm_utils_ip4_property_path (const char *ifname, const char *property gboolean nm_utils_is_specific_hostname (const char *name); +char *nm_utils_machine_id_read (void); +gboolean nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid); + +guint8 *nm_utils_secret_key_read (gsize *out_key_len, GError **error); + /* IPv6 Interface Identifer helpers */ /** diff --git a/src/org.freedesktop.NetworkManager.conf b/src/org.freedesktop.NetworkManager.conf index dd630e19..d130f7e2 100644 --- a/src/org.freedesktop.NetworkManager.conf +++ b/src/org.freedesktop.NetworkManager.conf @@ -26,6 +26,13 @@ + + + + @@ -127,6 +134,9 @@ + + + 1024 diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 254f9c85..c5045503 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -632,7 +632,9 @@ _linktype_get_type (NMPlatform *platform, obj = _lookup_cached_link (cache, ifindex, completed_from_cache, link_cached); /* If we detected the link type before, we stick to that - * decision unless the "kind" changed. + * decision unless the "kind" no "name" changed. If "name" changed, + * it means that their type may not have been determined correctly + * due to race conditions while accessing sysfs. * * This way, we save edditional ethtool/sysctl lookups, but moreover, * we keep the linktype stable and don't change it as long as the link @@ -643,6 +645,7 @@ _linktype_get_type (NMPlatform *platform, * of messing stuff up. */ if ( obj && !NM_IN_SET (obj->link.type, NM_LINK_TYPE_UNKNOWN, NM_LINK_TYPE_NONE) + && !g_strcmp0 (ifname, obj->link.name) && ( !kind || !g_strcmp0 (kind, obj->link.kind))) { nm_assert (obj->link.kind == g_intern_string (obj->link.kind)); diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 4ad395a1..65148994 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -112,6 +112,10 @@ EXPORT(nm_settings_connection_replace_and_commit) #define PLUGIN_MODULE_PATH "plugin-module-path" +#if (defined(HOSTNAME_PERSIST_SUSE) + defined(HOSTNAME_PERSIST_SLACKWARE) + defined(HOSTNAME_PERSIST_GENTOO)) > 1 +#error "Can only define one of HOSTNAME_PERSIST_*" +#endif + #if defined(HOSTNAME_PERSIST_SUSE) #define HOSTNAME_FILE HOSTNAME_FILE_UCASE_HOSTNAME #elif defined(HOSTNAME_PERSIST_SLACKWARE) @@ -258,6 +262,7 @@ load_connections (NMSettings *self) } priv->connections_loaded = TRUE; + g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTIONS); unmanaged_specs_changed (NULL, self); unrecognized_specs_changed (NULL, self); @@ -480,27 +485,58 @@ get_plugin (NMSettings *self, guint32 capability) static gchar * read_hostname_gentoo (const char *path) { - gchar *contents = NULL, *result = NULL, *tmp; - gchar **all_lines = NULL; - guint line_num, i; + gs_free char *contents = NULL; + gs_strfreev char **all_lines = NULL; + const char *tmp; + guint i; if (!g_file_get_contents (path, &contents, NULL, NULL)) return NULL; + all_lines = g_strsplit (contents, "\n", 0); - line_num = g_strv_length (all_lines); - for (i = 0; i < line_num; i++) { + for (i = 0; all_lines[i]; i++) { g_strstrip (all_lines[i]); if (all_lines[i][0] == '#' || all_lines[i][0] == '\0') continue; if (g_str_has_prefix (all_lines[i], "hostname=")) { tmp = &all_lines[i][NM_STRLEN ("hostname=")]; - result = g_shell_unquote (tmp, NULL); - break; + return g_shell_unquote (tmp, NULL); } } - g_strfreev (all_lines); - g_free (contents); - return result; + return NULL; +} +#endif + +#if defined(HOSTNAME_PERSIST_SLACKWARE) +static gchar * +read_hostname_slackware (const char *path) +{ + gs_free char *contents = NULL; + gs_strfreev char **all_lines = NULL; + char *tmp; + guint i, j = 0; + + if (!g_file_get_contents (path, &contents, NULL, NULL)) + return NULL; + + all_lines = g_strsplit (contents, "\n", 0); + for (i = 0; all_lines[i]; i++) { + g_strstrip (all_lines[i]); + if (all_lines[i][0] == '#' || all_lines[i][0] == '\0') + continue; + tmp = &all_lines[i][0]; + /* We only want up to the first '.' -- the rest of the */ + /* fqdn is defined in /etc/hosts */ + while (tmp[j] != '\0') { + if (tmp[j] == '.') { + tmp[j] = '\0'; + break; + } + j++; + } + return g_shell_unquote (tmp, NULL); + } + return NULL; } #endif @@ -547,18 +583,19 @@ nm_settings_get_hostname (NMSettings *self) goto out; } -#if defined(HOSTNAME_PERSIST_GENTOO) - hostname = read_hostname_gentoo (priv->hostname.file); -#else - #if defined(HOSTNAME_PERSIST_SUSE) if (priv->hostname.dhcp_monitor_id && hostname_is_dynamic ()) return NULL; #endif + +#if defined(HOSTNAME_PERSIST_GENTOO) + hostname = read_hostname_gentoo (priv->hostname.file); +#elif defined(HOSTNAME_PERSIST_SLACKWARE) + hostname = read_hostname_slackware (priv->hostname.file); +#else if (g_file_get_contents (priv->hostname.file, &hostname, NULL, NULL)) g_strchomp (hostname); - -#endif /* HOSTNAME_PERSIST_GENTOO */ +#endif out: if (hostname && !hostname[0]) { @@ -806,18 +843,19 @@ load_plugin: break; } + /* after accessing the plugin we cannot unload it anymore, because the glib + * types cannot be properly unregistered. */ + g_module_make_resident (plugin); + obj = (*factory_func) (); if (!obj || !NM_IS_SETTINGS_PLUGIN (obj)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "Plugin '%s' returned invalid system config object.", pname); success = FALSE; - g_module_close (plugin); break; } - g_module_make_resident (plugin); - g_object_weak_ref (obj, (GWeakNotify) g_module_close, plugin); g_object_set_data_full (obj, PLUGIN_MODULE_PATH, path, g_free); path = NULL; if (add_plugin (self, NM_SETTINGS_PLUGIN (obj))) @@ -1960,9 +1998,11 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self) g_object_unref (connection); if (!added) { - _LOGW ("(%s) couldn't create default wired connection: %s", - nm_device_get_iface (device), - error->message); + if (!g_error_matches (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_UUID_EXISTS)) { + _LOGW ("(%s) couldn't create default wired connection: %s", + nm_device_get_iface (device), + error->message); + } g_clear_error (&error); return; } diff --git a/src/settings/plugins/ifnet/connection_parser.c b/src/settings/plugins/ifnet/connection_parser.c index d9ff97dc..80b82535 100644 --- a/src/settings/plugins/ifnet/connection_parser.c +++ b/src/settings/plugins/ifnet/connection_parser.c @@ -1541,15 +1541,12 @@ make_wireless_security_setting (const char *conn_name, if (wsec == NULL) goto error; } - - if (!wsec) { - g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, - "Can't handle security information for ssid: %s", - conn_name); - } - return wsec; + error: + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Can't handle security information for ssid: %s", + conn_name); return NULL; } diff --git a/src/settings/plugins/ifupdown/interface_parser.c b/src/settings/plugins/ifupdown/interface_parser.c index d342501b..7ad902d4 100644 --- a/src/settings/plugins/ifupdown/interface_parser.c +++ b/src/settings/plugins/ifupdown/interface_parser.c @@ -39,7 +39,7 @@ if_data* last_data; void add_block(const char *type, const char* name) { - if_block *ret = (if_block*)calloc(1,sizeof(struct _if_block)); + if_block *ret = g_slice_new0 (struct _if_block); ret->name = g_strdup(name); ret->type = g_strdup(type); if (first == NULL) @@ -61,7 +61,7 @@ void add_data(const char *key,const char *data) if (first == NULL) return; - ret = (if_data*) calloc(1,sizeof(struct _if_data)); + ret = g_slice_new0 (struct _if_data); ret->key = g_strdup(key); /* Normalize keys. Convert '_' to '-', as ifupdown accepts both variants. @@ -298,9 +298,9 @@ void _destroy_data(if_data *ifd) if (ifd == NULL) return; _destroy_data(ifd->next); - free(ifd->key); - free(ifd->data); - free(ifd); + g_free(ifd->key); + g_free(ifd->data); + g_slice_free(struct _if_data, ifd); return; } @@ -310,9 +310,9 @@ void _destroy_block(if_block* ifb) return; _destroy_block(ifb->next); _destroy_data(ifb->info); - free(ifb->name); - free(ifb->type); - free(ifb); + g_free(ifb->name); + g_free(ifb->type); + g_slice_free(struct _if_block, ifb); return; } diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 5dacae8d..e1734b6d 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -137,6 +137,7 @@ test_utils_DEPENDENCIES = \ test_utils_CPPFLAGS = \ $(AM_CPPFLAGS) \ -DPREFIX=\"/nonexistent\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" \ -DNMSTATEDIR=\"/nonsense\" test_utils_LDADD = \ diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in index 380220f3..a4a135c5 100644 --- a/src/tests/Makefile.in +++ b/src/tests/Makefile.in @@ -887,6 +887,7 @@ test_utils_DEPENDENCIES = \ test_utils_CPPFLAGS = \ $(AM_CPPFLAGS) \ -DPREFIX=\"/nonexistent\" \ + -DLOCALSTATEDIR=\"$(localstatedir)\" \ -DNMSTATEDIR=\"/nonsense\" test_utils_LDADD = \ diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c index c8c82aef..6fc3978b 100644 --- a/src/tests/test-utils.c +++ b/src/tests/test-utils.c @@ -34,17 +34,17 @@ test_stable_privacy (void) struct in6_addr addr1; inet_pton (AF_INET6, "1234::", &addr1); - _set_stable_privacy (&addr1, "eth666", "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", 0, "key", 3, NULL); + _set_stable_privacy (&addr1, "eth666", "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", 0, (guint8 *) "key", 3, NULL); nmtst_assert_ip6_address (&addr1, "1234::4ceb:14cd:3d54:793f"); /* We get an address without the UUID. */ inet_pton (AF_INET6, "1::", &addr1); - _set_stable_privacy (&addr1, "eth666", NULL, 384, "key", 3, NULL); + _set_stable_privacy (&addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL); nmtst_assert_ip6_address (&addr1, "1::11aa:2530:9144:dafa"); /* We get a different address in a different network. */ inet_pton (AF_INET6, "2::", &addr1); - _set_stable_privacy (&addr1, "eth666", NULL, 384, "key", 3, NULL); + _set_stable_privacy (&addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL); nmtst_assert_ip6_address (&addr1, "2::338e:8d:c11:8726"); } -- cgit 1.3.0-6-gf8a5