From d4eb4cb45f41b1751cacf71da558bf8f0988f383 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 3 Jul 2018 14:35:32 +0200 Subject: [PATCH 1/8] dns: allow loading nm-dns-systemd-resolve alongside other DNS plugins Even when the system resolver is configured to something else that systemd-resolved, it still is a good idea to keep systemd-resolved up to date. If not anything else, it does a good job at doing per-interface resolving for connectivity checks. If for whatever reasons don't want NetworkManager to push the DNS data it discovers to systemd-resolved, the functionality can be disabled with: [main] systemd-resolved=false --- man/NetworkManager.conf.xml | 14 ++++++++++++ src/dns/nm-dns-manager.c | 45 +++++++++++++++++++++++++++++++------ src/nm-config-data.c | 10 +++++++++ src/nm-config-data.h | 1 + 4 files changed, 63 insertions(+), 7 deletions(-) diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index 6735af5fd4..1e1d0aab15 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -387,6 +387,20 @@ no-auto-default=* + + systemd-resolved + Send the connection DNS configuration to + systemd-resolved. Defaults to "true". + + Note that this setting is complementary to the + dns setting. You can keep this enable while using + dns set to another DNS plugin alongside + systemd-resolved, or dns set to + systemd-resolved to configure the system resolver to use + systemd-resolved. + + + debug Comma separated list of options to aid diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index a8dc0413d4..461a1385a5 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -122,6 +122,7 @@ typedef struct { NMDnsManagerResolvConfManager rc_manager; char *mode; + NMDnsPlugin *sd_resolve_plugin; NMDnsPlugin *plugin; NMConfig *config; @@ -1398,6 +1399,16 @@ update_dns (NMDnsManager *self, &searches, &options, &nameservers, &nis_servers, &nis_domain); + if (priv->plugin || priv->sd_resolve_plugin) + rebuild_domain_lists (self); + + if (priv->sd_resolve_plugin) { + nm_dns_plugin_update (priv->sd_resolve_plugin, + global_config, + _ip_config_lst_head (self), + priv->hostname); + } + /* Let any plugins do their thing first */ if (priv->plugin) { NMDnsPlugin *plugin = priv->plugin; @@ -1413,7 +1424,6 @@ update_dns (NMDnsManager *self, } _LOGD ("update-dns: updating plugin %s", plugin_name); - rebuild_domain_lists (self); if (!nm_dns_plugin_update (plugin, global_config, _ip_config_lst_head (self), @@ -1425,15 +1435,17 @@ update_dns (NMDnsManager *self, */ caching = FALSE; } - /* Clear the generated search list as it points to - * strings owned by IP configurations and we can't - * guarantee they stay alive. */ - clear_domain_lists (self); skip: ; } + /* Clear the generated search list as it points to + * strings owned by IP configurations and we can't + * guarantee they stay alive. */ + clear_domain_lists (self); + clear_domain_lists (self); + update_resolv_conf_no_stub (self, searches, nameservers, options); /* If caching was successful, we only send 127.0.0.1 to /etc/resolv.conf @@ -1932,9 +1944,11 @@ init_resolv_conf_mode (NMDnsManager *self, gboolean force_reload_plugin) NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); NMDnsManagerResolvConfManager rc_manager; const char *mode; + gboolean systemd_resolved = FALSE; gboolean param_changed = FALSE, plugin_changed = FALSE; mode = nm_config_data_get_dns_mode (nm_config_get_data (priv->config)); + systemd_resolved = nm_config_data_get_systemd_resolved (nm_config_get_data (priv->config)); if (nm_streq0 (mode, "none")) rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED; @@ -1980,6 +1994,7 @@ again: plugin_changed = TRUE; } mode = "systemd-resolved"; + systemd_resolved = FALSE; } else if (nm_streq0 (mode, "dnsmasq")) { if (force_reload_plugin || !NM_IS_DNS_DNSMASQ (priv->plugin)) { _clear_plugin (self); @@ -2002,6 +2017,20 @@ again: plugin_changed = TRUE; } + /* The systemd-resolved plugin is special. We typically always want to keep + * systemd-resolved up to date even if the configured plugin is different. */ + if (systemd_resolved) { + if (!priv->sd_resolve_plugin) { + priv->sd_resolve_plugin = nm_dns_systemd_resolved_new (); + plugin_changed = TRUE; + } + } else { + if (priv->sd_resolve_plugin) { + g_clear_object (&priv->sd_resolve_plugin); + plugin_changed = TRUE; + } + } + if (plugin_changed && priv->plugin) { g_signal_connect (priv->plugin, NM_DNS_PLUGIN_FAILED, G_CALLBACK (plugin_failed), self); g_signal_connect (priv->plugin, NM_DNS_PLUGIN_CHILD_QUIT, G_CALLBACK (plugin_child_quit), self); @@ -2023,8 +2052,9 @@ again: } if (param_changed || plugin_changed) { - _LOGI ("init: dns=%s, rc-manager=%s%s%s%s", - mode, _rc_manager_to_string (rc_manager), + _LOGI ("init: dns=%s%s rc-manager=%s%s%s%s", + mode, (systemd_resolved ? ",systemd-resolved" : ""), + _rc_manager_to_string (rc_manager), NM_PRINT_FMT_QUOTED (priv->plugin, ", plugin=", nm_dns_plugin_get_name (priv->plugin), "", "")); } @@ -2285,6 +2315,7 @@ dispose (GObject *object) if (priv->config) g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self); + g_clear_object (&priv->sd_resolve_plugin); _clear_plugin (self); priv->best_ip_config_4 = NULL; diff --git a/src/nm-config-data.c b/src/nm-config-data.c index 8d84e74abc..c37e36b421 100644 --- a/src/nm-config-data.c +++ b/src/nm-config-data.c @@ -108,6 +108,7 @@ typedef struct { char *dns_mode; char *rc_manager; + gboolean systemd_resolved; NMGlobalDnsConfig *global_dns; } NMConfigDataPrivate; @@ -322,6 +323,14 @@ nm_config_data_get_rc_manager (const NMConfigData *self) return NM_CONFIG_DATA_GET_PRIVATE (self)->rc_manager; } +gboolean +nm_config_data_get_systemd_resolved (const NMConfigData *self) +{ + g_return_val_if_fail (self, FALSE); + + return NM_CONFIG_DATA_GET_PRIVATE (self)->systemd_resolved; +} + gboolean nm_config_data_get_ignore_carrier (const NMConfigData *self, NMDevice *device) { @@ -1654,6 +1663,7 @@ constructed (GObject *object) priv->dns_mode = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "dns", NULL)); priv->rc_manager = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "rc-manager", NULL)); + priv->systemd_resolved = nm_config_keyfile_get_boolean (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "systemd-resolved", TRUE); priv->ignore_carrier = nm_config_get_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "ignore-carrier", NULL); priv->assume_ipv6ll_only = nm_config_get_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "assume-ipv6ll-only", NULL); diff --git a/src/nm-config-data.h b/src/nm-config-data.h index b52ddcc4f8..9d1e20a6a2 100644 --- a/src/nm-config-data.h +++ b/src/nm-config-data.h @@ -170,6 +170,7 @@ gboolean nm_config_data_get_no_auto_default_for_device (const NMConfigD const char *nm_config_data_get_dns_mode (const NMConfigData *self); const char *nm_config_data_get_rc_manager (const NMConfigData *self); +gboolean nm_config_data_get_systemd_resolved (const NMConfigData *self); gboolean nm_config_data_get_ignore_carrier (const NMConfigData *self, NMDevice *device); gboolean nm_config_data_get_assume_ipv6ll_only (const NMConfigData *self, NMDevice *device); From 753ffdbca8354e83f990d509f5e471cff89e161c Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 3 Jul 2018 14:09:01 +0200 Subject: [PATCH 2/8] connectivity: improve curl error messages --- src/nm-connectivity.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c index 8a6e955ae5..7ef99e54f9 100644 --- a/src/nm-connectivity.c +++ b/src/nm-connectivity.c @@ -285,7 +285,7 @@ _con_curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask) ret = curl_multi_socket_action (mhandle, sockfd, ev_bitmask, &running_handles); if (ret != CURLM_OK) { - _LOGD ("connectivity check failed: %d", ret); + _LOGD ("connectivity check failed: (%d) %s", ret, curl_easy_strerror (ret)); success = FALSE; } @@ -297,7 +297,8 @@ _con_curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask) /* Here we have completed a session. Check easy session result. */ eret = curl_easy_getinfo (msg->easy_handle, CURLINFO_PRIVATE, (char **) &cb_data); if (eret != CURLE_OK) { - _LOGD ("curl cannot extract cb_data for easy handle, skipping msg"); + _LOGD ("curl cannot extract cb_data for easy handle, skipping msg: (%d) %s", + eret, curl_easy_strerror (eret)); success = FALSE; continue; } @@ -314,7 +315,9 @@ _con_curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask) cb_data_queue_completed (cb_data, NM_CONNECTIVITY_LIMITED, NULL, - g_strdup_printf ("check failed with curl status %d", msg->data.result)); + g_strdup_printf ("check failed: (%d) %s", + msg->data.result, + curl_easy_strerror (msg->data.result))); } else if ( !((_check_handle_get_response (cb_data))[0]) && (curl_easy_getinfo (msg->easy_handle, CURLINFO_RESPONSE_CODE, &response_code) == CURLE_OK) && response_code == 204) { @@ -734,6 +737,9 @@ static void nm_connectivity_init (NMConnectivity *self) { NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self); +#if WITH_CONCHECK + CURLcode ret; +#endif c_list_init (&priv->handles_lst_head); c_list_init (&priv->completed_handles_lst_head); @@ -745,12 +751,14 @@ nm_connectivity_init (NMConnectivity *self) self); #if WITH_CONCHECK - if (curl_global_init (CURL_GLOBAL_ALL) == CURLE_OK) + ret = curl_global_init (CURL_GLOBAL_ALL); + if (ret == CURLE_OK) priv->concheck.curl_mhandle = curl_multi_init (); - if (!priv->concheck.curl_mhandle) - _LOGE ("unable to init cURL, connectivity check will not work"); - else { + if (!priv->concheck.curl_mhandle) { + _LOGE ("unable to init cURL, connectivity check will not work: (%d) %s", + ret, curl_easy_strerror (ret)); + } else { curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb); curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_SOCKETDATA, self); curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_TIMERFUNCTION, multi_timer_cb); From 2cec94bacce4a09c0e5ffa241b8d50fd4702dddc Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 22 Jun 2017 16:46:13 +0200 Subject: [PATCH 3/8] connectivity: use systemd-resolved for resolving the check endpoint This allows us to use the correct DNS server for the particular interface independent of what the system resolver is configured to use. The ugly part: Unfortunately, it is not all that easy. The libc's libresolv.so API does not provide means for influencing neither interface nor name servers used for DNS resolving. Curl can also be compiled with c-ares resolver backend that does provide the necessary functionality, but it requires and extra library and the Linux distributions don't seem to enable it. (Fedora doesn't, which is a good sign we don't have an option of relying on it.) systemd-resolved does provide everything we need. If we take care to keep its congfiguration up to date, we can use it to do the resolving on a particular interface with that interface's DNS configuration. Great! There's one more problem: Curl doesn't provide callbacks for resolving host names. It doesn't, however, allow us to pass in the pre-resolved hostnames in form of an CURLOPT_RESOLVE(3) option. This means we have to parse the host name out of the URL ourselves. Fair enough I guess... --- src/devices/nm-device.c | 1 + src/nm-connectivity.c | 233 ++++++++++++++++++++++++++++++++++++---- src/nm-connectivity.h | 1 + 3 files changed, 212 insertions(+), 23 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index b47b8914dd..035165dd34 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -2967,6 +2967,7 @@ concheck_start (NMDevice *self, is_periodic ? ", periodic-check" : ""); handle->c_handle = nm_connectivity_check_start (concheck_get_mgr (self), + nm_device_get_ip_ifindex (self), nm_device_get_ip_iface (self), concheck_cb, handle); diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c index 7ef99e54f9..2ce5481b49 100644 --- a/src/nm-connectivity.c +++ b/src/nm-connectivity.c @@ -17,12 +17,13 @@ * * Copyright (C) 2011 Thomas Bechtold * Copyright (C) 2011 Dan Williams - * Copyright (C) 2016,2017 Red Hat, Inc. + * Copyright (C) 2016 - 2018 Red Hat, Inc. */ #include "nm-default.h" #include "nm-connectivity.h" +#include "nm-dbus-manager.h" #include @@ -72,8 +73,11 @@ struct _NMConnectivityCheckHandle { struct { char *response; + int ifindex; + GCancellable *resolve_cancellable; CURL *curl_ehandle; struct curl_slist *request_headers; + struct curl_slist *hosts; GString *recv_msg; } concheck; @@ -98,6 +102,8 @@ typedef struct { CList handles_lst_head; CList completed_handles_lst_head; char *uri; + char *host; + char *port; char *response; gboolean enabled; guint interval; @@ -193,7 +199,9 @@ cb_data_complete (NMConnectivityCheckHandle *cb_data, curl_easy_cleanup (cb_data->concheck.curl_ehandle); curl_slist_free_all (cb_data->concheck.request_headers); + curl_slist_free_all (cb_data->concheck.hosts); } + nm_clear_g_cancellable (&cb_data->concheck.resolve_cancellable); #endif nm_clear_g_source (&cb_data->timeout_id); @@ -559,8 +567,139 @@ _idle_cb (gpointer user_data) return G_SOURCE_REMOVE; } +static void +do_curl_request (NMConnectivityCheckHandle *cb_data) +{ + NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (cb_data->self); + CURL *ehandle; + + ehandle = curl_easy_init (); + if (!ehandle) { + cb_data_free (cb_data, NM_CONNECTIVITY_ERROR, NULL, "curl error"); + return; + } + + cb_data->concheck.response = g_strdup (priv->response); + cb_data->concheck.curl_ehandle = ehandle; + cb_data->concheck.request_headers = curl_slist_append (NULL, "Connection: close"); + cb_data->timeout_id = g_timeout_add_seconds (20, _timeout_cb, cb_data); + + curl_easy_setopt (ehandle, CURLOPT_URL, priv->uri); + curl_easy_setopt (ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb); + curl_easy_setopt (ehandle, CURLOPT_WRITEDATA, cb_data); + curl_easy_setopt (ehandle, CURLOPT_HEADERFUNCTION, easy_header_cb); + curl_easy_setopt (ehandle, CURLOPT_HEADERDATA, cb_data); + curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data); + curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->concheck.request_headers); + curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec); + curl_easy_setopt (ehandle, CURLOPT_RESOLVE, cb_data->concheck.hosts); + + curl_multi_add_handle (priv->concheck.curl_mhandle, ehandle); +} + +static void +resolve_cb (GObject *object, GAsyncResult *res, gpointer user_data) +{ + NMConnectivityCheckHandle *cb_data = user_data; + NMConnectivity *self; + NMConnectivityPrivate *priv; + GVariant *result; + GVariant *addresses; + gsize no_addresses; + int ifindex; + int family; + GVariant *address = NULL; + const guchar *address_buf; + gsize len = 0; + char str[INET6_ADDRSTRLEN + 1] = { 0, }; + char *host; + gsize i; + gs_free_error GError *error = NULL; + + result = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), res, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = cb_data->self; + priv = NM_CONNECTIVITY_GET_PRIVATE (self); + + if (!result) { + /* Never mind. Just let do curl do its own resolving. */ + _LOG2D ("can't resolve a name via systemd-resolved: %s", error->message); + do_curl_request (cb_data); + return; + } + + addresses = g_variant_get_child_value (result, 0); + no_addresses = g_variant_n_children (addresses); + g_variant_unref (result); + + for (i = 0; i < no_addresses; i++) { + g_variant_get_child (addresses, i, "(ii@ay)", &ifindex, &family, &address); + address_buf = g_variant_get_fixed_array (address, &len, 1); + + if ( (family == AF_INET && len == sizeof (struct in_addr)) + || (family == AF_INET6 && len == sizeof (struct in6_addr))) { + inet_ntop (family, address_buf, str, sizeof (str)); + host = g_strdup_printf ("%s:%s:%s", priv->host, + priv->port ? priv->port : "80", str); + cb_data->concheck.hosts = curl_slist_append (cb_data->concheck.hosts, host); + _LOG2T ("adding '%s' to curl resolve list", host); + g_free (host); + } + + g_variant_unref (address); + } + + g_variant_unref (addresses); + do_curl_request (cb_data); +} + +#define SD_RESOLVED_DNS 1 + +static void +resolved_proxy_created (GObject *object, GAsyncResult *res, gpointer user_data) +{ + NMConnectivityCheckHandle *cb_data = user_data; + NMConnectivity *self; + NMConnectivityPrivate *priv; + gs_free_error GError *error = NULL; + GDBusProxy *proxy; + + proxy = g_dbus_proxy_new_for_bus_finish (res, &error); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = cb_data->self; + priv = NM_CONNECTIVITY_GET_PRIVATE (self); + + if (!proxy) { + /* Log a warning, but still proceed without systemd-resolved */ + _LOG2D ("failed to connect to resolved via DBus: %s", error->message); + do_curl_request (cb_data); + return; + } + + g_dbus_proxy_call (proxy, + "ResolveHostname", + g_variant_new ("(isit)", + cb_data->concheck.ifindex, + priv->host, + (gint32) AF_UNSPEC, + (guint64) SD_RESOLVED_DNS), + G_DBUS_CALL_FLAGS_NONE, + -1, + cb_data->concheck.resolve_cancellable, + resolve_cb, + cb_data); + g_object_unref (proxy); + + _LOG2D ("resolving '%s' for '%s' using systemd-resolved", priv->host, priv->uri); +} + NMConnectivityCheckHandle * nm_connectivity_check_start (NMConnectivity *self, + int ifindex, const char *iface, NMConnectivityCheckCallback callback, gpointer user_data) @@ -585,35 +724,29 @@ nm_connectivity_check_start (NMConnectivity *self, cb_data->ifspec = g_strdup_printf ("if!%s", iface); #if WITH_CONCHECK - if (iface) { - CURL *ehandle; + if (iface && ifindex > 0 && priv->enabled && priv->host) { + cb_data->concheck.ifindex = ifindex; + cb_data->concheck.resolve_cancellable = g_cancellable_new (); - if ( priv->enabled - && (ehandle = curl_easy_init ())) { + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + cb_data->concheck.resolve_cancellable, + resolved_proxy_created, + cb_data); - cb_data->concheck.response = g_strdup (priv->response); - cb_data->concheck.curl_ehandle = ehandle; - cb_data->concheck.request_headers = curl_slist_append (NULL, "Connection: close"); - curl_easy_setopt (ehandle, CURLOPT_URL, priv->uri); - curl_easy_setopt (ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb); - curl_easy_setopt (ehandle, CURLOPT_WRITEDATA, cb_data); - curl_easy_setopt (ehandle, CURLOPT_HEADERFUNCTION, easy_header_cb); - curl_easy_setopt (ehandle, CURLOPT_HEADERDATA, cb_data); - curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data); - curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->concheck.request_headers); - curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec); - curl_multi_add_handle (priv->concheck.curl_mhandle, ehandle); - - cb_data->timeout_id = g_timeout_add_seconds (20, _timeout_cb, cb_data); - - _LOG2D ("start request to '%s'", priv->uri); - return cb_data; - } + _LOG2D ("start request to '%s'", priv->uri); + return cb_data; } #endif _LOG2D ("start fake request"); cb_data->timeout_id = g_idle_add (_idle_cb, cb_data); + return cb_data; } @@ -649,6 +782,54 @@ nm_connectivity_get_interval (NMConnectivity *self) : 0; } +static gboolean +host_and_port_from_uri (const char *uri, char **host, char **port) +{ + const char *p = uri; + const char *host_begin = NULL; + size_t host_len = 0; + const char *port_begin = NULL; + size_t port_len = 0; + + /* scheme */ + while (*p != ':' && *p != '/') { + if (!*p++) + return FALSE; + } + + /* :// */ + if (*p++ != ':') + return FALSE; + if (*p++ != '/') + return FALSE; + if (*p++ != '/') + return FALSE; + /* host */ + if (*p == '[') + return FALSE; + host_begin = p; + while (*p && *p != ':' && *p != '/') { + host_len++; + p++; + } + if (host_len == 0) + return FALSE; + *host = strndup (host_begin, host_len); + + /* port */ + if (*p++ == ':') { + port_begin = p; + while (*p && *p != '/') { + port_len++; + p++; + } + if (port_len) + *port = strndup (port_begin, port_len); + } + + return TRUE; +} + static void update_config (NMConnectivity *self, NMConfigData *config_data) { @@ -682,6 +863,10 @@ update_config (NMConnectivity *self, NMConfigData *config_data) if (changed) { g_free (priv->uri); priv->uri = g_strdup (uri); + + g_clear_pointer (&priv->host, g_free); + g_clear_pointer (&priv->port, g_free); + host_and_port_from_uri (uri, &priv->host, &priv->port); } /* Set the interval. */ @@ -785,6 +970,8 @@ dispose (GObject *object) cb_data_complete (cb_data, NM_CONNECTIVITY_DISPOSING, "shutting down"); g_clear_pointer (&priv->uri, g_free); + g_clear_pointer (&priv->host, g_free); + g_clear_pointer (&priv->port, g_free); g_clear_pointer (&priv->response, g_free); #if WITH_CONCHECK diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h index 178f27ad9a..2bf07bc080 100644 --- a/src/nm-connectivity.h +++ b/src/nm-connectivity.h @@ -58,6 +58,7 @@ typedef void (*NMConnectivityCheckCallback) (NMConnectivity *self, gpointer user_data); NMConnectivityCheckHandle *nm_connectivity_check_start (NMConnectivity *self, + int ifindex, const char *iface, NMConnectivityCheckCallback callback, gpointer user_data); From 9664f284a1d8575798daa9abf9cd7f49a19b48d9 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 3 Jul 2018 19:20:45 +0200 Subject: [PATCH 4/8] connectivity: allow limiting the connectivity check to a specified AF Nothing changes practically, as the NMDevice still starts this with AF_UNSPEC. That is going to change in the following commit. The ugly part: priv->concheck_x[0] in few places. I believe we shouldn't be using union aliasing here, and instead of indexing the v4/v6 arrays by a boolean it should be an enum. I'm not fixing it here, but I eventually plan to if this gets an ACK. --- src/devices/nm-device.c | 233 +++++++++++++++++++++++----------------- src/devices/nm-device.h | 1 + src/nm-connectivity.c | 40 +++++-- src/nm-connectivity.h | 1 + src/nm-manager.c | 7 ++ 5 files changed, 176 insertions(+), 106 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 035165dd34..b145a96257 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -175,6 +175,7 @@ struct _NMDeviceConnectivityHandle { bool is_periodic:1; bool is_periodic_bump:1; bool is_periodic_bump_on_complete:1; + int addr_family; }; typedef struct { @@ -555,24 +556,24 @@ typedef struct _NMDevicePrivate { NMLldpListener *lldp_listener; NMConnectivity *concheck_mgr; - - /* if periodic checks are enabled, this is the source id for the next check. */ - guint concheck_p_cur_id; - - /* the currently configured max periodic interval. */ - guint concheck_p_max_interval; - - /* the current interval. If we are probing, the interval might be lower - * then the configured max interval. */ - guint concheck_p_cur_interval; - - /* the timestamp, when we last scheduled the timer concheck_p_cur_id with current interval - * concheck_p_cur_interval. */ - gint64 concheck_p_cur_basetime_ns; - - NMConnectivityState connectivity_state; - CList concheck_lst_head; + struct { + /* if periodic checks are enabled, this is the source id for the next check. */ + guint p_cur_id; + + /* the currently configured max periodic interval. */ + guint p_max_interval; + + /* the current interval. If we are probing, the interval might be lower + * then the configured max interval. */ + guint p_cur_interval; + + /* the timestamp, when we last scheduled the timer p_cur_id with current interval + * p_cur_interval. */ + gint64 p_cur_basetime_ns; + + NMConnectivityState state; + } concheck_x[2]; guint check_delete_unrealized_id; @@ -643,7 +644,10 @@ static void _set_mtu (NMDevice *self, guint32 mtu); static void _commit_mtu (NMDevice *self, const NMIP4Config *config); static void _cancel_activation (NMDevice *self); -static void concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean is_periodic); +static void concheck_update_state (NMDevice *self, + int addr_family, + NMConnectivityState state, + gboolean is_periodic); /*****************************************************************************/ @@ -2092,13 +2096,14 @@ nm_device_get_route_metric_default (NMDeviceType device_type) } static gboolean -default_route_metric_penalty_detect (NMDevice *self) +default_route_metric_penalty_detect (NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const gboolean IS_IPv4 = (addr_family == AF_INET); /* currently we don't differentiate between IPv4 and IPv6 when detecting * connectivity. */ - if ( priv->connectivity_state != NM_CONNECTIVITY_FULL + if ( priv->concheck_x[IS_IPv4].state != NM_CONNECTIVITY_FULL && nm_connectivity_check_enabled (concheck_get_mgr (self))) return TRUE; @@ -2448,23 +2453,35 @@ typedef enum { } ConcheckScheduleMode; static NMDeviceConnectivityHandle *concheck_start (NMDevice *self, + int addr_family, NMDeviceConnectivityCallback callback, gpointer user_data, gboolean is_periodic); static void concheck_periodic_schedule_set (NMDevice *self, + int addr_family, ConcheckScheduleMode mode); static gboolean -concheck_periodic_timeout_cb (gpointer user_data) +_concheck_periodic_timeout_cb (NMDevice *self, int addr_family) { - NMDevice *self = user_data; - _LOGt (LOGD_CONCHECK, "connectivity: periodic timeout"); - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_CHECK_PERIODIC); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_CHECK_PERIODIC); return G_SOURCE_REMOVE; } +static gboolean +concheck_ip4_periodic_timeout_cb (gpointer user_data) +{ + return _concheck_periodic_timeout_cb (user_data, AF_INET); +} + +static gboolean +concheck_ip6_periodic_timeout_cb (gpointer user_data) +{ + return _concheck_periodic_timeout_cb (user_data, AF_INET6); +} + static gboolean concheck_is_possible (NMDevice *self) { @@ -2483,17 +2500,18 @@ concheck_is_possible (NMDevice *self) } static gboolean -concheck_periodic_schedule_do (NMDevice *self, gint64 now_ns) +concheck_periodic_schedule_do (NMDevice *self, int addr_family, gint64 now_ns) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean periodic_check_disabled = FALSE; gint64 expiry, tdiff; + const gboolean IS_IPv4 = (addr_family == AF_INET); /* we always cancel whatever was pending. */ - if (nm_clear_g_source (&priv->concheck_p_cur_id)) + if (nm_clear_g_source (&priv->concheck_x[IS_IPv4].p_cur_id)) periodic_check_disabled = TRUE; - if (priv->concheck_p_max_interval == 0) { + if (priv->concheck_x[IS_IPv4].p_max_interval == 0) { /* periodic checks are disabled */ goto out; } @@ -2502,23 +2520,24 @@ concheck_periodic_schedule_do (NMDevice *self, gint64 now_ns) goto out; nm_assert (now_ns > 0); - nm_assert (priv->concheck_p_cur_interval > 0); + nm_assert (priv->concheck_x[IS_IPv4].p_cur_interval > 0); /* we schedule the timeout based on our current settings cur-interval and cur-basetime. * Before calling concheck_periodic_schedule_do(), make sure that these properties are * correct. */ - expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + expiry = priv->concheck_x[IS_IPv4].p_cur_basetime_ns + (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); tdiff = expiry - now_ns; _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: %sscheduled in %lld milliseconds (%u seconds interval)", periodic_check_disabled ? "re-" : "", (long long) (tdiff / NM_UTILS_NS_PER_MSEC), - priv->concheck_p_cur_interval); + priv->concheck_x[IS_IPv4].p_cur_interval); - priv->concheck_p_cur_id = g_timeout_add (NM_MAX ((gint64) 0, tdiff) / NM_UTILS_NS_PER_MSEC, - concheck_periodic_timeout_cb, - self); + priv->concheck_x[IS_IPv4].p_cur_id = + g_timeout_add (NM_MAX ((gint64) 0, tdiff) / NM_UTILS_NS_PER_MSEC, + IS_IPv4 ? concheck_ip4_periodic_timeout_cb : concheck_ip6_periodic_timeout_cb, + self); return TRUE; out: if (periodic_check_disabled) @@ -2529,19 +2548,19 @@ out: #define CONCHECK_P_PROBE_INTERVAL 1 static void -concheck_periodic_schedule_set (NMDevice *self, - ConcheckScheduleMode mode) +concheck_periodic_schedule_set (NMDevice *self, int addr_family, ConcheckScheduleMode mode) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gint64 new_expiry, exp_expiry, cur_expiry, tdiff; gint64 now_ns = 0; + const gboolean IS_IPv4 = (addr_family == AF_INET); - if (priv->concheck_p_max_interval == 0) { + if (priv->concheck_x[IS_IPv4].p_max_interval == 0) { /* periodic check is disabled. Nothing to do. */ return; } - if (!priv->concheck_p_cur_id) { + if (!priv->concheck_x[IS_IPv4].p_cur_id) { /* we currently don't have a timeout scheduled. No need to reschedule * another one... */ if (NM_IN_SET (mode, CONCHECK_SCHEDULE_UPDATE_INTERVAL, @@ -2555,19 +2574,19 @@ concheck_periodic_schedule_set (NMDevice *self, switch (mode) { case CONCHECK_SCHEDULE_UPDATE_INTERVAL_RESTART: - priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_max_interval, CONCHECK_P_PROBE_INTERVAL); - priv->concheck_p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); - if (concheck_periodic_schedule_do (self, now_ns)) - concheck_start (self, NULL, NULL, TRUE); + priv->concheck_x[IS_IPv4].p_cur_interval = NM_MIN (priv->concheck_x[IS_IPv4].p_max_interval, CONCHECK_P_PROBE_INTERVAL); + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); + if (concheck_periodic_schedule_do (self, addr_family, now_ns)) + concheck_start (self, addr_family, NULL, NULL, TRUE); return; case CONCHECK_SCHEDULE_UPDATE_INTERVAL: - /* called with "UPDATE_INTERVAL" and already have a concheck_p_cur_id scheduled. */ + /* called with "UPDATE_INTERVAL" and already have a p_cur_id scheduled. */ - nm_assert (priv->concheck_p_max_interval > 0); - nm_assert (priv->concheck_p_cur_interval > 0); + nm_assert (priv->concheck_x[IS_IPv4].p_max_interval > 0); + nm_assert (priv->concheck_x[IS_IPv4].p_cur_interval > 0); - if (priv->concheck_p_cur_interval <= priv->concheck_p_max_interval) { + if (priv->concheck_x[IS_IPv4].p_cur_interval <= priv->concheck_x[IS_IPv4].p_max_interval) { /* we currently have a shorter interval set, than what we now have. Either, * because we are probing, or because the previous max interval was shorter. * @@ -2576,17 +2595,17 @@ concheck_periodic_schedule_set (NMDevice *self, return; } - cur_expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_max_interval * NM_UTILS_NS_PER_SECOND); + cur_expiry = priv->concheck_x[IS_IPv4].p_cur_basetime_ns + (priv->concheck_x[IS_IPv4].p_max_interval * NM_UTILS_NS_PER_SECOND); nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); - priv->concheck_p_cur_interval = priv->concheck_p_max_interval; + priv->concheck_x[IS_IPv4].p_cur_interval = priv->concheck_x[IS_IPv4].p_max_interval; if (cur_expiry <= now_ns) { /* Since the last time we scheduled a periodic check, already more than the * new max_interval passed. We need to start a check right away (and * schedule a timeout in cur-interval in the future). */ - priv->concheck_p_cur_basetime_ns = now_ns; - if (concheck_periodic_schedule_do (self, now_ns)) - concheck_start (self, NULL, NULL, TRUE); + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = now_ns; + if (concheck_periodic_schedule_do (self, addr_family, now_ns)) + concheck_start (self, addr_family, NULL, NULL, TRUE); } else { /* we are reducing the max-interval to a shorter interval that we have currently * scheduled (with cur_interval). @@ -2594,24 +2613,26 @@ concheck_periodic_schedule_set (NMDevice *self, * However, since the last time we scheduled the check, not even the new max-interval * expired. All we need to do, is reschedule the timer to expire sooner. The cur_basetime * is unchanged. */ - concheck_periodic_schedule_do (self, now_ns); + concheck_periodic_schedule_do (self, addr_family, now_ns); } return; case CONCHECK_SCHEDULE_CHECK_EXTERNAL: /* a external connectivity check delays our periodic check. We reset the counter. */ - priv->concheck_p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); - concheck_periodic_schedule_do (self, now_ns); + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); + concheck_periodic_schedule_do (self, addr_family, now_ns); return; case CONCHECK_SCHEDULE_CHECK_PERIODIC: { gboolean any_periodic_pending; NMDeviceConnectivityHandle *handle; - guint old_interval = priv->concheck_p_cur_interval; + guint old_interval = priv->concheck_x[IS_IPv4].p_cur_interval; any_periodic_pending = FALSE; c_list_for_each_entry (handle, &priv->concheck_lst_head, concheck_lst) { + if (handle->addr_family != addr_family) + continue; if (handle->is_periodic_bump) { handle->is_periodic_bump = FALSE; handle->is_periodic_bump_on_complete = FALSE; @@ -2622,7 +2643,7 @@ concheck_periodic_schedule_set (NMDevice *self, /* we reached a timeout to schedule a new periodic request, however we still * have period requests pending that didn't complete yet. We need to bump the * interval already. */ - priv->concheck_p_cur_interval = NM_MIN (old_interval * 2, priv->concheck_p_max_interval); + priv->concheck_x[IS_IPv4].p_cur_interval = NM_MIN (old_interval * 2, priv->concheck_x[IS_IPv4].p_max_interval); } /* we just reached a timeout. The expected expiry (exp_expiry) should be @@ -2630,13 +2651,13 @@ concheck_periodic_schedule_set (NMDevice *self, * * We want to reschedule the timeout at exp_expiry (aka now) + cur_interval. */ nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); - exp_expiry = priv->concheck_p_cur_basetime_ns + (old_interval * NM_UTILS_NS_PER_SECOND); - new_expiry = exp_expiry + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + exp_expiry = priv->concheck_x[IS_IPv4].p_cur_basetime_ns + (old_interval * NM_UTILS_NS_PER_SECOND); + new_expiry = exp_expiry + (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); tdiff = NM_MAX (new_expiry - now_ns, 0); - priv->concheck_p_cur_basetime_ns = (now_ns + tdiff) - (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); - if (concheck_periodic_schedule_do (self, now_ns)) { - handle = concheck_start (self, NULL, NULL, TRUE); - if (old_interval != priv->concheck_p_cur_interval) { + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = (now_ns + tdiff) - (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); + if (concheck_periodic_schedule_do (self, addr_family, now_ns)) { + handle = concheck_start (self, addr_family, NULL, NULL, TRUE); + if (old_interval != priv->concheck_x[IS_IPv4].p_cur_interval) { /* we just bumped the interval already when scheduling this check. * When the handle returns, don't bump a second time. * @@ -2651,13 +2672,13 @@ concheck_periodic_schedule_set (NMDevice *self, /* we just got an event that we lost connectivity (that is, concheck returned). We reset * the interval to min/max or increase the probe interval (bump). */ case CONCHECK_SCHEDULE_RETURNED_MIN: - priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_max_interval, CONCHECK_P_PROBE_INTERVAL); + priv->concheck_x[IS_IPv4].p_cur_interval = NM_MIN (priv->concheck_x[IS_IPv4].p_max_interval, CONCHECK_P_PROBE_INTERVAL); break; case CONCHECK_SCHEDULE_RETURNED_MAX: - priv->concheck_p_cur_interval = priv->concheck_p_max_interval; + priv->concheck_x[IS_IPv4].p_cur_interval = priv->concheck_x[IS_IPv4].p_max_interval; break; case CONCHECK_SCHEDULE_RETURNED_BUMP: - priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_cur_interval * 2, priv->concheck_p_max_interval); + priv->concheck_x[IS_IPv4].p_cur_interval = NM_MIN (priv->concheck_x[IS_IPv4].p_cur_interval * 2, priv->concheck_x[IS_IPv4].p_max_interval); break; } @@ -2667,38 +2688,40 @@ concheck_periodic_schedule_set (NMDevice *self, * last check, instead of counting from now. The reason is that we want that the times * when we schedule checks be at precise intervals, without including the time it took for * the connectivity check. */ - new_expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + new_expiry = priv->concheck_x[IS_IPv4].p_cur_basetime_ns + (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); tdiff = NM_MAX (new_expiry - nm_utils_get_monotonic_timestamp_ns_cached (&now_ns), 0); - priv->concheck_p_cur_basetime_ns = now_ns + tdiff - (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); - concheck_periodic_schedule_do (self, now_ns); + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = now_ns + tdiff - (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); + concheck_periodic_schedule_do (self, addr_family, now_ns); } static void -concheck_update_interval (NMDevice *self, gboolean check_now) +concheck_update_interval (NMDevice *self, int addr_family, gboolean check_now) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); guint new_interval; + const gboolean IS_IPv4 = (addr_family == AF_INET); new_interval = nm_connectivity_get_interval (concheck_get_mgr (self)); new_interval = NM_MIN (new_interval, 7 *24 * 3600); - if (new_interval != priv->concheck_p_max_interval) { + if (new_interval != priv->concheck_x[IS_IPv4].p_max_interval) { _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: set interval to %u seconds", new_interval); - priv->concheck_p_max_interval = new_interval; + priv->concheck_x[IS_IPv4].p_max_interval = new_interval; } if (!new_interval) { /* this will cancel any potentially pending timeout because max-interval is zero. * But it logs a nice message... */ - concheck_periodic_schedule_do (self, 0); + concheck_periodic_schedule_do (self, addr_family, 0); /* also update the fake connectivity state. */ - concheck_update_state (self, NM_CONNECTIVITY_FAKE, TRUE); + concheck_update_state (self, addr_family, NM_CONNECTIVITY_FAKE, TRUE); return; } concheck_periodic_schedule_set (self, + addr_family, check_now ? CONCHECK_SCHEDULE_UPDATE_INTERVAL_RESTART : CONCHECK_SCHEDULE_UPDATE_INTERVAL); @@ -2707,13 +2730,16 @@ concheck_update_interval (NMDevice *self, gboolean check_now) void nm_device_check_connectivity_update_interval (NMDevice *self) { - concheck_update_interval (self, FALSE); + concheck_update_interval (self, AF_INET, FALSE); + concheck_update_interval (self, AF_INET6, FALSE); } static void -concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow_periodic_bump) +concheck_update_state (NMDevice *self, int addr_family, + NMConnectivityState state, gboolean allow_periodic_bump) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const gboolean IS_IPv4 = (addr_family == AF_INET); /* @state is a result of the connectivity check. We only expect a precise * number of possible values. */ @@ -2726,7 +2752,7 @@ concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow if (state == NM_CONNECTIVITY_ERROR) { /* on error, we don't change the current connectivity state, * except making UNKNOWN to NONE. */ - state = priv->connectivity_state; + state = priv->concheck_x[IS_IPv4].state; if (state == NM_CONNECTIVITY_UNKNOWN) state = NM_CONNECTIVITY_NONE; } else if (state == NM_CONNECTIVITY_FAKE) { @@ -2745,11 +2771,11 @@ concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow state = NM_CONNECTIVITY_NONE; } - if (priv->connectivity_state == state) { + if (priv->concheck_x[IS_IPv4].state == state) { /* we got a connectivty update, but the state didn't change. If we were probing, * we bump the probe frequency. */ if (allow_periodic_bump) - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_BUMP); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_RETURNED_BUMP); return; } /* we need to update the probe interval before emitting signals. Emitting @@ -2758,20 +2784,20 @@ concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow if (state == NM_CONNECTIVITY_FULL) { /* we reached full connectivity state. Stop probing by setting the * interval to the max. */ - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_MAX); - } else if (priv->connectivity_state == NM_CONNECTIVITY_FULL) { + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_RETURNED_MAX); + } else if (priv->concheck_x[IS_IPv4].state == NM_CONNECTIVITY_FULL) { /* we are about to loose connectivity. (re)start probing by setting * the timeout interval to the min. */ - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_MIN); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_RETURNED_MIN); } else { if (allow_periodic_bump) - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_BUMP); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_RETURNED_BUMP); } _LOGD (LOGD_CONCHECK, "connectivity state changed from %s to %s", - nm_connectivity_state_to_string (priv->connectivity_state), + nm_connectivity_state_to_string (priv->concheck_x[IS_IPv4].state), nm_connectivity_state_to_string (state)); - priv->connectivity_state = state; + priv->concheck_x[IS_IPv4].state = state; _notify (self, PROP_CONNECTIVITY); g_signal_emit (self, signals[CONNECTIVITY_CHANGED], 0); @@ -2788,9 +2814,10 @@ concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow } static void -concheck_handle_complete (NMDeviceConnectivityHandle *handle, - GError *error) +concheck_handle_complete (NMDeviceConnectivityHandle *handle, GError *error) { + const gboolean IS_IPv4 = (handle->addr_family == AF_INET); + /* The moment we invoke the callback, we unlink it. It signals * that @handle is handled -- as far as the callee of callback * is concerned. */ @@ -2802,7 +2829,7 @@ concheck_handle_complete (NMDeviceConnectivityHandle *handle, if (handle->callback) { handle->callback (handle->self, handle, - NM_DEVICE_GET_PRIVATE (handle->self)->connectivity_state, + NM_DEVICE_GET_PRIVATE (handle->self)->concheck_x[IS_IPv4].state, error, handle->user_data); } @@ -2864,6 +2891,8 @@ concheck_cb (NMConnectivity *connectivity, any_periodic_before = FALSE; any_periodic_after = FALSE; c_list_for_each_entry (other_handle, &priv->concheck_lst_head, concheck_lst) { + if (other_handle->addr_family != handle->addr_family) + continue; if (other_handle->is_periodic_bump_on_complete) { if (other_handle->seq < seq) any_periodic_before = TRUE; @@ -2890,7 +2919,7 @@ concheck_cb (NMConnectivity *connectivity, } /* first update the new state, and emit signals. */ - concheck_update_state (self, state, allow_periodic_bump); + concheck_update_state (self, handle->addr_family, state, allow_periodic_bump); handle_is_alive = FALSE; @@ -2902,6 +2931,8 @@ concheck_cb (NMConnectivity *connectivity, * @handle, as they are automatically obsoleted. */ check_handles: c_list_for_each_entry (other_handle, &priv->concheck_lst_head, concheck_lst) { + if (other_handle->addr_family != handle->addr_family) + continue; if (other_handle->seq >= seq) { /* it's not guaranteed that @handle is still in the list. It might already * be canceled while invoking callbacks for a previous other_handle. @@ -2939,6 +2970,7 @@ check_handles: static NMDeviceConnectivityHandle * concheck_start (NMDevice *self, + int addr_family, NMDeviceConnectivityCallback callback, gpointer user_data, gboolean is_periodic) @@ -2959,6 +2991,7 @@ concheck_start (NMDevice *self, handle->is_periodic = is_periodic; handle->is_periodic_bump = is_periodic; handle->is_periodic_bump_on_complete = is_periodic; + handle->addr_family = addr_family; c_list_link_tail (&priv->concheck_lst_head, &handle->concheck_lst); @@ -2967,6 +3000,7 @@ concheck_start (NMDevice *self, is_periodic ? ", periodic-check" : ""); handle->c_handle = nm_connectivity_check_start (concheck_get_mgr (self), + handle->addr_family, nm_device_get_ip_ifindex (self), nm_device_get_ip_iface (self), concheck_cb, @@ -2976,6 +3010,7 @@ concheck_start (NMDevice *self, NMDeviceConnectivityHandle * nm_device_check_connectivity (NMDevice *self, + int addr_family, NMDeviceConnectivityCallback callback, gpointer user_data) { @@ -2984,8 +3019,8 @@ nm_device_check_connectivity (NMDevice *self, if (!concheck_is_possible (self)) return NULL; - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_CHECK_EXTERNAL); - handle = concheck_start (self, callback, user_data, FALSE); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_CHECK_EXTERNAL); + handle = concheck_start (self, addr_family, callback, user_data, FALSE); return handle; } @@ -3009,9 +3044,13 @@ nm_device_check_connectivity_cancel (NMDeviceConnectivityHandle *handle) NMConnectivityState nm_device_get_connectivity_state (NMDevice *self) { + NMDevicePrivate *priv; + g_return_val_if_fail (NM_IS_DEVICE (self), NM_CONNECTIVITY_UNKNOWN); - return NM_DEVICE_GET_PRIVATE (self)->connectivity_state; + priv = NM_DEVICE_GET_PRIVATE (self); + + return NM_MAX (priv->concheck_x[0].state, priv->concheck_x[1].state); } /*****************************************************************************/ @@ -7083,7 +7122,7 @@ ip_config_merge_and_apply (NMDevice *self, if (commit) { gboolean v; - v = default_route_metric_penalty_detect (self); + v = default_route_metric_penalty_detect (self, addr_family); if (IS_IPv4) priv->default_route_metric_penalty_ip4_has = v; else @@ -14851,8 +14890,8 @@ _set_state_full (NMDevice *self, if (ip_config_valid (old_state) && !ip_config_valid (state)) notify_ip_properties (self); - concheck_update_interval (self, - state == NM_DEVICE_STATE_ACTIVATED); + concheck_update_interval (self, AF_INET, state == NM_DEVICE_STATE_ACTIVATED); + concheck_update_interval (self, AF_INET6, state == NM_DEVICE_STATE_ACTIVATED); /* Dispose of the cached activation request */ if (req) @@ -15816,7 +15855,8 @@ nm_device_init (NMDevice *self) c_list_init (&self->devices_lst); c_list_init (&priv->slaves); - priv->connectivity_state = NM_CONNECTIVITY_UNKNOWN; + priv->concheck_x[0].state = NM_CONNECTIVITY_UNKNOWN; + priv->concheck_x[1].state = NM_CONNECTIVITY_UNKNOWN; nm_dbus_track_obj_path_init (&priv->parent_device, G_OBJECT (self), obj_properties[PROP_PARENT]); nm_dbus_track_obj_path_init (&priv->act_request, G_OBJECT (self), obj_properties[PROP_ACTIVE_CONNECTION]); @@ -16017,7 +16057,8 @@ dispose (GObject *object) g_clear_object (&priv->lldp_listener); } - nm_clear_g_source (&priv->concheck_p_cur_id); + nm_clear_g_source (&priv->concheck_x[0].p_cur_id); + nm_clear_g_source (&priv->concheck_x[1].p_cur_id); G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); @@ -16355,7 +16396,7 @@ get_property (GObject *object, guint prop_id, g_value_set_uint64 (value, priv->stats.rx_bytes); break; case PROP_CONNECTIVITY: - g_value_set_uint (value, priv->connectivity_state); + g_value_set_uint (value, nm_device_get_connectivity_state (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 056d1c9cab..d32bf25250 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -818,6 +818,7 @@ typedef void (*NMDeviceConnectivityCallback) (NMDevice *self, void nm_device_check_connectivity_update_interval (NMDevice *self); NMDeviceConnectivityHandle *nm_device_check_connectivity (NMDevice *self, + int addr_family, NMDeviceConnectivityCallback callback, gpointer user_data); diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c index 2ce5481b49..3e39960b4d 100644 --- a/src/nm-connectivity.c +++ b/src/nm-connectivity.c @@ -68,6 +68,7 @@ struct _NMConnectivityCheckHandle { gpointer user_data; char *ifspec; + int addr_family; #if WITH_CONCHECK struct { @@ -145,9 +146,10 @@ NM_DEFINE_SINGLETON_GETTER (NMConnectivity, nm_connectivity_get, NM_TYPE_CONNECT _nm_log (__level, _NMLOG2_DOMAIN, 0, \ (cb_data->ifspec ? &cb_data->ifspec[3] : NULL), \ NULL, \ - "connectivity: (%s) " \ + "connectivity: (%s,AF_INET%s) " \ _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - (cb_data->ifspec ? &cb_data->ifspec[3] : "") \ + (cb_data->ifspec ? &cb_data->ifspec[3] : ""), \ + (cb_data->addr_family == AF_INET6 ? "6" : "") \ _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ } \ } G_STMT_END @@ -572,6 +574,7 @@ do_curl_request (NMConnectivityCheckHandle *cb_data) { NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (cb_data->self); CURL *ehandle; + long resolve; ehandle = curl_easy_init (); if (!ehandle) { @@ -579,6 +582,21 @@ do_curl_request (NMConnectivityCheckHandle *cb_data) return; } + switch (cb_data->addr_family) { + case AF_INET: + resolve = CURL_IPRESOLVE_V4; + break; + case AF_INET6: + resolve = CURL_IPRESOLVE_V6; + break; + case AF_UNSPEC: + resolve = CURL_IPRESOLVE_WHATEVER; + break; + default: + resolve = CURL_IPRESOLVE_WHATEVER; + g_warn_if_reached (); + } + cb_data->concheck.response = g_strdup (priv->response); cb_data->concheck.curl_ehandle = ehandle; cb_data->concheck.request_headers = curl_slist_append (NULL, "Connection: close"); @@ -593,6 +611,7 @@ do_curl_request (NMConnectivityCheckHandle *cb_data) curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->concheck.request_headers); curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec); curl_easy_setopt (ehandle, CURLOPT_RESOLVE, cb_data->concheck.hosts); + curl_easy_setopt (ehandle, CURLOPT_IPRESOLVE, resolve); curl_multi_add_handle (priv->concheck.curl_mhandle, ehandle); } @@ -607,7 +626,7 @@ resolve_cb (GObject *object, GAsyncResult *res, gpointer user_data) GVariant *addresses; gsize no_addresses; int ifindex; - int family; + int addr_family; GVariant *address = NULL; const guchar *address_buf; gsize len = 0; @@ -635,14 +654,13 @@ resolve_cb (GObject *object, GAsyncResult *res, gpointer user_data) g_variant_unref (result); for (i = 0; i < no_addresses; i++) { - g_variant_get_child (addresses, i, "(ii@ay)", &ifindex, &family, &address); + g_variant_get_child (addresses, i, "(ii@ay)", &ifindex, &addr_family, &address); address_buf = g_variant_get_fixed_array (address, &len, 1); - if ( (family == AF_INET && len == sizeof (struct in_addr)) - || (family == AF_INET6 && len == sizeof (struct in6_addr))) { - inet_ntop (family, address_buf, str, sizeof (str)); - host = g_strdup_printf ("%s:%s:%s", priv->host, - priv->port ? priv->port : "80", str); + if ( (addr_family == AF_INET && len == sizeof (struct in_addr)) + || (addr_family == AF_INET6 && len == sizeof (struct in6_addr))) { + inet_ntop (addr_family, address_buf, str, sizeof (str)); + host = g_strdup_printf ("%s:%s:%s", priv->host, priv->port ?: "80", str); cb_data->concheck.hosts = curl_slist_append (cb_data->concheck.hosts, host); _LOG2T ("adding '%s' to curl resolve list", host); g_free (host); @@ -685,7 +703,7 @@ resolved_proxy_created (GObject *object, GAsyncResult *res, gpointer user_data) g_variant_new ("(isit)", cb_data->concheck.ifindex, priv->host, - (gint32) AF_UNSPEC, + (gint32) cb_data->addr_family, (guint64) SD_RESOLVED_DNS), G_DBUS_CALL_FLAGS_NONE, -1, @@ -699,6 +717,7 @@ resolved_proxy_created (GObject *object, GAsyncResult *res, gpointer user_data) NMConnectivityCheckHandle * nm_connectivity_check_start (NMConnectivity *self, + int addr_family, int ifindex, const char *iface, NMConnectivityCheckCallback callback, @@ -719,6 +738,7 @@ nm_connectivity_check_start (NMConnectivity *self, cb_data->callback = callback; cb_data->user_data = user_data; cb_data->completed_state = NM_CONNECTIVITY_UNKNOWN; + cb_data->addr_family = addr_family; if (iface) cb_data->ifspec = g_strdup_printf ("if!%s", iface); diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h index 2bf07bc080..fad248be7b 100644 --- a/src/nm-connectivity.h +++ b/src/nm-connectivity.h @@ -58,6 +58,7 @@ typedef void (*NMConnectivityCheckCallback) (NMConnectivity *self, gpointer user_data); NMConnectivityCheckHandle *nm_connectivity_check_start (NMConnectivity *self, + int family, int ifindex, const char *iface, NMConnectivityCheckCallback callback, diff --git a/src/nm-manager.c b/src/nm-manager.c index 27d7c5ad98..120cecd928 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -2812,6 +2812,7 @@ device_realized (NMDevice *device, static void device_connectivity_changed (NMDevice *device, + GParamSpec *pspec, NMManager *self) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); @@ -6041,6 +6042,12 @@ check_connectivity_auth_done_cb (NMAuthChain *chain, c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) { if (nm_device_check_connectivity (device, + AF_INET, + device_connectivity_done, + data)) + data->remaining++; + if (nm_device_check_connectivity (device, + AF_INET6, device_connectivity_done, data)) data->remaining++; From d8971fcbcd3309e7abf6e30c4bcf5283858f4ce3 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 3 Jul 2018 19:09:34 +0200 Subject: [PATCH 5/8] device: expose connectivity check result on a device Separate properties for IPv4 and IPv6. --- .../org.freedesktop.NetworkManager.Device.xml | 22 +++++++++++++ src/devices/nm-device.c | 33 ++++++++++--------- src/devices/nm-device.h | 4 +-- src/nm-manager.c | 5 ++- 4 files changed, 45 insertions(+), 19 deletions(-) diff --git a/introspection/org.freedesktop.NetworkManager.Device.xml b/introspection/org.freedesktop.NetworkManager.Device.xml index be0a612cc4..f168115697 100644 --- a/introspection/org.freedesktop.NetworkManager.Device.xml +++ b/introspection/org.freedesktop.NetworkManager.Device.xml @@ -256,6 +256,28 @@ --> + + + + + +