mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-07 08:40:38 +01:00
NMPolicy only updates the NMDnsManager's hostname when it changes, which previously did not include at startup. Meaning if your hostname never changed, NMDnsManager would never learn it (and so would never add an appropriate "search" line to resolv.conf). Fix that.
2286 lines
73 KiB
C
2286 lines
73 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
|
/* NetworkManager -- Network link manager
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* 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 - 2013 Red Hat, Inc.
|
|
* Copyright (C) 2007 - 2008 Novell, Inc.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <netdb.h>
|
|
|
|
#include <gio/gio.h>
|
|
|
|
#include "nm-policy.h"
|
|
#include "NetworkManagerUtils.h"
|
|
#include "nm-wifi-ap.h"
|
|
#include "nm-activation-request.h"
|
|
#include "nm-logging.h"
|
|
#include "nm-device.h"
|
|
#include "nm-dbus-manager.h"
|
|
#include "nm-setting-ip4-config.h"
|
|
#include "nm-setting-connection.h"
|
|
#include "nm-platform.h"
|
|
#include "nm-dns-manager.h"
|
|
#include "nm-vpn-manager.h"
|
|
#include "nm-manager-auth.h"
|
|
#include "nm-firewall-manager.h"
|
|
#include "nm-dispatcher.h"
|
|
#include "nm-utils.h"
|
|
#include "nm-glib-compat.h"
|
|
|
|
typedef struct {
|
|
NMManager *manager;
|
|
guint update_state_id;
|
|
GSList *pending_activation_checks;
|
|
GSList *manager_ids;
|
|
GSList *settings_ids;
|
|
GSList *dev_ids;
|
|
|
|
GSList *pending_secondaries;
|
|
|
|
NMFirewallManager *fw_manager;
|
|
gulong fw_started_id;
|
|
|
|
NMSettings *settings;
|
|
|
|
NMDevice *default_device4, *activating_device4;
|
|
NMDevice *default_device6, *activating_device6;
|
|
|
|
GResolver *resolver;
|
|
GInetAddress *lookup_addr;
|
|
GCancellable *lookup_cancellable;
|
|
NMDnsManager *dns_manager;
|
|
gulong config_changed_id;
|
|
|
|
gint reset_retries_id; /* idle handler for resetting the retries count */
|
|
|
|
char *orig_hostname; /* hostname at NM start time */
|
|
char *cur_hostname; /* hostname we want to assign */
|
|
gboolean hostname_changed; /* TRUE if NM ever set the hostname */
|
|
} NMPolicyPrivate;
|
|
|
|
#define NM_POLICY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_POLICY, NMPolicyPrivate))
|
|
|
|
G_DEFINE_TYPE (NMPolicy, nm_policy, G_TYPE_OBJECT)
|
|
|
|
enum {
|
|
PROP_0,
|
|
|
|
PROP_DEFAULT_IP4_DEVICE,
|
|
PROP_DEFAULT_IP6_DEVICE,
|
|
PROP_ACTIVATING_IP4_DEVICE,
|
|
PROP_ACTIVATING_IP6_DEVICE
|
|
};
|
|
|
|
#define RETRIES_TAG "autoconnect-retries"
|
|
#define RETRIES_DEFAULT 4
|
|
#define RESET_RETRIES_TIMESTAMP_TAG "reset-retries-timestamp-tag"
|
|
#define RESET_RETRIES_TIMER 300
|
|
#define FAILURE_REASON_TAG "failure-reason"
|
|
|
|
static void schedule_activate_all (NMPolicy *policy);
|
|
|
|
|
|
static NMDevice *
|
|
get_best_ip4_device (NMManager *manager, gboolean fully_activated)
|
|
{
|
|
GSList *devices, *iter;
|
|
NMDevice *best = NULL;
|
|
int best_prio = G_MAXINT;
|
|
|
|
g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
|
|
|
|
devices = nm_manager_get_devices (manager);
|
|
for (iter = devices; iter; iter = g_slist_next (iter)) {
|
|
NMDevice *dev = NM_DEVICE (iter->data);
|
|
NMDeviceType devtype = nm_device_get_device_type (dev);
|
|
NMDeviceState state = nm_device_get_state (dev);
|
|
NMActRequest *req;
|
|
NMConnection *connection;
|
|
NMIP4Config *ip4_config;
|
|
NMSettingIP4Config *s_ip4;
|
|
int prio;
|
|
const char *method = NULL;
|
|
|
|
if ( state <= NM_DEVICE_STATE_DISCONNECTED
|
|
|| state >= NM_DEVICE_STATE_DEACTIVATING)
|
|
continue;
|
|
|
|
if (fully_activated && state < NM_DEVICE_STATE_SECONDARIES)
|
|
continue;
|
|
|
|
ip4_config = nm_device_get_ip4_config (dev);
|
|
if (ip4_config) {
|
|
/* Make sure the device has a gateway */
|
|
if (!nm_ip4_config_get_gateway (ip4_config) && (devtype != NM_DEVICE_TYPE_MODEM))
|
|
continue;
|
|
|
|
/* 'never-default' devices can't ever be the default */
|
|
if (nm_ip4_config_get_never_default (ip4_config))
|
|
continue;
|
|
} else if (fully_activated)
|
|
continue;
|
|
|
|
req = nm_device_get_act_request (dev);
|
|
g_assert (req);
|
|
connection = nm_act_request_get_connection (req);
|
|
g_assert (connection);
|
|
|
|
method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
|
|
/* If IPv4 is disabled or link-local-only, it can't be the default */
|
|
if ( !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)
|
|
|| !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
|
|
continue;
|
|
|
|
/* 'never-default' devices can't ever be the default */
|
|
s_ip4 = nm_connection_get_setting_ip4_config (connection);
|
|
g_assert (s_ip4);
|
|
if (nm_setting_ip4_config_get_never_default (s_ip4))
|
|
continue;
|
|
|
|
prio = nm_device_get_priority (dev);
|
|
if (prio > 0 && prio < best_prio) {
|
|
best = dev;
|
|
best_prio = prio;
|
|
}
|
|
}
|
|
|
|
if (!best)
|
|
return NULL;
|
|
|
|
if (!fully_activated) {
|
|
NMDeviceState state = nm_device_get_state (best);
|
|
|
|
/* There's only a best activating device if the best device
|
|
* among all activating and already-activated devices is a
|
|
* still-activating one.
|
|
*/
|
|
if (state >= NM_DEVICE_STATE_SECONDARIES)
|
|
return NULL;
|
|
}
|
|
|
|
return best;
|
|
}
|
|
|
|
static NMDevice *
|
|
get_best_ip6_device (NMManager *manager, gboolean fully_activated)
|
|
{
|
|
GSList *devices, *iter;
|
|
NMDevice *best = NULL;
|
|
int best_prio = G_MAXINT;
|
|
|
|
g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
|
|
|
|
devices = nm_manager_get_devices (manager);
|
|
for (iter = devices; iter; iter = g_slist_next (iter)) {
|
|
NMDevice *dev = NM_DEVICE (iter->data);
|
|
NMDeviceType devtype = nm_device_get_device_type (dev);
|
|
NMDeviceState state = nm_device_get_state (dev);
|
|
NMActRequest *req;
|
|
NMConnection *connection;
|
|
NMIP6Config *ip6_config;
|
|
NMSettingIP6Config *s_ip6;
|
|
int prio;
|
|
const char *method = NULL;
|
|
|
|
if ( state <= NM_DEVICE_STATE_DISCONNECTED
|
|
|| state >= NM_DEVICE_STATE_DEACTIVATING)
|
|
continue;
|
|
|
|
if (fully_activated && state < NM_DEVICE_STATE_SECONDARIES)
|
|
continue;
|
|
|
|
ip6_config = nm_device_get_ip6_config (dev);
|
|
if (ip6_config) {
|
|
if (!nm_ip6_config_get_gateway (ip6_config) && (devtype != NM_DEVICE_TYPE_MODEM))
|
|
continue;
|
|
|
|
if (nm_ip6_config_get_never_default (ip6_config))
|
|
continue;
|
|
} else if (fully_activated)
|
|
continue;
|
|
|
|
req = nm_device_get_act_request (dev);
|
|
g_assert (req);
|
|
connection = nm_act_request_get_connection (req);
|
|
g_assert (connection);
|
|
|
|
method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
|
|
if ( !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)
|
|
|| !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL))
|
|
continue;
|
|
|
|
s_ip6 = nm_connection_get_setting_ip6_config (connection);
|
|
g_assert (s_ip6);
|
|
if (nm_setting_ip6_config_get_never_default (s_ip6))
|
|
continue;
|
|
|
|
prio = nm_device_get_priority (dev);
|
|
if (prio > 0 && prio < best_prio) {
|
|
best = dev;
|
|
best_prio = prio;
|
|
}
|
|
}
|
|
|
|
if (!best)
|
|
return NULL;
|
|
|
|
if (!fully_activated) {
|
|
NMDeviceState state = nm_device_get_state (best);
|
|
|
|
/* There's only a best activating device if the best device
|
|
* among all activating and already-activated devices is an
|
|
* activating one.
|
|
*/
|
|
if (state >= NM_DEVICE_STATE_SECONDARIES)
|
|
return NULL;
|
|
}
|
|
|
|
return best;
|
|
}
|
|
|
|
#define FALLBACK_HOSTNAME4 "localhost.localdomain"
|
|
|
|
static gboolean
|
|
set_system_hostname (const char *new_hostname, const char *msg)
|
|
{
|
|
char old_hostname[HOST_NAME_MAX + 1];
|
|
const char *name;
|
|
int ret;
|
|
|
|
if (new_hostname)
|
|
g_warn_if_fail (strlen (new_hostname));
|
|
|
|
old_hostname[HOST_NAME_MAX] = '\0';
|
|
errno = 0;
|
|
ret = gethostname (old_hostname, HOST_NAME_MAX);
|
|
if (ret != 0) {
|
|
nm_log_warn (LOGD_DNS, "couldn't get the system hostname: (%d) %s",
|
|
errno, strerror (errno));
|
|
} else {
|
|
/* Don't set the hostname if it isn't actually changing */
|
|
if ( (new_hostname && !strcmp (old_hostname, new_hostname))
|
|
|| (!new_hostname && !strcmp (old_hostname, FALLBACK_HOSTNAME4)))
|
|
return FALSE;
|
|
}
|
|
|
|
name = (new_hostname && strlen (new_hostname)) ? new_hostname : FALLBACK_HOSTNAME4;
|
|
|
|
nm_log_info (LOGD_DNS, "Setting system hostname to '%s' (%s)", name, msg);
|
|
ret = sethostname (name, strlen (name));
|
|
if (ret != 0) {
|
|
nm_log_warn (LOGD_DNS, "couldn't set the system hostname to '%s': (%d) %s",
|
|
name, errno, strerror (errno));
|
|
}
|
|
|
|
return (ret == 0);
|
|
}
|
|
|
|
static void
|
|
_set_hostname (NMPolicy *policy,
|
|
const char *new_hostname,
|
|
const char *msg)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
|
|
/* The incoming hostname *can* be NULL, which will get translated to
|
|
* 'localhost.localdomain' or such in the hostname policy code, but we
|
|
* keep cur_hostname = NULL in the case because we need to know that
|
|
* there was no valid hostname to start with.
|
|
*/
|
|
|
|
/* Clear lookup adresses if we have a hostname, so that we don't
|
|
* restart the reverse lookup thread later.
|
|
*/
|
|
if (new_hostname)
|
|
g_clear_object (&priv->lookup_addr);
|
|
|
|
/* Don't change the hostname or update DNS this is the first time we're
|
|
* trying to change the hostname, and it's not actually changing.
|
|
*/
|
|
if ( priv->orig_hostname
|
|
&& (priv->hostname_changed == FALSE)
|
|
&& g_strcmp0 (priv->orig_hostname, new_hostname) == 0)
|
|
return;
|
|
|
|
/* Don't change the hostname or update DNS if the hostname isn't actually
|
|
* going to change.
|
|
*/
|
|
if (g_strcmp0 (priv->cur_hostname, new_hostname) == 0)
|
|
return;
|
|
|
|
g_free (priv->cur_hostname);
|
|
priv->cur_hostname = g_strdup (new_hostname);
|
|
priv->hostname_changed = TRUE;
|
|
|
|
nm_dns_manager_set_hostname (priv->dns_manager, priv->cur_hostname);
|
|
|
|
if (set_system_hostname (priv->cur_hostname, msg))
|
|
nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
lookup_callback (GObject *source,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
NMPolicy *policy = (NMPolicy *) user_data;
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
const char *hostname;
|
|
GError *error = NULL;
|
|
|
|
hostname = g_resolver_lookup_by_address_finish (G_RESOLVER (source), result, &error);
|
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
|
/* Don't touch policy; it may have been freed already */
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
if (hostname)
|
|
_set_hostname (policy, hostname, "from address lookup");
|
|
else {
|
|
_set_hostname (policy, NULL, error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
g_clear_object (&priv->lookup_cancellable);
|
|
}
|
|
|
|
static void
|
|
update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
char *configured_hostname = NULL;
|
|
const char *dhcp_hostname, *p;
|
|
NMIP4Config *ip4_config;
|
|
NMIP6Config *ip6_config;
|
|
|
|
g_return_if_fail (policy != NULL);
|
|
|
|
if (priv->lookup_cancellable) {
|
|
g_cancellable_cancel (priv->lookup_cancellable);
|
|
g_clear_object (&priv->lookup_cancellable);
|
|
}
|
|
|
|
/* Hostname precedence order:
|
|
*
|
|
* 1) a configured hostname (from settings)
|
|
* 2) automatic hostname from the default device's config (DHCP, VPN, etc)
|
|
* 3) the original hostname when NM started
|
|
* 4) reverse-DNS of the best device's IPv4 address
|
|
*
|
|
*/
|
|
|
|
/* Try a persistent hostname first */
|
|
g_object_get (G_OBJECT (priv->manager), NM_MANAGER_HOSTNAME, &configured_hostname, NULL);
|
|
if (configured_hostname) {
|
|
_set_hostname (policy, configured_hostname, "from system configuration");
|
|
g_free (configured_hostname);
|
|
return;
|
|
}
|
|
|
|
/* Try automatically determined hostname from the best device's IP config */
|
|
if (!best4)
|
|
best4 = get_best_ip4_device (priv->manager, TRUE);
|
|
if (!best6)
|
|
best6 = get_best_ip6_device (priv->manager, TRUE);
|
|
|
|
if (!best4 && !best6) {
|
|
/* No best device; fall back to original hostname or if there wasn't
|
|
* one, 'localhost.localdomain'
|
|
*/
|
|
_set_hostname (policy, priv->orig_hostname, "no default device");
|
|
return;
|
|
}
|
|
|
|
if (best4) {
|
|
NMDHCP4Config *dhcp4_config;
|
|
|
|
/* Grab a hostname out of the device's DHCP4 config */
|
|
dhcp4_config = nm_device_get_dhcp4_config (best4);
|
|
if (dhcp4_config) {
|
|
p = dhcp_hostname = nm_dhcp4_config_get_option (dhcp4_config, "host_name");
|
|
if (dhcp_hostname && strlen (dhcp_hostname)) {
|
|
/* Sanity check; strip leading spaces */
|
|
while (*p) {
|
|
if (!g_ascii_isspace (*p++)) {
|
|
_set_hostname (policy, p-1, "from DHCPv4");
|
|
return;
|
|
}
|
|
}
|
|
nm_log_warn (LOGD_DNS, "DHCPv4-provided hostname '%s' looks invalid; ignoring it",
|
|
dhcp_hostname);
|
|
}
|
|
}
|
|
} else if (best6) {
|
|
NMDHCP6Config *dhcp6_config;
|
|
|
|
/* Grab a hostname out of the device's DHCP6 config */
|
|
dhcp6_config = nm_device_get_dhcp6_config (best6);
|
|
if (dhcp6_config) {
|
|
p = dhcp_hostname = nm_dhcp6_config_get_option (dhcp6_config, "host_name");
|
|
if (dhcp_hostname && strlen (dhcp_hostname)) {
|
|
/* Sanity check; strip leading spaces */
|
|
while (*p) {
|
|
if (!g_ascii_isspace (*p++)) {
|
|
_set_hostname (policy, p-1, "from DHCPv6");
|
|
return;
|
|
}
|
|
}
|
|
nm_log_warn (LOGD_DNS, "DHCPv6-provided hostname '%s' looks invalid; ignoring it",
|
|
dhcp_hostname);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If no automatically-configured hostname, try using the hostname from
|
|
* when NM started up.
|
|
*/
|
|
if (priv->orig_hostname) {
|
|
_set_hostname (policy, priv->orig_hostname, "from system startup");
|
|
return;
|
|
}
|
|
|
|
/* No configured hostname, no automatically determined hostname, and no
|
|
* bootup hostname. Start reverse DNS of the current IPv4 or IPv6 address.
|
|
*/
|
|
ip4_config = best4 ? nm_device_get_ip4_config (best4) : NULL;
|
|
ip6_config = best6 ? nm_device_get_ip6_config (best6) : NULL;
|
|
|
|
if ( (!ip4_config || (nm_ip4_config_get_num_addresses (ip4_config) == 0))
|
|
&& (!ip6_config || (nm_ip6_config_get_num_addresses (ip6_config) == 0))) {
|
|
/* No valid IP config; fall back to localhost.localdomain */
|
|
_set_hostname (policy, NULL, "no IP config");
|
|
return;
|
|
}
|
|
|
|
if (ip4_config) {
|
|
const NMPlatformIP4Address *addr4;
|
|
|
|
addr4 = nm_ip4_config_get_address (ip4_config, 0);
|
|
g_assert (addr4); /* checked for > 1 address above */
|
|
|
|
priv->lookup_addr = g_inet_address_new_from_bytes ((guint8 *) &addr4->address,
|
|
G_SOCKET_FAMILY_IPV4);
|
|
} else if (ip6_config) {
|
|
const NMPlatformIP6Address *addr6;
|
|
|
|
addr6 = nm_ip6_config_get_address (ip6_config, 0);
|
|
g_assert (addr6); /* checked for > 1 address above */
|
|
|
|
priv->lookup_addr = g_inet_address_new_from_bytes ((guint8 *) &addr6->address,
|
|
G_SOCKET_FAMILY_IPV6);
|
|
} else {
|
|
/* Should never get here... */
|
|
g_warn_if_reached ();
|
|
_set_hostname (policy, NULL, "no IP config");
|
|
return;
|
|
}
|
|
|
|
priv->lookup_cancellable = g_cancellable_new ();
|
|
g_resolver_lookup_by_address_async (priv->resolver,
|
|
priv->lookup_addr,
|
|
priv->lookup_cancellable,
|
|
lookup_callback, policy);
|
|
}
|
|
|
|
static void
|
|
update_default_ac (NMPolicy *policy,
|
|
NMActiveConnection *best,
|
|
void (*set_active_func)(NMActiveConnection*, gboolean))
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
const GSList *connections, *iter;
|
|
|
|
/* Clear the 'default[6]' flag on all active connections that aren't the new
|
|
* default active connection. We'll set the new default after; this ensures
|
|
* we don't ever have two marked 'default[6]' simultaneously.
|
|
*/
|
|
connections = nm_manager_get_active_connections (priv->manager);
|
|
for (iter = connections; iter; iter = g_slist_next (iter)) {
|
|
if (NM_ACTIVE_CONNECTION (iter->data) != best)
|
|
set_active_func (NM_ACTIVE_CONNECTION (iter->data), FALSE);
|
|
}
|
|
|
|
/* Mark new default active connection */
|
|
if (best)
|
|
set_active_func (best, TRUE);
|
|
}
|
|
|
|
static NMIP4Config *
|
|
get_best_ip4_config (NMPolicy *policy,
|
|
gboolean ignore_never_default,
|
|
const char **out_ip_iface,
|
|
int *out_ip_ifindex,
|
|
NMActiveConnection **out_ac,
|
|
NMDevice **out_device,
|
|
NMVPNConnection **out_vpn)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
const GSList *connections, *iter;
|
|
NMDevice *device;
|
|
NMActRequest *req = NULL;
|
|
NMIP4Config *ip4_config = NULL;
|
|
|
|
/* If a VPN connection is active, it is preferred */
|
|
connections = nm_manager_get_active_connections (priv->manager);
|
|
for (iter = connections; iter; iter = g_slist_next (iter)) {
|
|
NMActiveConnection *active = NM_ACTIVE_CONNECTION (iter->data);
|
|
NMVPNConnection *candidate;
|
|
NMIP4Config *vpn_ip4;
|
|
NMConnection *tmp;
|
|
NMSettingIP4Config *s_ip4;
|
|
NMVPNConnectionState vpn_state;
|
|
|
|
if (!NM_IS_VPN_CONNECTION (active))
|
|
continue;
|
|
|
|
candidate = NM_VPN_CONNECTION (active);
|
|
|
|
tmp = nm_active_connection_get_connection (active);
|
|
g_assert (tmp);
|
|
|
|
vpn_state = nm_vpn_connection_get_vpn_state (candidate);
|
|
if (vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED)
|
|
continue;
|
|
|
|
vpn_ip4 = nm_vpn_connection_get_ip4_config (candidate);
|
|
if (!vpn_ip4)
|
|
continue;
|
|
|
|
if (ignore_never_default == FALSE) {
|
|
/* Check for a VPN-provided config never-default */
|
|
if (nm_ip4_config_get_never_default (vpn_ip4))
|
|
continue;
|
|
|
|
/* Check the user's preference from the NMConnection */
|
|
s_ip4 = nm_connection_get_setting_ip4_config (tmp);
|
|
if (nm_setting_ip4_config_get_never_default (s_ip4))
|
|
continue;
|
|
}
|
|
|
|
ip4_config = vpn_ip4;
|
|
if (out_vpn)
|
|
*out_vpn = candidate;
|
|
if (out_ac)
|
|
*out_ac = active;
|
|
if (out_ip_iface)
|
|
*out_ip_iface = nm_vpn_connection_get_ip_iface (candidate);
|
|
if (out_ip_ifindex)
|
|
*out_ip_ifindex = nm_vpn_connection_get_ip_ifindex (candidate);
|
|
break;
|
|
}
|
|
|
|
/* If no VPN connections, we use the best device instead */
|
|
if (!ip4_config) {
|
|
device = get_best_ip4_device (priv->manager, TRUE);
|
|
if (device) {
|
|
ip4_config = nm_device_get_ip4_config (device);
|
|
g_assert (ip4_config);
|
|
req = nm_device_get_act_request (device);
|
|
g_assert (req);
|
|
|
|
if (out_device)
|
|
*out_device = device;
|
|
if (out_ac)
|
|
*out_ac = NM_ACTIVE_CONNECTION (req);
|
|
if (out_ip_iface)
|
|
*out_ip_iface = nm_device_get_ip_iface (device);
|
|
if (out_ip_ifindex)
|
|
*out_ip_ifindex = nm_device_get_ip_ifindex (device);
|
|
}
|
|
}
|
|
|
|
return ip4_config;
|
|
}
|
|
|
|
static void
|
|
update_ip4_dns (NMPolicy *policy, NMDnsManager *dns_mgr)
|
|
{
|
|
NMIP4Config *ip4_config;
|
|
const char *ip_iface = NULL;
|
|
NMVPNConnection *vpn = NULL;
|
|
NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE;
|
|
|
|
ip4_config = get_best_ip4_config (policy, TRUE, &ip_iface, NULL, NULL, NULL, &vpn);
|
|
if (ip4_config) {
|
|
if (vpn)
|
|
dns_type = NM_DNS_IP_CONFIG_TYPE_VPN;
|
|
|
|
/* Tell the DNS manager this config is preferred by re-adding it with
|
|
* a different IP config type.
|
|
*/
|
|
nm_dns_manager_add_ip4_config (dns_mgr, ip_iface, ip4_config, dns_type);
|
|
}
|
|
}
|
|
|
|
static void
|
|
update_ip4_routing (NMPolicy *policy, gboolean force_update)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
NMDevice *best = NULL, *default_device;
|
|
NMConnection *connection = NULL;
|
|
NMVPNConnection *vpn = NULL;
|
|
NMActiveConnection *best_ac = NULL;
|
|
NMIP4Config *ip4_config = NULL;
|
|
const char *ip_iface = NULL;
|
|
int ip_ifindex = -1;
|
|
guint32 gw_addr = 0;
|
|
|
|
/* Note that we might have an IPv4 VPN tunneled over an IPv6-only device,
|
|
* so we can get (vpn != NULL && best == NULL).
|
|
*/
|
|
ip4_config = get_best_ip4_config (policy, FALSE, &ip_iface, &ip_ifindex, &best_ac, &best, &vpn);
|
|
if (!ip4_config) {
|
|
gboolean changed;
|
|
|
|
changed = (priv->default_device4 != NULL);
|
|
priv->default_device4 = NULL;
|
|
if (changed)
|
|
g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP4_DEVICE);
|
|
|
|
return;
|
|
}
|
|
g_assert ((best || vpn) && best_ac);
|
|
|
|
if (!force_update && best && (best == priv->default_device4))
|
|
return;
|
|
|
|
gw_addr = nm_ip4_config_get_gateway (ip4_config);
|
|
|
|
if (vpn) {
|
|
NMDevice *parent = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn));
|
|
int parent_ifindex = nm_device_get_ip_ifindex (parent);
|
|
NMIP4Config *parent_ip4 = nm_device_get_ip4_config (parent);
|
|
guint32 parent_mss = parent_ip4 ? nm_ip4_config_get_mss (parent_ip4) : 0;
|
|
in_addr_t int_gw = nm_vpn_connection_get_ip4_internal_gateway (vpn);
|
|
int mss = nm_ip4_config_get_mss (ip4_config);
|
|
|
|
if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, int_gw, 0, mss)) {
|
|
(void) nm_platform_ip4_route_add (parent_ifindex, gw_addr, 32, 0, 0, parent_mss);
|
|
if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, int_gw, 0, mss)) {
|
|
nm_log_err (LOGD_IP4 | LOGD_VPN, "Failed to set default route.");
|
|
}
|
|
}
|
|
|
|
default_device = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn));
|
|
} else {
|
|
int mss = nm_ip4_config_get_mss (ip4_config);
|
|
|
|
if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, gw_addr, 0, mss)) {
|
|
(void) nm_platform_ip4_route_add (ip_ifindex, gw_addr, 32, 0, 0, mss);
|
|
if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, gw_addr, 0, mss)) {
|
|
nm_log_err (LOGD_IP4, "Failed to set default route.");
|
|
}
|
|
}
|
|
|
|
default_device = best;
|
|
}
|
|
|
|
update_default_ac (policy, best_ac, nm_active_connection_set_default);
|
|
|
|
if (default_device == priv->default_device4)
|
|
return;
|
|
|
|
priv->default_device4 = default_device;
|
|
connection = nm_active_connection_get_connection (best_ac);
|
|
nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for IPv4 routing and DNS.",
|
|
nm_connection_get_id (connection), ip_iface);
|
|
g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP4_DEVICE);
|
|
}
|
|
|
|
static NMIP6Config *
|
|
get_best_ip6_config (NMPolicy *policy,
|
|
gboolean ignore_never_default,
|
|
const char **out_ip_iface,
|
|
int *out_ip_ifindex,
|
|
NMActiveConnection **out_ac,
|
|
NMDevice **out_device,
|
|
NMVPNConnection **out_vpn)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
const GSList *connections, *iter;
|
|
NMDevice *device;
|
|
NMActRequest *req = NULL;
|
|
NMIP6Config *ip6_config = NULL;
|
|
|
|
/* If a VPN connection is active, it is preferred */
|
|
connections = nm_manager_get_active_connections (priv->manager);
|
|
for (iter = connections; iter; iter = g_slist_next (iter)) {
|
|
NMActiveConnection *active = NM_ACTIVE_CONNECTION (iter->data);
|
|
NMVPNConnection *candidate;
|
|
NMIP6Config *vpn_ip6;
|
|
NMConnection *tmp;
|
|
NMSettingIP6Config *s_ip6;
|
|
NMVPNConnectionState vpn_state;
|
|
|
|
if (!NM_IS_VPN_CONNECTION (active))
|
|
continue;
|
|
|
|
candidate = NM_VPN_CONNECTION (active);
|
|
|
|
tmp = nm_active_connection_get_connection (active);
|
|
g_assert (tmp);
|
|
|
|
vpn_state = nm_vpn_connection_get_vpn_state (candidate);
|
|
if (vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED)
|
|
continue;
|
|
|
|
vpn_ip6 = nm_vpn_connection_get_ip6_config (candidate);
|
|
if (!vpn_ip6)
|
|
continue;
|
|
|
|
if (ignore_never_default == FALSE) {
|
|
/* Check for a VPN-provided config never-default */
|
|
if (nm_ip6_config_get_never_default (vpn_ip6))
|
|
continue;
|
|
|
|
/* Check the user's preference from the NMConnection */
|
|
s_ip6 = nm_connection_get_setting_ip6_config (tmp);
|
|
if (nm_setting_ip6_config_get_never_default (s_ip6))
|
|
continue;
|
|
}
|
|
|
|
ip6_config = vpn_ip6;
|
|
if (out_vpn)
|
|
*out_vpn = candidate;
|
|
if (out_ac)
|
|
*out_ac = NM_ACTIVE_CONNECTION (candidate);
|
|
if (out_ip_iface)
|
|
*out_ip_iface = nm_vpn_connection_get_ip_iface (candidate);
|
|
if (out_ip_ifindex)
|
|
*out_ip_ifindex = nm_vpn_connection_get_ip_ifindex (candidate);
|
|
break;
|
|
}
|
|
|
|
/* If no VPN connections, we use the best device instead */
|
|
if (!ip6_config) {
|
|
device = get_best_ip6_device (priv->manager, TRUE);
|
|
if (device) {
|
|
req = nm_device_get_act_request (device);
|
|
g_assert (req);
|
|
ip6_config = nm_device_get_ip6_config (device);
|
|
g_assert (ip6_config);
|
|
|
|
if (out_device)
|
|
*out_device = device;
|
|
if (out_ac)
|
|
*out_ac = NM_ACTIVE_CONNECTION (req);
|
|
if (out_ip_iface)
|
|
*out_ip_iface = nm_device_get_ip_iface (device);
|
|
if (out_ip_ifindex)
|
|
*out_ip_ifindex = nm_device_get_ip_ifindex (device);
|
|
}
|
|
}
|
|
|
|
return ip6_config;
|
|
}
|
|
|
|
static void
|
|
update_ip6_dns (NMPolicy *policy, NMDnsManager *dns_mgr)
|
|
{
|
|
NMIP6Config *ip6_config;
|
|
const char *ip_iface = NULL;
|
|
NMVPNConnection *vpn = NULL;
|
|
NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE;
|
|
|
|
ip6_config = get_best_ip6_config (policy, TRUE, &ip_iface, NULL, NULL, NULL, &vpn);
|
|
if (ip6_config) {
|
|
if (vpn)
|
|
dns_type = NM_DNS_IP_CONFIG_TYPE_VPN;
|
|
|
|
/* Tell the DNS manager this config is preferred by re-adding it with
|
|
* a different IP config type.
|
|
*/
|
|
nm_dns_manager_add_ip6_config (dns_mgr, ip_iface, ip6_config, dns_type);
|
|
}
|
|
}
|
|
|
|
static void
|
|
update_ip6_routing (NMPolicy *policy, gboolean force_update)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
NMDevice *best = NULL, *default_device6;
|
|
NMConnection *connection = NULL;
|
|
NMVPNConnection *vpn = NULL;
|
|
NMActiveConnection *best_ac = NULL;
|
|
NMIP6Config *ip6_config = NULL;
|
|
const char *ip_iface = NULL;
|
|
int ip_ifindex = -1;
|
|
const struct in6_addr *gw_addr;
|
|
|
|
/* Note that we might have an IPv6 VPN tunneled over an IPv4-only device,
|
|
* so we can get (vpn != NULL && best == NULL).
|
|
*/
|
|
ip6_config = get_best_ip6_config (policy, FALSE, &ip_iface, &ip_ifindex, &best_ac, &best, &vpn);
|
|
if (!ip6_config) {
|
|
gboolean changed;
|
|
|
|
changed = (priv->default_device6 != NULL);
|
|
priv->default_device6 = NULL;
|
|
if (changed)
|
|
g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP6_DEVICE);
|
|
|
|
return;
|
|
}
|
|
g_assert ((best || vpn) && best_ac);
|
|
|
|
if (!force_update && best && (best == priv->default_device6))
|
|
return;
|
|
|
|
/* If no better gateway is found, use ::; not all configurations will
|
|
* have a gateway, especially WWAN/Point-to-Point connections.
|
|
*/
|
|
gw_addr = nm_ip6_config_get_gateway (ip6_config);
|
|
if (!gw_addr)
|
|
gw_addr = &in6addr_any;
|
|
|
|
if (vpn) {
|
|
NMDevice *parent = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn));
|
|
int parent_ifindex = nm_device_get_ip_ifindex (parent);
|
|
NMIP6Config *parent_ip6 = nm_device_get_ip6_config (parent);
|
|
guint32 parent_mss = parent_ip6 ? nm_ip6_config_get_mss (parent_ip6) : 0;
|
|
const struct in6_addr *int_gw = nm_vpn_connection_get_ip6_internal_gateway (vpn);
|
|
int mss = nm_ip6_config_get_mss (ip6_config);
|
|
|
|
if (!int_gw)
|
|
int_gw = &in6addr_any;
|
|
|
|
if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *int_gw, 0, mss)) {
|
|
(void) nm_platform_ip6_route_add (parent_ifindex, *gw_addr, 128, in6addr_any, 0, parent_mss);
|
|
if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *int_gw, 0, mss)) {
|
|
nm_log_err (LOGD_IP6 | LOGD_VPN, "Failed to set default route.");
|
|
}
|
|
}
|
|
|
|
default_device6 = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn));
|
|
} else {
|
|
int mss = nm_ip6_config_get_mss (ip6_config);
|
|
|
|
if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *gw_addr, 0, mss)) {
|
|
(void) nm_platform_ip6_route_add (ip_ifindex, *gw_addr, 128, in6addr_any, 0, mss);
|
|
if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *gw_addr, 0, mss)) {
|
|
nm_log_err (LOGD_IP6, "Failed to set default route.");
|
|
}
|
|
}
|
|
|
|
default_device6 = best;
|
|
}
|
|
|
|
update_default_ac (policy, best_ac, nm_active_connection_set_default6);
|
|
|
|
if (default_device6 == priv->default_device6)
|
|
return;
|
|
|
|
priv->default_device6 = default_device6;
|
|
connection = nm_active_connection_get_connection (best_ac);
|
|
nm_log_info (LOGD_CORE, "Policy set '%s' (%s) as default for IPv6 routing and DNS.",
|
|
nm_connection_get_id (connection), ip_iface);
|
|
g_object_notify (G_OBJECT (policy), NM_POLICY_DEFAULT_IP6_DEVICE);
|
|
}
|
|
|
|
static void
|
|
update_routing_and_dns (NMPolicy *policy, gboolean force_update)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
|
|
nm_dns_manager_begin_updates (priv->dns_manager, __func__);
|
|
|
|
update_ip4_dns (policy, priv->dns_manager);
|
|
update_ip6_dns (policy, priv->dns_manager);
|
|
|
|
update_ip4_routing (policy, force_update);
|
|
update_ip6_routing (policy, force_update);
|
|
|
|
/* Update the system hostname */
|
|
update_system_hostname (policy, priv->default_device4, priv->default_device6);
|
|
|
|
nm_dns_manager_end_updates (priv->dns_manager, __func__);
|
|
}
|
|
|
|
static void
|
|
check_activating_devices (NMPolicy *policy)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
GObject *object = G_OBJECT (policy);
|
|
NMDevice *best4, *best6 = NULL;
|
|
|
|
best4 = get_best_ip4_device (priv->manager, FALSE);
|
|
best6 = get_best_ip6_device (priv->manager, FALSE);
|
|
|
|
g_object_freeze_notify (object);
|
|
|
|
if (best4 != priv->activating_device4) {
|
|
priv->activating_device4 = best4;
|
|
g_object_notify (object, NM_POLICY_ACTIVATING_IP4_DEVICE);
|
|
}
|
|
if (best6 != priv->activating_device6) {
|
|
priv->activating_device6 = best6;
|
|
g_object_notify (object, NM_POLICY_ACTIVATING_IP6_DEVICE);
|
|
}
|
|
|
|
g_object_thaw_notify (object);
|
|
}
|
|
|
|
static void
|
|
set_connection_auto_retries (NMConnection *connection, guint retries)
|
|
{
|
|
/* add +1 so that the tag still exists if the # retries is 0 */
|
|
g_object_set_data (G_OBJECT (connection), RETRIES_TAG, GUINT_TO_POINTER (retries + 1));
|
|
}
|
|
|
|
static guint32
|
|
get_connection_auto_retries (NMConnection *connection)
|
|
{
|
|
/* subtract 1 to handle the +1 from set_connection_auto_retries() */
|
|
return GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), RETRIES_TAG)) - 1;
|
|
}
|
|
|
|
typedef struct {
|
|
NMPolicy *policy;
|
|
NMDevice *device;
|
|
guint id;
|
|
} ActivateData;
|
|
|
|
static void
|
|
activate_data_free (ActivateData *data)
|
|
{
|
|
nm_device_remove_pending_action (data->device, "autoactivate");
|
|
|
|
if (data->id)
|
|
g_source_remove (data->id);
|
|
g_object_unref (data->device);
|
|
memset (data, 0, sizeof (*data));
|
|
g_free (data);
|
|
}
|
|
|
|
static gboolean
|
|
auto_activate_device (gpointer user_data)
|
|
{
|
|
ActivateData *data = (ActivateData *) user_data;
|
|
NMPolicy *policy;
|
|
NMPolicyPrivate *priv;
|
|
NMConnection *best_connection;
|
|
char *specific_object = NULL;
|
|
GSList *connections, *iter;
|
|
|
|
g_assert (data);
|
|
policy = data->policy;
|
|
priv = NM_POLICY_GET_PRIVATE (policy);
|
|
|
|
data->id = 0;
|
|
priv->pending_activation_checks = g_slist_remove (priv->pending_activation_checks, data);
|
|
|
|
// FIXME: if a device is already activating (or activated) with a connection
|
|
// but another connection now overrides the current one for that device,
|
|
// deactivate the device and activate the new connection instead of just
|
|
// bailing if the device is already active
|
|
if (nm_device_get_act_request (data->device))
|
|
goto out;
|
|
|
|
iter = connections = nm_manager_get_activatable_connections (priv->manager);
|
|
|
|
/* Remove connections that shouldn't be auto-activated */
|
|
while (iter) {
|
|
NMSettingsConnection *candidate = NM_SETTINGS_CONNECTION (iter->data);
|
|
gboolean remove_it = FALSE;
|
|
const char *permission;
|
|
|
|
/* Grab next item before we possibly delete the current item */
|
|
iter = g_slist_next (iter);
|
|
|
|
/* Ignore connections that were tried too many times or are not visible
|
|
* to any logged-in users. Also ignore shared wifi connections for
|
|
* which no user has the shared wifi permission.
|
|
*/
|
|
if ( get_connection_auto_retries (NM_CONNECTION (candidate)) == 0
|
|
|| nm_settings_connection_is_visible (candidate) == FALSE)
|
|
remove_it = TRUE;
|
|
else {
|
|
permission = nm_utils_get_shared_wifi_permission (NM_CONNECTION (candidate));
|
|
if (permission) {
|
|
if (nm_settings_connection_check_permission (candidate, permission) == FALSE)
|
|
remove_it = TRUE;
|
|
}
|
|
}
|
|
|
|
if (remove_it)
|
|
connections = g_slist_remove (connections, candidate);
|
|
}
|
|
|
|
best_connection = nm_device_get_best_auto_connection (data->device, connections, &specific_object);
|
|
if (best_connection) {
|
|
GError *error = NULL;
|
|
NMAuthSubject *subject;
|
|
|
|
nm_log_info (LOGD_DEVICE, "Auto-activating connection '%s'.",
|
|
nm_connection_get_id (best_connection));
|
|
subject = nm_auth_subject_new_internal ();
|
|
if (!nm_manager_activate_connection (priv->manager,
|
|
best_connection,
|
|
specific_object,
|
|
data->device,
|
|
subject,
|
|
&error)) {
|
|
nm_log_info (LOGD_DEVICE, "Connection '%s' auto-activation failed: (%d) %s",
|
|
nm_connection_get_id (best_connection),
|
|
error ? error->code : -1,
|
|
error ? error->message : "(none)");
|
|
g_error_free (error);
|
|
}
|
|
g_object_unref (subject);
|
|
}
|
|
|
|
g_slist_free (connections);
|
|
|
|
out:
|
|
activate_data_free (data);
|
|
return FALSE;
|
|
}
|
|
|
|
static ActivateData *
|
|
activate_data_new (NMPolicy *policy, NMDevice *device, guint delay_seconds)
|
|
{
|
|
ActivateData *data;
|
|
|
|
data = g_malloc0 (sizeof (ActivateData));
|
|
data->policy = policy;
|
|
data->device = g_object_ref (device);
|
|
if (delay_seconds > 0)
|
|
data->id = g_timeout_add_seconds (delay_seconds, auto_activate_device, data);
|
|
else
|
|
data->id = g_idle_add (auto_activate_device, data);
|
|
|
|
nm_device_add_pending_action (device, "autoactivate");
|
|
|
|
return data;
|
|
}
|
|
|
|
static ActivateData *
|
|
find_pending_activation (GSList *list, NMDevice *device)
|
|
{
|
|
GSList *iter;
|
|
|
|
for (iter = list; iter; iter = g_slist_next (iter)) {
|
|
if (((ActivateData *) iter->data)->device == device)
|
|
return iter->data;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
NMDevice *device;
|
|
GSList *secondaries;
|
|
} PendingSecondaryData;
|
|
|
|
static PendingSecondaryData *
|
|
pending_secondary_data_new (NMDevice *device, GSList *secondaries)
|
|
{
|
|
PendingSecondaryData *data;
|
|
|
|
data = g_malloc0 (sizeof (PendingSecondaryData));
|
|
data->device = g_object_ref (device);
|
|
data->secondaries = secondaries;
|
|
return data;
|
|
}
|
|
|
|
static void
|
|
pending_secondary_data_free (PendingSecondaryData *data)
|
|
{
|
|
g_object_unref (data->device);
|
|
g_slist_free_full (data->secondaries, g_object_unref);
|
|
memset (data, 0, sizeof (*data));
|
|
g_free (data);
|
|
}
|
|
|
|
static void
|
|
process_secondaries (NMPolicy *policy,
|
|
NMActiveConnection *active,
|
|
gboolean connected)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
GSList *iter, *iter2;
|
|
|
|
/* Loop through devices waiting for secondary connections to activate */
|
|
for (iter = priv->pending_secondaries; iter; iter = g_slist_next (iter)) {
|
|
PendingSecondaryData *secondary_data = (PendingSecondaryData *) iter->data;
|
|
NMDevice *item_device = secondary_data->device;
|
|
|
|
/* Look for 'active' in each device's secondary connections list */
|
|
for (iter2 = secondary_data->secondaries; iter2; iter2 = g_slist_next (iter2)) {
|
|
NMActiveConnection *secondary_active = NM_ACTIVE_CONNECTION (iter2->data);
|
|
|
|
if (active != secondary_active)
|
|
continue;
|
|
|
|
if (connected) {
|
|
nm_log_dbg (LOGD_DEVICE, "Secondary connection '%s' SUCCEEDED; active path '%s'",
|
|
nm_active_connection_get_name (active),
|
|
nm_active_connection_get_path (active));
|
|
|
|
/* Secondary connection activated */
|
|
secondary_data->secondaries = g_slist_remove (secondary_data->secondaries, secondary_active);
|
|
g_object_unref (secondary_active);
|
|
if (!secondary_data->secondaries) {
|
|
/* No secondary UUID remained -> remove the secondary data item */
|
|
priv->pending_secondaries = g_slist_remove (priv->pending_secondaries, secondary_data);
|
|
pending_secondary_data_free (secondary_data);
|
|
nm_device_state_changed (item_device, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE);
|
|
break;
|
|
}
|
|
} else {
|
|
nm_log_dbg (LOGD_DEVICE, "Secondary connection '%s' FAILED; active path '%s'",
|
|
nm_active_connection_get_name (active),
|
|
nm_active_connection_get_path (active));
|
|
|
|
/* Secondary connection failed -> do not watch other connections */
|
|
priv->pending_secondaries = g_slist_remove (priv->pending_secondaries, secondary_data);
|
|
pending_secondary_data_free (secondary_data);
|
|
nm_device_state_changed (item_device, NM_DEVICE_STATE_FAILED,
|
|
NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
global_state_changed (NMManager *manager, NMState state, gpointer user_data)
|
|
{
|
|
}
|
|
|
|
static void
|
|
hostname_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
|
|
{
|
|
update_system_hostname ((NMPolicy *) user_data, NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
reset_retries_all (NMSettings *settings, NMDevice *device)
|
|
{
|
|
GSList *connections, *iter;
|
|
GError *error = NULL;
|
|
|
|
connections = nm_settings_get_connections (settings);
|
|
for (iter = connections; iter; iter = g_slist_next (iter)) {
|
|
if (!device || nm_device_check_connection_compatible (device, iter->data, &error))
|
|
set_connection_auto_retries (NM_CONNECTION (iter->data), RETRIES_DEFAULT);
|
|
g_clear_error (&error);
|
|
}
|
|
g_slist_free (connections);
|
|
}
|
|
|
|
static void
|
|
reset_retries_for_failed_secrets (NMSettings *settings)
|
|
{
|
|
GSList *connections, *iter;
|
|
|
|
connections = nm_settings_get_connections (settings);
|
|
for (iter = connections; iter; iter = g_slist_next (iter)) {
|
|
NMDeviceStateReason reason = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (iter->data), FAILURE_REASON_TAG));
|
|
|
|
if (reason == NM_DEVICE_STATE_REASON_NO_SECRETS) {
|
|
set_connection_auto_retries (NM_CONNECTION (iter->data), RETRIES_DEFAULT);
|
|
g_object_set_data (G_OBJECT (iter->data), FAILURE_REASON_TAG, GUINT_TO_POINTER (0));
|
|
}
|
|
}
|
|
g_slist_free (connections);
|
|
}
|
|
|
|
static void
|
|
sleeping_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
|
|
{
|
|
NMPolicy *policy = user_data;
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
gboolean sleeping = FALSE, enabled = FALSE;
|
|
|
|
g_object_get (G_OBJECT (manager), NM_MANAGER_SLEEPING, &sleeping, NULL);
|
|
g_object_get (G_OBJECT (manager), NM_MANAGER_NETWORKING_ENABLED, &enabled, NULL);
|
|
|
|
/* Reset retries on all connections so they'll checked on wakeup */
|
|
if (sleeping || !enabled)
|
|
reset_retries_all (priv->settings, NULL);
|
|
}
|
|
|
|
static void
|
|
schedule_activate_check (NMPolicy *policy, NMDevice *device, guint delay_seconds)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
ActivateData *data;
|
|
const GSList *active_connections, *iter;
|
|
|
|
if (nm_manager_get_state (priv->manager) == NM_STATE_ASLEEP)
|
|
return;
|
|
|
|
if (!nm_device_get_enabled (device))
|
|
return;
|
|
|
|
if (!nm_device_autoconnect_allowed (device))
|
|
return;
|
|
|
|
/* If the device already has an activation in-progress or waiting for
|
|
* authentication, don't start an auto-activation for it.
|
|
*/
|
|
active_connections = nm_manager_get_active_connections (priv->manager);
|
|
for (iter = active_connections; iter; iter = iter->next) {
|
|
if (nm_active_connection_get_device (NM_ACTIVE_CONNECTION (iter->data)) == device)
|
|
return;
|
|
}
|
|
|
|
/* Schedule an auto-activation if there isn't one already for this device */
|
|
if (find_pending_activation (priv->pending_activation_checks, device) == NULL) {
|
|
data = activate_data_new (policy, device, delay_seconds);
|
|
priv->pending_activation_checks = g_slist_append (priv->pending_activation_checks, data);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
reset_connections_retries (gpointer user_data)
|
|
{
|
|
NMPolicy *policy = (NMPolicy *) user_data;
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
GSList *connections, *iter;
|
|
time_t con_stamp, min_stamp, now;
|
|
gboolean changed = FALSE;
|
|
|
|
priv->reset_retries_id = 0;
|
|
|
|
min_stamp = now = time (NULL);
|
|
connections = nm_settings_get_connections (priv->settings);
|
|
for (iter = connections; iter; iter = g_slist_next (iter)) {
|
|
con_stamp = GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (iter->data), RESET_RETRIES_TIMESTAMP_TAG));
|
|
if (con_stamp == 0)
|
|
continue;
|
|
if (con_stamp + RESET_RETRIES_TIMER <= now) {
|
|
set_connection_auto_retries (NM_CONNECTION (iter->data), RETRIES_DEFAULT);
|
|
g_object_set_data (G_OBJECT (iter->data), RESET_RETRIES_TIMESTAMP_TAG, GSIZE_TO_POINTER (0));
|
|
changed = TRUE;
|
|
continue;
|
|
}
|
|
if (con_stamp < min_stamp)
|
|
min_stamp = con_stamp;
|
|
}
|
|
g_slist_free (connections);
|
|
|
|
/* Schedule the handler again if there are some stamps left */
|
|
if (min_stamp != now)
|
|
priv->reset_retries_id = g_timeout_add_seconds (RESET_RETRIES_TIMER - (now - min_stamp), reset_connections_retries, policy);
|
|
|
|
/* If anything changed, try to activate the newly re-enabled connections */
|
|
if (changed)
|
|
schedule_activate_all (policy);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void schedule_activate_all (NMPolicy *policy);
|
|
|
|
static void
|
|
activate_slave_connections (NMPolicy *policy, NMConnection *connection,
|
|
NMDevice *device)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
const char *master_device;
|
|
GSList *connections, *iter;
|
|
|
|
master_device = nm_device_get_iface (device);
|
|
g_assert (master_device);
|
|
|
|
connections = nm_settings_get_connections (priv->settings);
|
|
for (iter = connections; iter; iter = g_slist_next (iter)) {
|
|
NMConnection *slave;
|
|
NMSettingConnection *s_slave_con;
|
|
|
|
slave = NM_CONNECTION (iter->data);
|
|
g_assert (slave);
|
|
|
|
s_slave_con = nm_connection_get_setting_connection (slave);
|
|
g_assert (s_slave_con);
|
|
|
|
if (!g_strcmp0 (nm_setting_connection_get_master (s_slave_con), master_device))
|
|
set_connection_auto_retries (slave, RETRIES_DEFAULT);
|
|
}
|
|
|
|
g_slist_free (connections);
|
|
|
|
schedule_activate_all (policy);
|
|
}
|
|
|
|
static gboolean
|
|
activate_secondary_connections (NMPolicy *policy,
|
|
NMConnection *connection,
|
|
NMDevice *device)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
NMSettingConnection *s_con;
|
|
NMSettingsConnection *settings_con;
|
|
NMActiveConnection *ac;
|
|
PendingSecondaryData *secondary_data;
|
|
GSList *secondary_ac_list = NULL;
|
|
GError *error = NULL;
|
|
guint32 i;
|
|
gboolean success = TRUE;
|
|
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
g_assert (s_con);
|
|
|
|
for (i = 0; i < nm_setting_connection_get_num_secondaries (s_con); i++) {
|
|
const char *sec_uuid = nm_setting_connection_get_secondary (s_con, i);
|
|
NMActRequest *req;
|
|
|
|
settings_con = nm_settings_get_connection_by_uuid (priv->settings, sec_uuid);
|
|
if (!settings_con) {
|
|
nm_log_warn (LOGD_DEVICE, "Secondary connection '%s' auto-activation failed: The connection doesn't exist.",
|
|
sec_uuid);
|
|
success = FALSE;
|
|
break;
|
|
}
|
|
if (!nm_connection_is_type (NM_CONNECTION (settings_con), NM_SETTING_VPN_SETTING_NAME)) {
|
|
nm_log_warn (LOGD_DEVICE, "Secondary connection '%s (%s)' auto-activation failed: The connection is not a VPN.",
|
|
nm_connection_get_id (NM_CONNECTION (settings_con)), sec_uuid);
|
|
success = FALSE;
|
|
break;
|
|
}
|
|
|
|
req = nm_device_get_act_request (device);
|
|
g_assert (req);
|
|
|
|
nm_log_dbg (LOGD_DEVICE, "Activating secondary connection '%s (%s)' for base connection '%s (%s)'",
|
|
nm_connection_get_id (NM_CONNECTION (settings_con)), sec_uuid,
|
|
nm_connection_get_id (connection), nm_connection_get_uuid (connection));
|
|
ac = nm_manager_activate_connection (priv->manager,
|
|
NM_CONNECTION (settings_con),
|
|
nm_active_connection_get_path (NM_ACTIVE_CONNECTION (req)),
|
|
device,
|
|
nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (req)),
|
|
&error);
|
|
if (ac)
|
|
secondary_ac_list = g_slist_append (secondary_ac_list, g_object_ref (ac));
|
|
else {
|
|
nm_log_warn (LOGD_DEVICE, "Secondary connection '%s (%s)' auto-activation failed: (%d) %s",
|
|
nm_connection_get_id (NM_CONNECTION (settings_con)), sec_uuid,
|
|
error ? error->code : 0,
|
|
(error && error->message) ? error->message : "unknown");
|
|
g_clear_error (&error);
|
|
success = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (success && secondary_ac_list != NULL) {
|
|
secondary_data = pending_secondary_data_new (device, secondary_ac_list);
|
|
priv->pending_secondaries = g_slist_append (priv->pending_secondaries, secondary_data);
|
|
} else
|
|
g_slist_free_full (secondary_ac_list, g_object_unref);
|
|
|
|
return success;
|
|
}
|
|
|
|
static void
|
|
device_state_changed (NMDevice *device,
|
|
NMDeviceState new_state,
|
|
NMDeviceState old_state,
|
|
NMDeviceStateReason reason,
|
|
gpointer user_data)
|
|
{
|
|
NMPolicy *policy = (NMPolicy *) user_data;
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
NMConnection *connection = nm_device_get_connection (device);
|
|
const char *ip_iface = nm_device_get_ip_iface (device);
|
|
NMIP4Config *ip4_config;
|
|
NMIP6Config *ip6_config;
|
|
NMSettingConnection *s_con;
|
|
|
|
if (connection)
|
|
g_object_set_data (G_OBJECT (connection), FAILURE_REASON_TAG, GUINT_TO_POINTER (0));
|
|
|
|
switch (new_state) {
|
|
case NM_DEVICE_STATE_FAILED:
|
|
/* Mark the connection invalid if it failed during activation so that
|
|
* it doesn't get automatically chosen over and over and over again.
|
|
*/
|
|
if ( connection
|
|
&& old_state >= NM_DEVICE_STATE_PREPARE
|
|
&& old_state <= NM_DEVICE_STATE_ACTIVATED) {
|
|
guint32 tries = get_connection_auto_retries (connection);
|
|
|
|
if (reason == NM_DEVICE_STATE_REASON_NO_SECRETS) {
|
|
/* If the connection couldn't get the secrets it needed (ex because
|
|
* the user canceled, or no secrets exist), there's no point in
|
|
* automatically retrying because it's just going to fail anyway.
|
|
*/
|
|
set_connection_auto_retries (connection, 0);
|
|
|
|
/* Mark the connection as failed due to missing secrets so that we can reset
|
|
* RETRIES_TAG and automatically re-try when an secret agent registers.
|
|
*/
|
|
g_object_set_data (G_OBJECT (connection), FAILURE_REASON_TAG, GUINT_TO_POINTER (NM_DEVICE_STATE_REASON_NO_SECRETS));
|
|
} else if (tries > 0) {
|
|
/* Otherwise if it's a random failure, just decrease the number
|
|
* of automatic retries so that the connection gets tried again
|
|
* if it still has a retry count.
|
|
*/
|
|
set_connection_auto_retries (connection, tries - 1);
|
|
}
|
|
|
|
if (get_connection_auto_retries (connection) == 0) {
|
|
nm_log_info (LOGD_DEVICE, "Marking connection '%s' invalid.", nm_connection_get_id (connection));
|
|
/* Schedule a handler to reset retries count */
|
|
g_object_set_data (G_OBJECT (connection), RESET_RETRIES_TIMESTAMP_TAG, GSIZE_TO_POINTER ((gsize) time (NULL)));
|
|
if (!priv->reset_retries_id)
|
|
priv->reset_retries_id = g_timeout_add_seconds (RESET_RETRIES_TIMER, reset_connections_retries, policy);
|
|
}
|
|
nm_connection_clear_secrets (connection);
|
|
}
|
|
break;
|
|
case NM_DEVICE_STATE_ACTIVATED:
|
|
if (connection) {
|
|
/* Reset auto retries back to default since connection was successful */
|
|
set_connection_auto_retries (connection, RETRIES_DEFAULT);
|
|
|
|
/* And clear secrets so they will always be requested from the
|
|
* settings service when the next connection is made.
|
|
*/
|
|
nm_connection_clear_secrets (connection);
|
|
}
|
|
|
|
/* Add device's new IPv4 and IPv6 configs to DNS */
|
|
|
|
nm_dns_manager_begin_updates (priv->dns_manager, __func__);
|
|
|
|
ip4_config = nm_device_get_ip4_config (device);
|
|
if (ip4_config)
|
|
nm_dns_manager_add_ip4_config (priv->dns_manager, ip_iface, ip4_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
|
|
ip6_config = nm_device_get_ip6_config (device);
|
|
if (ip6_config)
|
|
nm_dns_manager_add_ip6_config (priv->dns_manager, ip_iface, ip6_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
|
|
|
|
update_routing_and_dns (policy, FALSE);
|
|
|
|
nm_dns_manager_end_updates (priv->dns_manager, __func__);
|
|
break;
|
|
case NM_DEVICE_STATE_UNMANAGED:
|
|
case NM_DEVICE_STATE_UNAVAILABLE:
|
|
if (old_state > NM_DEVICE_STATE_DISCONNECTED)
|
|
update_routing_and_dns (policy, FALSE);
|
|
break;
|
|
case NM_DEVICE_STATE_DISCONNECTED:
|
|
/* Reset RETRIES_TAG when carrier on. If cable was unplugged
|
|
* and plugged again, we should try to reconnect */
|
|
if (reason == NM_DEVICE_STATE_REASON_CARRIER && old_state == NM_DEVICE_STATE_UNAVAILABLE)
|
|
reset_retries_all (priv->settings, device);
|
|
|
|
if (old_state > NM_DEVICE_STATE_DISCONNECTED)
|
|
update_routing_and_dns (policy, FALSE);
|
|
|
|
/* Device is now available for auto-activation */
|
|
schedule_activate_check (policy, device, 0);
|
|
break;
|
|
|
|
case NM_DEVICE_STATE_PREPARE:
|
|
/* Reset auto-connect retries of all slaves and schedule them for
|
|
* activation. */
|
|
activate_slave_connections (policy, connection, device);
|
|
break;
|
|
case NM_DEVICE_STATE_SECONDARIES:
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
if (s_con && nm_setting_connection_get_num_secondaries (s_con) > 0) {
|
|
/* Make routes and DNS up-to-date before activating dependent connections */
|
|
update_routing_and_dns (policy, FALSE);
|
|
|
|
/* Activate secondary (VPN) connections */
|
|
if (!activate_secondary_connections (policy, connection, device))
|
|
nm_device_queue_state (device, NM_DEVICE_STATE_FAILED,
|
|
NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED);
|
|
} else
|
|
nm_device_queue_state (device, NM_DEVICE_STATE_ACTIVATED,
|
|
NM_DEVICE_STATE_REASON_NONE);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
check_activating_devices (policy);
|
|
}
|
|
|
|
static void
|
|
device_ip4_config_changed (NMDevice *device,
|
|
NMIP4Config *new_config,
|
|
NMIP4Config *old_config,
|
|
gpointer user_data)
|
|
{
|
|
NMPolicy *policy = user_data;
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
const char *ip_iface = nm_device_get_ip_iface (device);
|
|
|
|
nm_dns_manager_begin_updates (priv->dns_manager, __func__);
|
|
|
|
/* Ignore IP config changes while the device is activating, because we'll
|
|
* catch all the changes when the device moves to ACTIVATED state.
|
|
* Prevents unecessary changes to DNS information.
|
|
*/
|
|
if (!nm_device_is_activating (device)) {
|
|
if (old_config != new_config) {
|
|
if (old_config)
|
|
nm_dns_manager_remove_ip4_config (priv->dns_manager, old_config);
|
|
if (new_config)
|
|
nm_dns_manager_add_ip4_config (priv->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
|
|
}
|
|
update_ip4_dns (policy, priv->dns_manager);
|
|
update_ip4_routing (policy, TRUE);
|
|
} else {
|
|
/* Old configs get removed immediately */
|
|
if (old_config)
|
|
nm_dns_manager_remove_ip4_config (priv->dns_manager, old_config);
|
|
}
|
|
|
|
nm_dns_manager_end_updates (priv->dns_manager, __func__);
|
|
}
|
|
|
|
static void
|
|
device_ip6_config_changed (NMDevice *device,
|
|
NMIP6Config *new_config,
|
|
NMIP6Config *old_config,
|
|
gpointer user_data)
|
|
{
|
|
NMPolicy *policy = user_data;
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
const char *ip_iface = nm_device_get_ip_iface (device);
|
|
|
|
nm_dns_manager_begin_updates (priv->dns_manager, __func__);
|
|
|
|
/* Ignore IP config changes while the device is activating, because we'll
|
|
* catch all the changes when the device moves to ACTIVATED state.
|
|
* Prevents unecessary changes to DNS information.
|
|
*/
|
|
if (!nm_device_is_activating (device)) {
|
|
if (old_config != new_config) {
|
|
if (old_config)
|
|
nm_dns_manager_remove_ip6_config (priv->dns_manager, old_config);
|
|
if (new_config)
|
|
nm_dns_manager_add_ip6_config (priv->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
|
|
}
|
|
update_ip6_dns (policy, priv->dns_manager);
|
|
update_ip6_routing (policy, TRUE);
|
|
} else {
|
|
/* Old configs get removed immediately */
|
|
if (old_config)
|
|
nm_dns_manager_remove_ip6_config (priv->dns_manager, old_config);
|
|
}
|
|
|
|
nm_dns_manager_end_updates (priv->dns_manager, __func__);
|
|
}
|
|
|
|
static void
|
|
device_autoconnect_changed (NMDevice *device,
|
|
GParamSpec *pspec,
|
|
gpointer user_data)
|
|
{
|
|
if (nm_device_get_autoconnect (device))
|
|
schedule_activate_check ((NMPolicy *) user_data, device, 0);
|
|
}
|
|
|
|
static void
|
|
wireless_networks_changed (NMDevice *device, GObject *ap, gpointer user_data)
|
|
{
|
|
schedule_activate_check ((NMPolicy *) user_data, device, 0);
|
|
}
|
|
|
|
static void
|
|
nsps_changed (NMDevice *device, GObject *nsp, gpointer user_data)
|
|
{
|
|
schedule_activate_check ((NMPolicy *) user_data, device, 0);
|
|
}
|
|
|
|
static void
|
|
modem_enabled_changed (NMDevice *device, gpointer user_data)
|
|
{
|
|
schedule_activate_check ((NMPolicy *) (user_data), device, 0);
|
|
}
|
|
|
|
typedef struct {
|
|
gulong id;
|
|
NMDevice *device;
|
|
} DeviceSignalId;
|
|
|
|
static void
|
|
_connect_device_signal (NMPolicy *policy,
|
|
NMDevice *device,
|
|
const char *name,
|
|
gpointer callback,
|
|
gboolean after)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
DeviceSignalId *data;
|
|
|
|
data = g_slice_new0 (DeviceSignalId);
|
|
g_assert (data);
|
|
if (after)
|
|
data->id = g_signal_connect_after (device, name, callback, policy);
|
|
else
|
|
data->id = g_signal_connect (device, name, callback, policy);
|
|
data->device = device;
|
|
priv->dev_ids = g_slist_prepend (priv->dev_ids, data);
|
|
}
|
|
|
|
static void
|
|
device_added (NMManager *manager, NMDevice *device, gpointer user_data)
|
|
{
|
|
NMPolicy *policy = (NMPolicy *) user_data;
|
|
|
|
/* Connect state-changed with _after, so that the handler is invoked after other handlers. */
|
|
_connect_device_signal (policy, device, "state-changed", device_state_changed, TRUE);
|
|
_connect_device_signal (policy, device, NM_DEVICE_IP4_CONFIG_CHANGED, device_ip4_config_changed, FALSE);
|
|
_connect_device_signal (policy, device, NM_DEVICE_IP6_CONFIG_CHANGED, device_ip6_config_changed, FALSE);
|
|
_connect_device_signal (policy, device, "notify::" NM_DEVICE_AUTOCONNECT, device_autoconnect_changed, FALSE);
|
|
|
|
switch (nm_device_get_device_type (device)) {
|
|
case NM_DEVICE_TYPE_WIFI:
|
|
_connect_device_signal (policy, device, "access-point-added", wireless_networks_changed, FALSE);
|
|
_connect_device_signal (policy, device, "access-point-removed", wireless_networks_changed, FALSE);
|
|
break;
|
|
case NM_DEVICE_TYPE_WIMAX:
|
|
_connect_device_signal (policy, device, "nsp-added", nsps_changed, FALSE);
|
|
_connect_device_signal (policy, device, "nsp-removed", nsps_changed, FALSE);
|
|
break;
|
|
case NM_DEVICE_TYPE_MODEM:
|
|
_connect_device_signal (policy, device, "enable-changed", modem_enabled_changed, FALSE);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
device_removed (NMManager *manager, NMDevice *device, gpointer user_data)
|
|
{
|
|
NMPolicy *policy = (NMPolicy *) user_data;
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
ActivateData *tmp;
|
|
GSList *iter;
|
|
|
|
/* Clear any idle callbacks for this device */
|
|
tmp = find_pending_activation (priv->pending_activation_checks, device);
|
|
if (tmp) {
|
|
priv->pending_activation_checks = g_slist_remove (priv->pending_activation_checks, tmp);
|
|
activate_data_free (tmp);
|
|
}
|
|
|
|
/* Clear any signal handlers for this device */
|
|
iter = priv->dev_ids;
|
|
while (iter) {
|
|
DeviceSignalId *data = iter->data;
|
|
GSList *next = g_slist_next (iter);
|
|
|
|
if (data->device == device) {
|
|
g_signal_handler_disconnect (data->device, data->id);
|
|
g_slice_free (DeviceSignalId, data);
|
|
priv->dev_ids = g_slist_delete_link (priv->dev_ids, iter);
|
|
}
|
|
iter = next;
|
|
}
|
|
|
|
/* Don't update routing and DNS here as we've already handled that
|
|
* for devices that need it when the device's state changed to UNMANAGED.
|
|
*/
|
|
}
|
|
|
|
/**************************************************************************/
|
|
|
|
static void
|
|
vpn_connection_activated (NMPolicy *policy, NMVPNConnection *vpn)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
NMIP4Config *ip4_config;
|
|
NMIP6Config *ip6_config;
|
|
const char *ip_iface;
|
|
|
|
nm_dns_manager_begin_updates (priv->dns_manager, __func__);
|
|
|
|
ip_iface = nm_vpn_connection_get_ip_iface (vpn);
|
|
|
|
/* Add the VPN connection's IP configs from DNS */
|
|
|
|
ip4_config = nm_vpn_connection_get_ip4_config (vpn);
|
|
if (ip4_config)
|
|
nm_dns_manager_add_ip4_config (priv->dns_manager, ip_iface, ip4_config, NM_DNS_IP_CONFIG_TYPE_VPN);
|
|
|
|
ip6_config = nm_vpn_connection_get_ip6_config (vpn);
|
|
if (ip6_config)
|
|
nm_dns_manager_add_ip6_config (priv->dns_manager, ip_iface, ip6_config, NM_DNS_IP_CONFIG_TYPE_VPN);
|
|
|
|
update_routing_and_dns (policy, TRUE);
|
|
|
|
nm_dns_manager_end_updates (priv->dns_manager, __func__);
|
|
}
|
|
|
|
static void
|
|
vpn_connection_deactivated (NMPolicy *policy, NMVPNConnection *vpn)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
NMIP4Config *ip4_config;
|
|
NMIP6Config *ip6_config;
|
|
|
|
nm_dns_manager_begin_updates (priv->dns_manager, __func__);
|
|
|
|
ip4_config = nm_vpn_connection_get_ip4_config (vpn);
|
|
if (ip4_config) {
|
|
/* Remove the VPN connection's IP4 config from DNS */
|
|
nm_dns_manager_remove_ip4_config (priv->dns_manager, ip4_config);
|
|
}
|
|
|
|
ip6_config = nm_vpn_connection_get_ip6_config (vpn);
|
|
if (ip6_config) {
|
|
/* Remove the VPN connection's IP6 config from DNS */
|
|
nm_dns_manager_remove_ip6_config (priv->dns_manager, ip6_config);
|
|
}
|
|
|
|
update_routing_and_dns (policy, TRUE);
|
|
|
|
nm_dns_manager_end_updates (priv->dns_manager, __func__);
|
|
}
|
|
|
|
static void
|
|
vpn_connection_state_changed (NMVPNConnection *vpn,
|
|
NMVPNConnectionState new_state,
|
|
NMVPNConnectionState old_state,
|
|
NMVPNConnectionStateReason reason,
|
|
NMPolicy *policy)
|
|
{
|
|
if (new_state == NM_VPN_CONNECTION_STATE_ACTIVATED)
|
|
vpn_connection_activated (policy, vpn);
|
|
else if (new_state >= NM_VPN_CONNECTION_STATE_FAILED) {
|
|
/* Only clean up IP/DNS if the connection ever got past IP_CONFIG */
|
|
if (old_state >= NM_VPN_CONNECTION_STATE_IP_CONFIG_GET &&
|
|
old_state <= NM_VPN_CONNECTION_STATE_ACTIVATED)
|
|
vpn_connection_deactivated (policy, vpn);
|
|
}
|
|
}
|
|
|
|
static void
|
|
active_connection_state_changed (NMActiveConnection *active,
|
|
GParamSpec *pspec,
|
|
NMPolicy *policy)
|
|
{
|
|
NMActiveConnectionState state = nm_active_connection_get_state (active);
|
|
|
|
if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
|
|
process_secondaries (policy, active, TRUE);
|
|
else if (state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)
|
|
process_secondaries (policy, active, FALSE);
|
|
}
|
|
|
|
static void
|
|
active_connection_added (NMManager *manager,
|
|
NMActiveConnection *active,
|
|
gpointer user_data)
|
|
{
|
|
NMPolicy *policy = NM_POLICY (user_data);
|
|
|
|
if (NM_IS_VPN_CONNECTION (active)) {
|
|
g_signal_connect (active, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED,
|
|
G_CALLBACK (vpn_connection_state_changed),
|
|
policy);
|
|
}
|
|
|
|
g_signal_connect (active, "notify::" NM_ACTIVE_CONNECTION_STATE,
|
|
G_CALLBACK (active_connection_state_changed),
|
|
policy);
|
|
}
|
|
|
|
static void
|
|
active_connection_removed (NMManager *manager,
|
|
NMActiveConnection *active,
|
|
gpointer user_data)
|
|
{
|
|
NMPolicy *policy = NM_POLICY (user_data);
|
|
|
|
g_signal_handlers_disconnect_by_func (active,
|
|
vpn_connection_state_changed,
|
|
policy);
|
|
g_signal_handlers_disconnect_by_func (active,
|
|
active_connection_state_changed,
|
|
policy);
|
|
}
|
|
|
|
/**************************************************************************/
|
|
|
|
static void
|
|
schedule_activate_all (NMPolicy *policy)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
GSList *iter, *devices;
|
|
|
|
devices = nm_manager_get_devices (priv->manager);
|
|
for (iter = devices; iter; iter = g_slist_next (iter))
|
|
schedule_activate_check (policy, NM_DEVICE (iter->data), 0);
|
|
}
|
|
|
|
static void
|
|
connection_added (NMSettings *settings,
|
|
NMConnection *connection,
|
|
gpointer user_data)
|
|
{
|
|
set_connection_auto_retries (connection, RETRIES_DEFAULT);
|
|
schedule_activate_all ((NMPolicy *) user_data);
|
|
}
|
|
|
|
static void
|
|
connections_loaded (NMSettings *settings, gpointer user_data)
|
|
{
|
|
// FIXME: "connections-loaded" signal is emmitted *before* we connect to it
|
|
// in nm_policy_new(). So this function is never called. Currently we work around
|
|
// that by calling reset_retries_all() in nm_policy_new()
|
|
|
|
/* Initialize connections' auto-retries */
|
|
reset_retries_all (settings, NULL);
|
|
|
|
schedule_activate_all ((NMPolicy *) user_data);
|
|
}
|
|
|
|
static void
|
|
add_or_change_zone_cb (GError *error, gpointer user_data)
|
|
{
|
|
NMDevice *device = NM_DEVICE (user_data);
|
|
|
|
if (error) {
|
|
/* FIXME: what do we do here? */
|
|
}
|
|
|
|
g_object_unref (device);
|
|
}
|
|
|
|
static void
|
|
firewall_update_zone (NMPolicy *policy, NMConnection *connection)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
NMSettingConnection *s_con = nm_connection_get_setting_connection (connection);
|
|
GSList *iter, *devices;
|
|
|
|
devices = nm_manager_get_devices (priv->manager);
|
|
/* find dev with passed connection and change zone its interface belongs to */
|
|
for (iter = devices; iter; iter = g_slist_next (iter)) {
|
|
NMDevice *dev = NM_DEVICE (iter->data);
|
|
|
|
if ( (nm_device_get_connection (dev) == connection)
|
|
&& (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED)) {
|
|
nm_firewall_manager_add_or_change_zone (priv->fw_manager,
|
|
nm_device_get_ip_iface (dev),
|
|
nm_setting_connection_get_zone (s_con),
|
|
FALSE, /* change zone */
|
|
add_or_change_zone_cb,
|
|
g_object_ref (dev));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
firewall_started (NMFirewallManager *manager,
|
|
gpointer user_data)
|
|
{
|
|
NMPolicy *policy = (NMPolicy *) user_data;
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
NMConnection *connection;
|
|
NMSettingConnection *s_con;
|
|
GSList *iter, *devices;
|
|
|
|
devices = nm_manager_get_devices (priv->manager);
|
|
/* add interface of each device to correct zone */
|
|
for (iter = devices; iter; iter = g_slist_next (iter)) {
|
|
NMDevice *dev = NM_DEVICE (iter->data);
|
|
|
|
connection = nm_device_get_connection (dev);
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED) {
|
|
nm_firewall_manager_add_or_change_zone (priv->fw_manager,
|
|
nm_device_get_ip_iface (dev),
|
|
nm_setting_connection_get_zone (s_con),
|
|
TRUE, /* add zone */
|
|
add_or_change_zone_cb,
|
|
g_object_ref (dev));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
dns_config_changed (NMDnsManager *dns_manager, gpointer user_data)
|
|
{
|
|
NMPolicy *policy = (NMPolicy *) user_data;
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
|
|
/* Restart a thread for reverse-DNS lookup after we are signalled that
|
|
* DNS changed. Because the result from a previous run may not be right
|
|
* (race in updating DNS and doing the reverse lookup).
|
|
*/
|
|
|
|
/* Stop a lookup thread if any. */
|
|
if (priv->lookup_cancellable) {
|
|
g_cancellable_cancel (priv->lookup_cancellable);
|
|
g_clear_object (&priv->lookup_cancellable);
|
|
}
|
|
|
|
/* Re-start the hostname lookup thread if we don't have hostname yet. */
|
|
if (priv->lookup_addr) {
|
|
char *str = g_inet_address_to_string (priv->lookup_addr);
|
|
|
|
nm_log_dbg (LOGD_DNS, "restarting reverse-lookup thread for address %s", str);
|
|
g_free (str);
|
|
|
|
priv->lookup_cancellable = g_cancellable_new ();
|
|
g_resolver_lookup_by_address_async (priv->resolver,
|
|
priv->lookup_addr,
|
|
priv->lookup_cancellable,
|
|
lookup_callback, policy);
|
|
}
|
|
}
|
|
|
|
static void
|
|
connection_updated (NMSettings *settings,
|
|
NMConnection *connection,
|
|
gpointer user_data)
|
|
{
|
|
NMPolicy *policy = (NMPolicy *) user_data;
|
|
|
|
firewall_update_zone (policy, connection);
|
|
|
|
/* FIXME: previously, we set_connection_auto_retries() again to
|
|
* RETRIES_DEFAULT, in order to re-enable a connection when it is changed
|
|
* by the user. This caused a serious problem, so remove it for now
|
|
* (rh #1040528).
|
|
**/
|
|
|
|
schedule_activate_all (policy);
|
|
}
|
|
|
|
static void
|
|
_deactivate_if_active (NMManager *manager, NMConnection *connection)
|
|
{
|
|
const GSList *active, *iter;
|
|
|
|
active = nm_manager_get_active_connections (manager);
|
|
for (iter = active; iter; iter = g_slist_next (iter)) {
|
|
NMActiveConnection *ac = iter->data;
|
|
NMActiveConnectionState state = nm_active_connection_get_state (ac);
|
|
GError *error = NULL;
|
|
|
|
if (nm_active_connection_get_connection (ac) == connection &&
|
|
(state <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) {
|
|
if (!nm_manager_deactivate_connection (manager,
|
|
nm_active_connection_get_path (ac),
|
|
NM_DEVICE_STATE_REASON_CONNECTION_REMOVED,
|
|
&error)) {
|
|
nm_log_warn (LOGD_DEVICE, "Connection '%s' disappeared, but error deactivating it: (%d) %s",
|
|
nm_connection_get_id (connection),
|
|
error ? error->code : -1,
|
|
error ? error->message : "(unknown)");
|
|
g_clear_error (&error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
connection_removed (NMSettings *settings,
|
|
NMConnection *connection,
|
|
gpointer user_data)
|
|
{
|
|
NMPolicy *policy = user_data;
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
|
|
_deactivate_if_active (priv->manager, connection);
|
|
}
|
|
|
|
static void
|
|
connection_visibility_changed (NMSettings *settings,
|
|
NMSettingsConnection *connection,
|
|
gpointer user_data)
|
|
{
|
|
NMPolicy *policy = user_data;
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
|
|
if (nm_settings_connection_is_visible (connection))
|
|
schedule_activate_all (policy);
|
|
else
|
|
_deactivate_if_active (priv->manager, NM_CONNECTION (connection));
|
|
}
|
|
|
|
static void
|
|
secret_agent_registered (NMSettings *settings,
|
|
NMSecretAgent *agent,
|
|
gpointer user_data)
|
|
{
|
|
/* The registered secret agent may provide some missing secrets. Thus we
|
|
* reset retries count here and schedule activation, so that the
|
|
* connections failed due to missing secrets may re-try auto-connection.
|
|
*/
|
|
reset_retries_for_failed_secrets (settings);
|
|
schedule_activate_all ((NMPolicy *) user_data);
|
|
}
|
|
|
|
static void
|
|
_connect_manager_signal (NMPolicy *policy, const char *name, gpointer callback)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
guint id;
|
|
|
|
id = g_signal_connect (priv->manager, name, callback, policy);
|
|
priv->manager_ids = g_slist_prepend (priv->manager_ids, GUINT_TO_POINTER (id));
|
|
}
|
|
|
|
static void
|
|
_connect_settings_signal (NMPolicy *policy, const char *name, gpointer callback)
|
|
{
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
guint id;
|
|
|
|
id = g_signal_connect (priv->settings, name, callback, policy);
|
|
priv->settings_ids = g_slist_prepend (priv->settings_ids, GUINT_TO_POINTER (id));
|
|
}
|
|
|
|
NMPolicy *
|
|
nm_policy_new (NMManager *manager, NMSettings *settings)
|
|
{
|
|
NMPolicy *policy;
|
|
NMPolicyPrivate *priv;
|
|
static gboolean initialized = FALSE;
|
|
gulong id;
|
|
char hostname[HOST_NAME_MAX + 2];
|
|
|
|
g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
|
|
g_return_val_if_fail (initialized == FALSE, NULL);
|
|
|
|
policy = g_object_new (NM_TYPE_POLICY, NULL);
|
|
priv = NM_POLICY_GET_PRIVATE (policy);
|
|
priv->manager = manager;
|
|
priv->settings = g_object_ref (settings);
|
|
priv->update_state_id = 0;
|
|
|
|
/* Grab hostname on startup and use that if nothing provides one */
|
|
memset (hostname, 0, sizeof (hostname));
|
|
if (gethostname (&hostname[0], HOST_NAME_MAX) == 0) {
|
|
/* only cache it if it's a valid hostname */
|
|
if ( strlen (hostname)
|
|
&& strcmp (hostname, "localhost")
|
|
&& strcmp (hostname, "localhost.localdomain")
|
|
&& strcmp (hostname, "(none)"))
|
|
priv->orig_hostname = g_strdup (hostname);
|
|
}
|
|
|
|
priv->fw_manager = nm_firewall_manager_get();
|
|
id = g_signal_connect (priv->fw_manager, "started",
|
|
G_CALLBACK (firewall_started), policy);
|
|
priv->fw_started_id = id;
|
|
|
|
priv->dns_manager = nm_dns_manager_get ();
|
|
nm_dns_manager_set_initial_hostname (priv->dns_manager, priv->orig_hostname);
|
|
priv->config_changed_id = g_signal_connect (priv->dns_manager, "config-changed",
|
|
G_CALLBACK (dns_config_changed), policy);
|
|
|
|
priv->resolver = g_resolver_get_default ();
|
|
|
|
_connect_manager_signal (policy, "state-changed", global_state_changed);
|
|
_connect_manager_signal (policy, "notify::" NM_MANAGER_HOSTNAME, hostname_changed);
|
|
_connect_manager_signal (policy, "notify::" NM_MANAGER_SLEEPING, sleeping_changed);
|
|
_connect_manager_signal (policy, "notify::" NM_MANAGER_NETWORKING_ENABLED, sleeping_changed);
|
|
_connect_manager_signal (policy, "device-added", device_added);
|
|
_connect_manager_signal (policy, "device-removed", device_removed);
|
|
_connect_manager_signal (policy, NM_MANAGER_ACTIVE_CONNECTION_ADDED, active_connection_added);
|
|
_connect_manager_signal (policy, NM_MANAGER_ACTIVE_CONNECTION_REMOVED, active_connection_removed);
|
|
|
|
_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_CONNECTIONS_LOADED, connections_loaded);
|
|
_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, connection_added);
|
|
_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, connection_updated);
|
|
_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, connection_removed);
|
|
_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED,
|
|
connection_visibility_changed);
|
|
_connect_settings_signal (policy, NM_SETTINGS_SIGNAL_AGENT_REGISTERED, secret_agent_registered);
|
|
|
|
/* Initialize connections' auto-retries */
|
|
reset_retries_all (priv->settings, NULL);
|
|
|
|
initialized = TRUE;
|
|
return policy;
|
|
}
|
|
|
|
NMDevice *
|
|
nm_policy_get_default_ip4_device (NMPolicy *policy)
|
|
{
|
|
return NM_POLICY_GET_PRIVATE (policy)->default_device4;
|
|
}
|
|
|
|
NMDevice *
|
|
nm_policy_get_default_ip6_device (NMPolicy *policy)
|
|
{
|
|
return NM_POLICY_GET_PRIVATE (policy)->default_device6;
|
|
}
|
|
|
|
NMDevice *
|
|
nm_policy_get_activating_ip4_device (NMPolicy *policy)
|
|
{
|
|
return NM_POLICY_GET_PRIVATE (policy)->activating_device4;
|
|
}
|
|
|
|
NMDevice *
|
|
nm_policy_get_activating_ip6_device (NMPolicy *policy)
|
|
{
|
|
return NM_POLICY_GET_PRIVATE (policy)->activating_device6;
|
|
}
|
|
|
|
static void
|
|
nm_policy_init (NMPolicy *policy)
|
|
{
|
|
}
|
|
|
|
static void
|
|
get_property (GObject *object, guint prop_id,
|
|
GValue *value, GParamSpec *pspec)
|
|
{
|
|
NMPolicy *policy = NM_POLICY (object);
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
|
|
switch (prop_id) {
|
|
case PROP_DEFAULT_IP4_DEVICE:
|
|
g_value_set_object (value, priv->default_device4);
|
|
break;
|
|
case PROP_DEFAULT_IP6_DEVICE:
|
|
g_value_set_object (value, priv->default_device6);
|
|
break;
|
|
case PROP_ACTIVATING_IP4_DEVICE:
|
|
g_value_set_object (value, priv->activating_device4);
|
|
break;
|
|
case PROP_ACTIVATING_IP6_DEVICE:
|
|
g_value_set_object (value, priv->activating_device6);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dispose (GObject *object)
|
|
{
|
|
NMPolicy *policy = NM_POLICY (object);
|
|
NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (policy);
|
|
const GSList *connections, *iter;
|
|
|
|
/* Tell any existing hostname lookup thread to die. */
|
|
if (priv->lookup_cancellable) {
|
|
g_cancellable_cancel (priv->lookup_cancellable);
|
|
g_clear_object (&priv->lookup_cancellable);
|
|
}
|
|
g_clear_object (&priv->lookup_addr);
|
|
g_clear_object (&priv->resolver);
|
|
|
|
g_slist_free_full (priv->pending_activation_checks, (GDestroyNotify) activate_data_free);
|
|
priv->pending_activation_checks = NULL;
|
|
|
|
g_slist_free_full (priv->pending_secondaries, (GDestroyNotify) pending_secondary_data_free);
|
|
priv->pending_secondaries = NULL;
|
|
|
|
if (priv->fw_manager) {
|
|
g_signal_handler_disconnect (priv->fw_manager, priv->fw_started_id);
|
|
g_object_unref (priv->fw_manager);
|
|
priv->fw_manager = NULL;
|
|
}
|
|
|
|
if (priv->dns_manager) {
|
|
g_signal_handler_disconnect (priv->dns_manager, priv->config_changed_id);
|
|
priv->dns_manager = NULL;
|
|
}
|
|
|
|
for (iter = priv->manager_ids; iter; iter = g_slist_next (iter))
|
|
g_signal_handler_disconnect (priv->manager, GPOINTER_TO_UINT (iter->data));
|
|
g_clear_pointer (&priv->manager_ids, g_slist_free);
|
|
|
|
for (iter = priv->settings_ids; iter; iter = g_slist_next (iter))
|
|
g_signal_handler_disconnect (priv->settings, GPOINTER_TO_UINT (iter->data));
|
|
g_clear_pointer (&priv->settings_ids, g_slist_free);
|
|
|
|
for (iter = priv->dev_ids; iter; iter = g_slist_next (iter)) {
|
|
DeviceSignalId *data = iter->data;
|
|
|
|
g_signal_handler_disconnect (data->device, data->id);
|
|
g_slice_free (DeviceSignalId, data);
|
|
}
|
|
g_clear_pointer (&priv->dev_ids, g_slist_free);
|
|
|
|
/* The manager should have disposed of ActiveConnections already, which
|
|
* will have called active_connection_removed() and thus we don't need
|
|
* to clean anything up. Assert that this is TRUE.
|
|
*/
|
|
connections = nm_manager_get_active_connections (priv->manager);
|
|
g_assert (connections == NULL);
|
|
|
|
if (priv->reset_retries_id) {
|
|
g_source_remove (priv->reset_retries_id);
|
|
priv->reset_retries_id = 0;
|
|
}
|
|
|
|
g_clear_pointer (&priv->orig_hostname, g_free);
|
|
g_clear_pointer (&priv->cur_hostname, g_free);
|
|
|
|
g_clear_object (&priv->settings);
|
|
|
|
G_OBJECT_CLASS (nm_policy_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
nm_policy_class_init (NMPolicyClass *policy_class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (policy_class);
|
|
|
|
g_type_class_add_private (policy_class, sizeof (NMPolicyPrivate));
|
|
|
|
object_class->get_property = get_property;
|
|
object_class->dispose = dispose;
|
|
|
|
g_object_class_install_property
|
|
(object_class, PROP_DEFAULT_IP4_DEVICE,
|
|
g_param_spec_object (NM_POLICY_DEFAULT_IP4_DEVICE,
|
|
"Default IP4 device",
|
|
"Default IP4 device",
|
|
NM_TYPE_DEVICE,
|
|
G_PARAM_READABLE));
|
|
g_object_class_install_property
|
|
(object_class, PROP_DEFAULT_IP6_DEVICE,
|
|
g_param_spec_object (NM_POLICY_DEFAULT_IP6_DEVICE,
|
|
"Default IP6 device",
|
|
"Default IP6 device",
|
|
NM_TYPE_DEVICE,
|
|
G_PARAM_READABLE));
|
|
g_object_class_install_property
|
|
(object_class, PROP_ACTIVATING_IP4_DEVICE,
|
|
g_param_spec_object (NM_POLICY_ACTIVATING_IP4_DEVICE,
|
|
"Activating default IP4 device",
|
|
"Activating default IP4 device",
|
|
NM_TYPE_DEVICE,
|
|
G_PARAM_READABLE));
|
|
g_object_class_install_property
|
|
(object_class, PROP_ACTIVATING_IP6_DEVICE,
|
|
g_param_spec_object (NM_POLICY_ACTIVATING_IP6_DEVICE,
|
|
"Activating default IP6 device",
|
|
"Activating default IP6 device",
|
|
NM_TYPE_DEVICE,
|
|
G_PARAM_READABLE));
|
|
}
|