Commit 9d43869e authored by Lubomir Rintel's avatar Lubomir Rintel 🥕

core: make connectivity checking per-device

This moves tracking of connectivity to NMDevice and makes the NMManager
negotiate the best of known connectivity states of devices. The NMConnectivity
singleton handles its own configuration and scheduling of the permission
checks, but otherwise greatly simplifies it.

This will be useful to determine correct metrics for multiple default routes
depending on actual internet connectivity.

The per-device connection checks is not yet exposed on the D-Bus, since they
probably should be per-address-family as well.
parent 4ec7dd98
......@@ -1428,7 +1428,6 @@ src_libNetworkManager_la_SOURCES = \
src/nm-config.h \
src/nm-config-data.c \
src/nm-config-data.h \
src/nm-connectivity.c \
src/nm-connectivity.h \
src/nm-dcb.c \
src/nm-dcb.h \
......@@ -1466,6 +1465,11 @@ src_libNetworkManager_la_SOURCES = \
\
$(NULL)
if WITH_CONCHECK
src_libNetworkManager_la_SOURCES += \
src/nm-connectivity.c
endif
src_libNetworkManager_la_LIBADD = \
src/libNetworkManagerBase.la \
src/libsystemd-nm.la \
......
......@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2005 - 2013 Red Hat, Inc.
* Copyright (C) 2005 - 2017 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
......@@ -66,6 +66,8 @@
#include "nm-lldp-listener.h"
#include "nm-audit-manager.h"
#include "nm-arping-manager.h"
#include "nm-connectivity.h"
#include "nm-dbus-interface.h"
#include "nm-device-logging.h"
_LOG_DECLARE_SELF (NMDevice);
......@@ -189,6 +191,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice,
PROP_REFRESH_RATE_MS,
PROP_TX_BYTES,
PROP_RX_BYTES,
PROP_CONNECTIVITY,
);
typedef struct _NMDevicePrivate {
......@@ -442,6 +445,9 @@ typedef struct _NMDevicePrivate {
NMSettings *settings;
NMLldpListener *lldp_listener;
NMConnectivityState connectivity_state;
guint concheck_periodic_id;
guint64 concheck_seq;
guint check_delete_unrealized_id;
......@@ -1697,6 +1703,167 @@ nm_device_get_physical_port_id (NMDevice *self)
/*****************************************************************************/
static void
update_connectivity_state (NMDevice *self, NMConnectivityState state)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
/* If the connectivity check is disabled, make an optimistic guess. */
if (state == NM_CONNECTIVITY_UNKNOWN) {
if (priv->state == NM_DEVICE_STATE_ACTIVATED) {
if (priv->default_route.v4_has || priv->default_route.v6_has)
state = NM_CONNECTIVITY_FULL;
else
state = NM_CONNECTIVITY_LIMITED;
} else {
state = NM_CONNECTIVITY_NONE;
}
}
if (priv->connectivity_state != state) {
#if WITH_CONCHECK
_LOGD (LOGD_CONCHECK, "state changed from %s to %s",
nm_connectivity_state_to_string (priv->connectivity_state),
nm_connectivity_state_to_string (state));
#endif
priv->connectivity_state = state;
_notify (self, PROP_CONNECTIVITY);
}
}
typedef struct {
NMDevice *self;
NMDeviceConnectivityCallback callback;
gpointer user_data;
guint64 seq;
} ConnectivityCheckData;
static void
concheck_done (ConnectivityCheckData *data)
{
NMDevice *self = data->self;
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
/* The unsolicited connectivity checks don't hook a callback. */
if (data->callback)
data->callback (data->self, priv->connectivity_state, data->user_data);
g_object_unref (data->self);
g_slice_free (ConnectivityCheckData, data);
}
#if WITH_CONCHECK
static void
concheck_cb (GObject *source_object, GAsyncResult *result, gpointer user_data)
{
ConnectivityCheckData *data = user_data;
NMDevice *self = data->self;
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMConnectivity *connectivity = NM_CONNECTIVITY (source_object);
NMConnectivityState state;
GError *error = NULL;
state = nm_connectivity_check_finish (connectivity, result, &error);
if (error) {
_LOGW (LOGD_DEVICE, "connectivity checking on '%s' failed: %s",
nm_device_get_iface (self), error->message);
g_error_free (error);
}
if (data->seq == priv->concheck_seq)
update_connectivity_state (data->self, state);
concheck_done (data);
}
#endif /* WITH_CONCHECK */
static gboolean
no_concheck (gpointer user_data)
{
ConnectivityCheckData *data = user_data;
concheck_done (data);
return G_SOURCE_REMOVE;
}
void
nm_device_check_connectivity (NMDevice *self,
NMDeviceConnectivityCallback callback,
gpointer user_data)
{
ConnectivityCheckData *data;
#if WITH_CONCHECK
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
#endif
data = g_slice_new0 (ConnectivityCheckData);
data->self = g_object_ref (self);
data->callback = callback;
data->user_data = user_data;
#if WITH_CONCHECK
if (priv->concheck_periodic_id) {
data->seq = ++priv->concheck_seq;
/* Kick off a real connectivity check. */
nm_connectivity_check_async (nm_connectivity_get (),
nm_device_get_iface (self),
concheck_cb,
data);
return;
}
#endif
/* Fake one. */
g_idle_add (no_concheck, data);
}
NMConnectivityState
nm_device_get_connectivity_state (NMDevice *self)
{
g_return_val_if_fail (NM_IS_DEVICE (self), NM_CONNECTIVITY_UNKNOWN);
return NM_DEVICE_GET_PRIVATE (self)->connectivity_state;
}
#if WITH_CONCHECK
static void
concheck_periodic (NMConnectivity *connectivity, NMDevice *self)
{
nm_device_check_connectivity (self, NULL, NULL);
}
#endif
static void
concheck_periodic_update (NMDevice *self)
{
#if WITH_CONCHECK
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
gboolean check_enable;
check_enable = (priv->state == NM_DEVICE_STATE_ACTIVATED)
&& (priv->default_route.v4_has || priv->default_route.v6_has);
if (check_enable && !priv->concheck_periodic_id) {
/* We just gained a default route. Enable periodic checking. */
priv->concheck_periodic_id = g_signal_connect (nm_connectivity_get (),
NM_CONNECTIVITY_PERIODIC_CHECK,
G_CALLBACK (concheck_periodic), self);
/* Also kick off a check right away. */
nm_device_check_connectivity (self, NULL, NULL);
} else if (!check_enable && priv->concheck_periodic_id) {
/* The default route has gone off, and so has connectivity. */
update_connectivity_state (self, NM_CONNECTIVITY_NONE);
g_signal_handler_disconnect (nm_connectivity_get (), priv->concheck_periodic_id);
priv->concheck_periodic_id = 0;
}
#else
/* update_connectivity_state() figures out how to lie about
* connectivity state if the actual state is not really known. */
update_connectivity_state (self, NM_CONNECTIVITY_UNKNOWN);
#endif
}
/*****************************************************************************/
static SlaveInfo *
find_slave_info (NMDevice *self, NMDevice *slave)
{
......@@ -9483,6 +9650,8 @@ nm_device_set_ip4_config (NMDevice *self,
}
nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self);
concheck_periodic_update (self);
if (!nm_device_sys_iface_state_is_external_or_assume (self))
ip4_rp_filter_update (self);
......@@ -12445,6 +12614,8 @@ _set_state_full (NMDevice *self,
if (ip_config_valid (old_state) && !ip_config_valid (state))
notify_ip_properties (self);
concheck_periodic_update (self);
/* Dispose of the cached activation request */
if (req)
g_object_unref (req);
......@@ -13837,6 +14008,9 @@ get_property (GObject *object, guint prop_id,
case PROP_RX_BYTES:
g_value_set_uint64 (value, priv->stats.rx_bytes);
break;
case PROP_CONNECTIVITY:
g_value_set_uint (value, priv->connectivity_state);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
......@@ -14107,6 +14281,13 @@ nm_device_class_init (NMDeviceClass *klass)
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
/* Connectivity */
obj_properties[PROP_CONNECTIVITY] =
g_param_spec_uint (NM_DEVICE_CONNECTIVITY, "", "",
NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
signals[STATE_CHANGED] =
......
......@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2005 - 2013 Red Hat, Inc.
* Copyright (C) 2005 - 2017 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
......@@ -135,6 +135,8 @@ nm_device_state_reason_check (NMDeviceStateReason reason)
#define NM_DEVICE_STATISTICS_TX_BYTES "tx-bytes"
#define NM_DEVICE_STATISTICS_RX_BYTES "rx-bytes"
#define NM_DEVICE_CONNECTIVITY "connectivity"
#define NM_TYPE_DEVICE (nm_device_get_type ())
#define NM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE, NMDevice))
#define NM_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE, NMDeviceClass))
......@@ -689,4 +691,12 @@ gboolean nm_device_hw_addr_get_cloned (NMDevice *self,
gboolean *preserve,
GError **error);
typedef void (*NMDeviceConnectivityCallback) (NMDevice *self,
NMConnectivityState state,
gpointer user_data);
void nm_device_check_connectivity (NMDevice *self,
NMDeviceConnectivityCallback callback,
gpointer user_data);
NMConnectivityState nm_device_get_connectivity_state (NMDevice *self);
#endif /* __NETWORKMANAGER_DEVICE_H__ */
......@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2004 - 2012 Red Hat, Inc.
* Copyright (C) 2004 - 2017 Red Hat, Inc.
* Copyright (C) 2005 - 2008 Novell, Inc.
*/
......@@ -49,6 +49,7 @@
#include "nm-auth-manager.h"
#include "nm-core-internal.h"
#include "nm-exported-object.h"
#include "nm-connectivity.h"
#include "dns/nm-dns-manager.h"
#include "systemd/nm-sd.h"
......@@ -391,6 +392,9 @@ main (int argc, char *argv[])
nm_linux_platform_setup ();
NM_UTILS_KEEP_ALIVE (config, NM_PLATFORM_GET, "NMConfig-depends-on-NMPlatform");
#if WITH_CONCHECK
NM_UTILS_KEEP_ALIVE (nm_manager_get (), nm_connectivity_get (), "NMManager-depends-on-NMConnectivity");
#endif
nm_dispatcher_init ();
......
This diff is collapsed.
......@@ -16,6 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2011 Thomas Bechtold <thomasbechtold@jpberlin.de>
* Copyright (C) 2017 Red Hat, Inc.
*/
#ifndef __NETWORKMANAGER_CONNECTIVITY_H__
......@@ -30,27 +31,18 @@
#define NM_IS_CONNECTIVITY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CONNECTIVITY))
#define NM_CONNECTIVITY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CONNECTIVITY, NMConnectivityClass))
#define NM_CONNECTIVITY_URI "uri"
#define NM_CONNECTIVITY_INTERVAL "interval"
#define NM_CONNECTIVITY_RESPONSE "response"
#define NM_CONNECTIVITY_STATE "state"
#define NM_CONNECTIVITY_PERIODIC_CHECK "nm-connectivity-periodic-check"
typedef struct _NMConnectivityClass NMConnectivityClass;
GType nm_connectivity_get_type (void);
const char *nm_connectivity_state_to_string (NMConnectivityState state);
NMConnectivity *nm_connectivity_new (const char *uri,
guint interval,
const char *response);
NMConnectivity *nm_connectivity_get (void);
void nm_connectivity_set_online (NMConnectivity *self,
gboolean online);
NMConnectivityState nm_connectivity_get_state (NMConnectivity *self);
const char *nm_connectivity_state_to_string (NMConnectivityState state);
void nm_connectivity_check_async (NMConnectivity *self,
const char *iface,
GAsyncReadyCallback callback,
gpointer user_data);
NMConnectivityState nm_connectivity_check_finish (NMConnectivity *self,
......
......@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2004 - 2012 Red Hat, Inc.
* Copyright (C) 2004 - 2017 Red Hat, Inc.
* Copyright (C) 2005 - 2008 Novell, Inc.
*/
......@@ -507,6 +507,7 @@ _dispatcher_call (NMDispatcherAction action,
GError *error = NULL;
static guint request_counter = 0;
guint reqid = ++request_counter;
const char *connectivity_state_string = "UNKNOWN";
if (!dispatcher_proxy)
return FALSE;
......@@ -616,6 +617,10 @@ _dispatcher_call (NMDispatcherAction action,
if (!device_dhcp6_props)
device_dhcp6_props = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0));
#if WITH_CONCHECK
connectivity_state_string = nm_connectivity_state_to_string (connectivity_state);
#endif
/* Send the action to the dispatcher */
if (blocking) {
GVariant *ret;
......@@ -632,7 +637,7 @@ _dispatcher_call (NMDispatcherAction action,
&device_ip6_props,
device_dhcp4_props,
device_dhcp6_props,
nm_connectivity_state_to_string (connectivity_state),
connectivity_state_string,
vpn_iface ? vpn_iface : "",
&vpn_proxy_props,
&vpn_ip4_props,
......@@ -670,7 +675,7 @@ _dispatcher_call (NMDispatcherAction action,
&device_ip6_props,
device_dhcp4_props,
device_dhcp6_props,
nm_connectivity_state_to_string (connectivity_state),
connectivity_state_string,
vpn_iface ? vpn_iface : "",
&vpn_proxy_props,
&vpn_ip4_props,
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment