merge: branch 'lr/conn-check-af'

https://github.com/NetworkManager/NetworkManager/pull/157
This commit is contained in:
Lubomir Rintel 2018-09-24 15:44:11 +02:00
commit fa6e4c7eb0
21 changed files with 2530 additions and 1363 deletions

View file

@ -1380,3 +1380,20 @@ nmc_error_get_simple_message (GError *error)
else
return error->message;
}
/*****************************************************************************/
NM_UTILS_LOOKUP_STR_DEFINE (nm_connectivity_to_string_no_l10n, NMConnectivityState,
NM_UTILS_LOOKUP_DEFAULT (N_("unknown")),
NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_NONE, N_("none")),
NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_PORTAL, N_("portal")),
NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_LIMITED, N_("limited")),
NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_FULL, N_("full")),
NM_UTILS_LOOKUP_ITEM_IGNORE (NM_CONNECTIVITY_UNKNOWN),
);
const char *
nm_connectivity_to_string (NMConnectivityState connectivity)
{
return _(nm_connectivity_to_string_no_l10n (connectivity));
}

View file

@ -93,4 +93,7 @@ extern const NmcMetaGenericInfo *const metagen_ip4_config[];
extern const NmcMetaGenericInfo *const metagen_ip6_config[];
extern const NmcMetaGenericInfo *const metagen_dhcp_config[];
const char *nm_connectivity_to_string (NMConnectivityState connectivity);
const char *nm_connectivity_to_string_no_l10n (NMConnectivityState connectivity);
#endif /* NMC_COMMON_H */

View file

@ -116,6 +116,12 @@ _metagen_device_status_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
case NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_STATE:
return nmc_meta_generic_get_str_i18n (nmc_device_state_to_string (nm_device_get_state (d)),
get_type);
case NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_IP4_CONNECTIVITY:
return nmc_meta_generic_get_str_i18n (nm_connectivity_to_string (nm_device_get_connectivity (d, AF_INET)),
get_type);
case NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_IP6_CONNECTIVITY:
return nmc_meta_generic_get_str_i18n (nm_connectivity_to_string (nm_device_get_connectivity (d, AF_INET6)),
get_type);
case NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_DBUS_PATH:
return nm_object_get_path (NM_OBJECT (d));
case NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CONNECTION:
@ -137,13 +143,15 @@ _metagen_device_status_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
const NmcMetaGenericInfo *const metagen_device_status[_NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_NUM + 1] = {
#define _METAGEN_DEVICE_STATUS(type, name) \
[type] = NMC_META_GENERIC(name, .info_type = type, .get_fcn = _metagen_device_status_get_fcn)
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_DEVICE, "DEVICE"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_TYPE, "TYPE"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_STATE, "STATE"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_DBUS_PATH, "DBUS-PATH"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CONNECTION, "CONNECTION"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CON_UUID, "CON-UUID"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CON_PATH, "CON-PATH"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_DEVICE, "DEVICE"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_TYPE, "TYPE"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_STATE, "STATE"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_IP4_CONNECTIVITY, "IP4-CONNECTIVITY"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_IP6_CONNECTIVITY, "IP6-CONNECTIVITY"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_DBUS_PATH, "DBUS-PATH"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CONNECTION, "CONNECTION"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CON_UUID, "CON-UUID"),
_METAGEN_DEVICE_STATUS (NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CON_PATH, "CON-PATH"),
};
/*****************************************************************************/
@ -155,6 +163,7 @@ _metagen_device_detail_general_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
NMActiveConnection *ac;
NMDeviceState state;
NMDeviceStateReason state_reason;
NMConnectivityState connectivity;
const char *s;
NMC_HANDLE_COLOR (NM_META_COLOR_NONE);
@ -194,6 +203,18 @@ _metagen_device_detail_general_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS)
state_reason,
nmc_device_reason_to_string (state_reason),
get_type));
case NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP4_CONNECTIVITY:
connectivity = nm_device_get_connectivity (d, AF_INET);
return (*out_to_free = nmc_meta_generic_get_enum_with_detail (NMC_META_GENERIC_GET_ENUM_TYPE_PARENTHESES,
connectivity,
nm_connectivity_to_string (connectivity),
get_type));
case NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP6_CONNECTIVITY:
connectivity = nm_device_get_connectivity (d, AF_INET6);
return (*out_to_free = nmc_meta_generic_get_enum_with_detail (NMC_META_GENERIC_GET_ENUM_TYPE_PARENTHESES,
connectivity,
nm_connectivity_to_string (connectivity),
get_type));
case NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_UDI:
return nm_device_get_udi (d);
case NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP_IFACE:
@ -244,6 +265,8 @@ const NmcMetaGenericInfo *const metagen_device_detail_general[_NMC_GENERIC_INFO_
_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_MTU, "MTU"),
_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_STATE, "STATE"),
_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_REASON, "REASON"),
_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP4_CONNECTIVITY, "IP4-CONNECTIVITY"),
_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP6_CONNECTIVITY, "IP6-CONNECTIVITY"),
_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_UDI, "UDI"),
_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP_IFACE, "IP-IFACE"),
_METAGEN_DEVICE_DETAIL_GENERAL (NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IS_SOFTWARE, "IS-SOFTWARE"),

View file

@ -78,21 +78,6 @@ state_to_color (NMState state)
}
}
NM_UTILS_LOOKUP_STR_DEFINE_STATIC (nm_connectivity_to_string_no_l10n, NMConnectivityState,
NM_UTILS_LOOKUP_DEFAULT (N_("unknown")),
NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_NONE, N_("none")),
NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_PORTAL, N_("portal")),
NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_LIMITED, N_("limited")),
NM_UTILS_LOOKUP_ITEM (NM_CONNECTIVITY_FULL, N_("full")),
NM_UTILS_LOOKUP_ITEM_IGNORE (NM_CONNECTIVITY_UNKNOWN),
);
static const char *
nm_connectivity_to_string (NMConnectivityState connectivity)
{
return _(nm_connectivity_to_string_no_l10n (connectivity));
}
static NMMetaColor
connectivity_to_color (NMConnectivityState connectivity)
{

View file

@ -166,6 +166,8 @@ typedef enum {
NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_DEVICE = 0,
NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_TYPE,
NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_STATE,
NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_IP4_CONNECTIVITY,
NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_IP6_CONNECTIVITY,
NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_DBUS_PATH,
NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CONNECTION,
NMC_GENERIC_INFO_TYPE_DEVICE_STATUS_CON_UUID,
@ -184,6 +186,8 @@ typedef enum {
NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_MTU,
NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_STATE,
NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_REASON,
NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP4_CONNECTIVITY,
NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP6_CONNECTIVITY,
NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_UDI,
NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IP_IFACE,
NMC_GENERIC_INFO_TYPE_DEVICE_DETAIL_GENERAL_IS_SOFTWARE,

View file

@ -28,34 +28,34 @@ wlan1 wifi niedostępne --
wlan1 wifi niedostępne --
<<<
size: 762
size: 978
location: clients/tests/test-client.py:844:test_002()/3
cmd: $NMCLI -f all d
lang: C
returncode: 0
stdout: 636 bytes
stdout: 852 bytes
>>>
DEVICE TYPE STATE DBUS-PATH CONNECTION CON-UUID CON-PATH
eth0 ethernet unavailable /org/freedesktop/NetworkManager/Devices/1 -- -- --
eth1 ethernet unavailable /org/freedesktop/NetworkManager/Devices/2 -- -- --
wlan0 wifi unavailable /org/freedesktop/NetworkManager/Devices/3 -- -- --
wlan1 wifi unavailable /org/freedesktop/NetworkManager/Devices/4 -- -- --
wlan1 wifi unavailable /org/freedesktop/NetworkManager/Devices/5 -- -- --
DEVICE TYPE STATE IP4-CONNECTIVITY IP6-CONNECTIVITY DBUS-PATH CONNECTION CON-UUID CON-PATH
eth0 ethernet unavailable unknown unknown /org/freedesktop/NetworkManager/Devices/1 -- -- --
eth1 ethernet unavailable unknown unknown /org/freedesktop/NetworkManager/Devices/2 -- -- --
wlan0 wifi unavailable unknown unknown /org/freedesktop/NetworkManager/Devices/3 -- -- --
wlan1 wifi unavailable unknown unknown /org/freedesktop/NetworkManager/Devices/4 -- -- --
wlan1 wifi unavailable unknown unknown /org/freedesktop/NetworkManager/Devices/5 -- -- --
<<<
size: 777
size: 993
location: clients/tests/test-client.py:844:test_002()/4
cmd: $NMCLI -f all d
lang: pl_PL.UTF-8
returncode: 0
stdout: 641 bytes
stdout: 857 bytes
>>>
DEVICE TYPE STATE DBUS-PATH CONNECTION CON-UUID CON-PATH
eth0 ethernet niedostępne /org/freedesktop/NetworkManager/Devices/1 -- -- --
eth1 ethernet niedostępne /org/freedesktop/NetworkManager/Devices/2 -- -- --
wlan0 wifi niedostępne /org/freedesktop/NetworkManager/Devices/3 -- -- --
wlan1 wifi niedostępne /org/freedesktop/NetworkManager/Devices/4 -- -- --
wlan1 wifi niedostępne /org/freedesktop/NetworkManager/Devices/5 -- -- --
DEVICE TYPE STATE IP4-CONNECTIVITY IP6-CONNECTIVITY DBUS-PATH CONNECTION CON-UUID CON-PATH
eth0 ethernet niedostępne nieznane nieznane /org/freedesktop/NetworkManager/Devices/1 -- -- --
eth1 ethernet niedostępne nieznane nieznane /org/freedesktop/NetworkManager/Devices/2 -- -- --
wlan0 wifi niedostępne nieznane nieznane /org/freedesktop/NetworkManager/Devices/3 -- -- --
wlan1 wifi niedostępne nieznane nieznane /org/freedesktop/NetworkManager/Devices/4 -- -- --
wlan1 wifi niedostępne nieznane nieznane /org/freedesktop/NetworkManager/Devices/5 -- -- --
<<<
size: 739

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -256,6 +256,28 @@
-->
<property name="Real" type="b" access="read"/>
<!--
Ip4Connectivity:
The result of the last IPv4 connectivity check.
Since: 1.16
Returns: <link linkend="NMConnectivityState">NMConnectivityState</link>
-->
<property name="Ip4Connectivity" type="u" access="read"/>
<!--
Ip6Connectivity:
The result of the last IPv6 connectivity check.
Since: 1.16
Returns: <link linkend="NMConnectivityState">NMConnectivityState</link>
-->
<property name="Ip6Connectivity" type="u" access="read"/>
<!--
Reapply:
@connection: The optional connection settings that will be reapplied on the device. If empty, the currently active settings-connection will be used. The connection cannot arbitrarly differ from the current applied-connection otherwise the call will fail. Only certain changes are supported, like adding or removing IP addresses.

View file

@ -1439,3 +1439,8 @@ global:
nm_utils_sriov_vf_from_str;
nm_utils_sriov_vf_to_str;
} libnm_1_12_0;
libnm_1_16_0 {
global:
nm_device_get_connectivity;
} libnm_1_14_0;

View file

@ -74,6 +74,8 @@ typedef struct {
NMDhcpConfig *dhcp4_config;
NMIPConfig *ip6_config;
NMDhcpConfig *dhcp6_config;
NMConnectivityState ip4_connectivity;
NMConnectivityState ip6_connectivity;
NMDeviceState state;
NMDeviceState last_seen_state;
NMDeviceStateReason reason;
@ -120,6 +122,8 @@ enum {
PROP_MTU,
PROP_METERED,
PROP_LLDP_NEIGHBORS,
PROP_IP4_CONNECTIVITY,
PROP_IP6_CONNECTIVITY,
LAST_PROP
};
@ -144,6 +148,8 @@ nm_device_init (NMDevice *device)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
priv->ip4_connectivity = NM_CONNECTIVITY_UNKNOWN;
priv->ip6_connectivity = NM_CONNECTIVITY_UNKNOWN;
priv->state = NM_DEVICE_STATE_UNKNOWN;
priv->reason = NM_DEVICE_STATE_REASON_NONE;
priv->lldp_neighbors = g_ptr_array_new ();
@ -216,6 +222,8 @@ init_dbus (NMObject *object)
{ NM_DEVICE_DHCP4_CONFIG, &priv->dhcp4_config, NULL, NM_TYPE_DHCP4_CONFIG },
{ NM_DEVICE_IP6_CONFIG, &priv->ip6_config, NULL, NM_TYPE_IP6_CONFIG },
{ NM_DEVICE_DHCP6_CONFIG, &priv->dhcp6_config, NULL, NM_TYPE_DHCP6_CONFIG },
{ NM_DEVICE_IP4_CONNECTIVITY, &priv->ip4_connectivity },
{ NM_DEVICE_IP6_CONNECTIVITY, &priv->ip6_connectivity },
{ NM_DEVICE_STATE, &priv->state },
{ NM_DEVICE_STATE_REASON, &priv->reason, demarshal_state_reason },
{ NM_DEVICE_ACTIVE_CONNECTION, &priv->active_connection, NULL, NM_TYPE_ACTIVE_CONNECTION },
@ -428,6 +436,12 @@ get_property (GObject *object,
case PROP_LLDP_NEIGHBORS:
g_value_set_boxed (value, nm_device_get_lldp_neighbors (device));
break;
case PROP_IP4_CONNECTIVITY:
g_value_set_enum (value, nm_device_get_connectivity (device, AF_INET));
break;
case PROP_IP6_CONNECTIVITY:
g_value_set_enum (value, nm_device_get_connectivity (device, AF_INET6));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -700,6 +714,36 @@ nm_device_class_init (NMDeviceClass *device_class)
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
/**
* NMDevice:ip4-connectivity:
*
* The IPv4 connectivity state of the device.
*
* Since: 1.16
**/
g_object_class_install_property
(object_class, PROP_IP4_CONNECTIVITY,
g_param_spec_enum (NM_DEVICE_IP4_CONNECTIVITY, "", "",
NM_TYPE_CONNECTIVITY_STATE,
NM_CONNECTIVITY_UNKNOWN,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
/**
* NMDevice:ip6-connectivity:
*
* The IPv6 connectivity state of the device.
*
* Since: 1.16
**/
g_object_class_install_property
(object_class, PROP_IP6_CONNECTIVITY,
g_param_spec_enum (NM_DEVICE_IP6_CONNECTIVITY, "", "",
NM_TYPE_CONNECTIVITY_STATE,
NM_CONNECTIVITY_UNKNOWN,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
/**
* NMDevice:state:
*
@ -1230,6 +1274,36 @@ nm_device_get_dhcp6_config (NMDevice *device)
return NM_DEVICE_GET_PRIVATE (device)->dhcp6_config;
}
/**
* nm_device_get_connectivity:
* @device: a #NMDevice
* @addr_family: network address family
*
* The connectivity state of the device for given address family.
* Supported address families are %AF_INET for IPv4, %AF_INET6
* for IPv6 or %AF_UNSPEC for any.
*
* Returns: the current connectivity state
*
* Since: 1.16
**/
NMConnectivityState
nm_device_get_connectivity (NMDevice *device, int addr_family)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
switch (addr_family) {
case AF_INET:
return priv->ip4_connectivity;
case AF_INET6:
return priv->ip6_connectivity;
case AF_UNSPEC:
return NM_MAX (priv->ip4_connectivity, priv->ip6_connectivity);
default:
g_return_val_if_reached (NM_CONNECTIVITY_UNKNOWN);
}
}
/**
* nm_device_get_state:
* @device: a #NMDevice

View file

@ -64,6 +64,8 @@ G_BEGIN_DECLS
#define NM_DEVICE_MTU "mtu"
#define NM_DEVICE_METERED "metered"
#define NM_DEVICE_LLDP_NEIGHBORS "lldp-neighbors"
#define NM_DEVICE_IP4_CONNECTIVITY "ip4-connectivity"
#define NM_DEVICE_IP6_CONNECTIVITY "ip6-connectivity"
/**
* NMDevice:
@ -121,6 +123,8 @@ NMIPConfig * nm_device_get_ip4_config (NMDevice *device);
NMDhcpConfig * nm_device_get_dhcp4_config (NMDevice *device);
NMIPConfig * nm_device_get_ip6_config (NMDevice *device);
NMDhcpConfig * nm_device_get_dhcp6_config (NMDevice *device);
NM_AVAILABLE_IN_1_16
NMConnectivityState nm_device_get_connectivity (NMDevice *device, int addr_family);
NMDeviceState nm_device_get_state (NMDevice *device);
NMDeviceStateReason nm_device_get_state_reason (NMDevice *device);
NMActiveConnection * nm_device_get_active_connection(NMDevice *device);

View file

@ -387,6 +387,20 @@ no-auto-default=*
</listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd-resolved</varname></term>
<listitem><para>Send the connection DNS configuration to
<literal>systemd-resolved</literal>. Defaults to "<literal>true</literal>".
</para>
<para>Note that this setting is complementary to the
<varname>dns</varname> setting. You can keep this enable while using
<varname>dns</varname> set to another DNS plugin alongside
<literal>systemd-resolved</literal>, or <varname>dns</varname> set to
<literal>systemd-resolved</literal> to configure the system resolver to use
<literal>systemd-resolved</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>debug</varname></term>
<listitem><para>Comma separated list of options to aid

View file

@ -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 {
@ -196,7 +197,6 @@ enum {
REMOVED,
RECHECK_AUTO_ACTIVATE,
RECHECK_ASSUME,
CONNECTIVITY_CHANGED,
LAST_SIGNAL,
};
static guint signals[LAST_SIGNAL] = { 0 };
@ -242,7 +242,8 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice,
PROP_REFRESH_RATE_MS,
PROP_TX_BYTES,
PROP_RX_BYTES,
PROP_CONNECTIVITY,
PROP_IP4_CONNECTIVITY,
PROP_IP6_CONNECTIVITY,
);
typedef struct _NMDevicePrivate {
@ -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,23 +2784,22 @@ 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);
_notify (self, IS_IPv4 ? PROP_IP4_CONNECTIVITY : PROP_IP6_CONNECTIVITY);
if ( priv->state == NM_DEVICE_STATE_ACTIVATED
&& !nm_device_sys_iface_state_is_external (self)) {
@ -2788,9 +2813,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 +2828,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 +2890,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 +2918,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 +2930,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 +2969,7 @@ check_handles:
static NMDeviceConnectivityHandle *
concheck_start (NMDevice *self,
int addr_family,
NMDeviceConnectivityCallback callback,
gpointer user_data,
gboolean is_periodic)
@ -2959,6 +2990,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 +2999,8 @@ 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,
handle);
@ -2975,6 +3009,7 @@ concheck_start (NMDevice *self,
NMDeviceConnectivityHandle *
nm_device_check_connectivity (NMDevice *self,
int addr_family,
NMDeviceConnectivityCallback callback,
gpointer user_data)
{
@ -2983,8 +3018,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;
}
@ -3008,9 +3043,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);
}
/*****************************************************************************/
@ -7082,7 +7121,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
@ -14850,8 +14889,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)
@ -15815,7 +15854,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]);
@ -16016,7 +16056,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);
@ -16353,8 +16394,11 @@ 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);
case PROP_IP4_CONNECTIVITY:
g_value_set_uint (value, priv->concheck_x[1].state);
break;
case PROP_IP6_CONNECTIVITY:
g_value_set_uint (value, priv->concheck_x[0].state);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -16442,6 +16486,8 @@ static const NMDBusInterfaceInfoExtended interface_info_device = {
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Metered", "u", NM_DEVICE_METERED),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("LldpNeighbors", "aa{sv}", NM_DEVICE_LLDP_NEIGHBORS),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Real", "b", NM_DEVICE_REAL),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Ip4Connectivity", "u", NM_DEVICE_IP4_CONNECTIVITY),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Ip6Connectivity", "u", NM_DEVICE_IP6_CONNECTIVITY),
),
),
};
@ -16718,8 +16764,13 @@ nm_device_class_init (NMDeviceClass *klass)
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_CONNECTIVITY] =
g_param_spec_uint (NM_DEVICE_CONNECTIVITY, "", "",
obj_properties[PROP_IP4_CONNECTIVITY] =
g_param_spec_uint (NM_DEVICE_IP4_CONNECTIVITY, "", "",
NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_IP6_CONNECTIVITY] =
g_param_spec_uint (NM_DEVICE_IP6_CONNECTIVITY, "", "",
NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
@ -16799,12 +16850,4 @@ nm_device_class_init (NMDeviceClass *klass)
G_SIGNAL_RUN_FIRST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 0);
signals[CONNECTIVITY_CHANGED] =
g_signal_new (NM_DEVICE_CONNECTIVITY_CHANGED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
0, NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}

View file

@ -143,13 +143,13 @@ nm_device_state_reason_check (NMDeviceStateReason reason)
#define NM_DEVICE_STATE_CHANGED "state-changed"
#define NM_DEVICE_LINK_INITIALIZED "link-initialized"
#define NM_DEVICE_AUTOCONNECT_ALLOWED "autoconnect-allowed"
#define NM_DEVICE_CONNECTIVITY_CHANGED "connectivity-changed"
#define NM_DEVICE_STATISTICS_REFRESH_RATE_MS "refresh-rate-ms"
#define NM_DEVICE_STATISTICS_TX_BYTES "tx-bytes"
#define NM_DEVICE_STATISTICS_RX_BYTES "rx-bytes"
#define NM_DEVICE_CONNECTIVITY "connectivity"
#define NM_DEVICE_IP4_CONNECTIVITY "ip4-connectivity"
#define NM_DEVICE_IP6_CONNECTIVITY "ip6-connectivity"
#define NM_TYPE_DEVICE (nm_device_get_type ())
#define NM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE, NMDevice))
@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -17,12 +17,13 @@
*
* Copyright (C) 2011 Thomas Bechtold <thomasbechtold@jpberlin.de>
* Copyright (C) 2011 Dan Williams <dcbw@redhat.com>
* 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 <string.h>
@ -67,13 +68,19 @@ struct _NMConnectivityCheckHandle {
gpointer user_data;
char *ifspec;
int addr_family;
#if WITH_CONCHECK
struct {
char *response;
int ifindex;
GCancellable *resolve_cancellable;
CURLM *curl_mhandle;
guint curl_timer;
CURL *curl_ehandle;
struct curl_slist *request_headers;
struct curl_slist *hosts;
GString *recv_msg;
} concheck;
@ -98,16 +105,12 @@ typedef struct {
CList handles_lst_head;
CList completed_handles_lst_head;
char *uri;
char *host;
char *port;
char *response;
gboolean enabled;
guint interval;
NMConfig *config;
#if WITH_CONCHECK
struct {
CURLM *curl_mhandle;
guint curl_timer;
} concheck;
#endif
} NMConnectivityPrivate;
struct _NMConnectivity {
@ -139,9 +142,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
@ -171,8 +175,6 @@ cb_data_complete (NMConnectivityCheckHandle *cb_data,
#if WITH_CONCHECK
if (cb_data->concheck.curl_ehandle) {
NMConnectivityPrivate *priv;
/* Contrary to what cURL manual claim it is *not* safe to remove
* the easy handle "at any moment"; specifically it's not safe to
* remove *any* handle from within a libcurl callback. That is
@ -187,13 +189,16 @@ cb_data_complete (NMConnectivityCheckHandle *cb_data,
curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_PRIVATE, NULL);
curl_easy_setopt (cb_data->concheck.curl_ehandle, CURLOPT_HTTPHEADER, NULL);
priv = NM_CONNECTIVITY_GET_PRIVATE (self);
curl_multi_remove_handle (priv->concheck.curl_mhandle, cb_data->concheck.curl_ehandle);
curl_multi_remove_handle (cb_data->concheck.curl_mhandle,
cb_data->concheck.curl_ehandle);
curl_easy_cleanup (cb_data->concheck.curl_ehandle);
curl_multi_cleanup (cb_data->concheck.curl_mhandle);
curl_slist_free_all (cb_data->concheck.request_headers);
curl_slist_free_all (cb_data->concheck.hosts);
}
nm_clear_g_source (&cb_data->concheck.curl_timer);
nm_clear_g_cancellable (&cb_data->concheck.resolve_cancellable);
#endif
nm_clear_g_source (&cb_data->timeout_id);
@ -285,7 +290,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 +302,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 +320,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) {
@ -346,29 +354,27 @@ _con_curl_check_connectivity (CURLM *mhandle, int sockfd, int ev_bitmask)
static gboolean
_con_curl_timeout_cb (gpointer user_data)
{
gs_unref_object NMConnectivity *self = g_object_ref (NM_CONNECTIVITY (user_data));
NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
NMConnectivityCheckHandle *cb_data = user_data;
priv->concheck.curl_timer = 0;
_con_curl_check_connectivity (priv->concheck.curl_mhandle, CURL_SOCKET_TIMEOUT, 0);
_complete_queued (self);
cb_data->concheck.curl_timer = 0;
_con_curl_check_connectivity (cb_data->concheck.curl_mhandle, CURL_SOCKET_TIMEOUT, 0);
_complete_queued (cb_data->self);
return G_SOURCE_REMOVE;
}
static int
multi_timer_cb (CURLM *multi, long timeout_ms, void *userdata)
{
NMConnectivity *self = NM_CONNECTIVITY (userdata);
NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
NMConnectivityCheckHandle *cb_data = userdata;
nm_clear_g_source (&priv->concheck.curl_timer);
nm_clear_g_source (&cb_data->concheck.curl_timer);
if (timeout_ms != -1)
priv->concheck.curl_timer = g_timeout_add (timeout_ms, _con_curl_timeout_cb, self);
cb_data->concheck.curl_timer = g_timeout_add (timeout_ms, _con_curl_timeout_cb, cb_data);
return 0;
}
typedef struct {
NMConnectivity *self;
NMConnectivityCheckHandle *cb_data;
GIOChannel *ch;
/* this is a very simplistic weak-pointer. If ConCurlSockData gets
@ -385,8 +391,7 @@ static gboolean
_con_curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_data)
{
ConCurlSockData *fdp = user_data;
gs_unref_object NMConnectivity *self = g_object_ref (fdp->self);
NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
NMConnectivityCheckHandle *cb_data = fdp->cb_data;
int fd = g_io_channel_unix_get_fd (ch);
int action = 0;
gboolean fdp_destroyed = FALSE;
@ -402,7 +407,7 @@ _con_curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_
nm_assert (!fdp->destroy_notify);
fdp->destroy_notify = &fdp_destroyed;
success = _con_curl_check_connectivity (priv->concheck.curl_mhandle, fd, action);
success = _con_curl_check_connectivity (cb_data->concheck.curl_mhandle, fd, action);
if (fdp_destroyed) {
/* hups. fdp got invalidated during _con_curl_check_connectivity(). That's fine,
@ -414,7 +419,7 @@ _con_curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_
fdp->ev = 0;
}
_complete_queued (self);
_complete_queued (cb_data->self);
return success ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
}
@ -422,8 +427,7 @@ _con_curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer user_
static int
multi_socket_cb (CURL *e_handle, curl_socket_t fd, int what, void *userdata, void *socketp)
{
NMConnectivity *self = NM_CONNECTIVITY (userdata);
NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
NMConnectivityCheckHandle *cb_data = userdata;
ConCurlSockData *fdp = socketp;
GIOCondition condition = 0;
@ -433,7 +437,7 @@ multi_socket_cb (CURL *e_handle, curl_socket_t fd, int what, void *userdata, voi
if (fdp) {
if (fdp->destroy_notify)
*fdp->destroy_notify = TRUE;
curl_multi_assign (priv->concheck.curl_mhandle, fd, NULL);
curl_multi_assign (cb_data->concheck.curl_mhandle, fd, NULL);
nm_clear_g_source (&fdp->ev);
g_io_channel_unref (fdp->ch);
g_slice_free (ConCurlSockData, fdp);
@ -441,9 +445,9 @@ multi_socket_cb (CURL *e_handle, curl_socket_t fd, int what, void *userdata, voi
} else {
if (!fdp) {
fdp = g_slice_new0 (ConCurlSockData);
fdp->self = self;
fdp->cb_data = cb_data;
fdp->ch = g_io_channel_unix_new (fd);
curl_multi_assign (priv->concheck.curl_mhandle, fd, fdp);
curl_multi_assign (cb_data->concheck.curl_mhandle, fd, fdp);
} else
nm_clear_g_source (&fdp->ev);
@ -556,8 +560,171 @@ _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);
CURLM *mhandle;
CURL *ehandle;
long resolve;
mhandle = curl_multi_init ();
if (!mhandle) {
cb_data_complete (cb_data, NM_CONNECTIVITY_ERROR, "curl error");
return;
}
ehandle = curl_easy_init ();
if (!ehandle) {
curl_multi_cleanup (mhandle);
cb_data_complete (cb_data, NM_CONNECTIVITY_ERROR, "curl error");
return;
}
cb_data->concheck.response = g_strdup (priv->response);
cb_data->concheck.curl_mhandle = mhandle;
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_multi_setopt (mhandle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb);
curl_multi_setopt (mhandle, CURLMOPT_SOCKETDATA, cb_data);
curl_multi_setopt (mhandle, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
curl_multi_setopt (mhandle, CURLMOPT_TIMERDATA, cb_data);
curl_multi_setopt (mhandle, CURLOPT_VERBOSE, 1);
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 ();
}
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_easy_setopt (ehandle, CURLOPT_IPRESOLVE, resolve);
curl_multi_add_handle (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 addr_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, &addr_family, &address);
address_buf = g_variant_get_fixed_array (address, &len, 1);
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);
}
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) cb_data->addr_family,
(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 addr_family,
int ifindex,
const char *iface,
NMConnectivityCheckCallback callback,
gpointer user_data)
@ -577,40 +744,35 @@ 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);
#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;
}
@ -646,6 +808,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)
{
@ -679,6 +889,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. */
@ -691,11 +905,8 @@ update_config (NMConnectivity *self, NMConfigData *config_data)
enabled = FALSE;
#if WITH_CONCHECK
/* connectivity checking also requires a valid URI, interval and
* curl_mhandle */
if ( priv->uri
&& priv->interval
&& priv->concheck.curl_mhandle)
&& priv->interval)
enabled = nm_config_data_get_connectivity_enabled (config_data);
#endif
@ -734,6 +945,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,17 +959,10 @@ nm_connectivity_init (NMConnectivity *self)
self);
#if WITH_CONCHECK
if (curl_global_init (CURL_GLOBAL_ALL) == 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 {
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);
curl_multi_setopt (priv->concheck.curl_mhandle, CURLMOPT_TIMERDATA, self);
curl_multi_setopt (priv->concheck.curl_mhandle, CURLOPT_VERBOSE, 1);
ret = curl_global_init (CURL_GLOBAL_ALL);
if (!ret == CURLE_OK) {
_LOGE ("unable to init cURL, connectivity check will not work: (%d) %s",
ret, curl_easy_strerror (ret));
}
#endif
@ -777,12 +984,11 @@ 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
nm_clear_g_source (&priv->concheck.curl_timer);
curl_multi_cleanup (priv->concheck.curl_mhandle);
curl_global_cleanup ();
#endif

View file

@ -58,6 +58,8 @@ typedef void (*NMConnectivityCheckCallback) (NMConnectivity *self,
gpointer user_data);
NMConnectivityCheckHandle *nm_connectivity_check_start (NMConnectivity *self,
int family,
int ifindex,
const char *iface,
NMConnectivityCheckCallback callback,
gpointer user_data);

View file

@ -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);
@ -2954,7 +2955,10 @@ add_device (NMManager *self, NMDevice *device, GError **error)
G_CALLBACK (device_realized),
self);
g_signal_connect (device, NM_DEVICE_CONNECTIVITY_CHANGED,
g_signal_connect (device, "notify::" NM_DEVICE_IP4_CONNECTIVITY,
G_CALLBACK (device_connectivity_changed),
self);
g_signal_connect (device, "notify::" NM_DEVICE_IP6_CONNECTIVITY,
G_CALLBACK (device_connectivity_changed),
self);
@ -6041,6 +6045,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++;