diff options
Diffstat (limited to 'src/nm-device.c')
| -rw-r--r-- | src/nm-device.c | 5706 |
1 files changed, 0 insertions, 5706 deletions
diff --git a/src/nm-device.c b/src/nm-device.c deleted file mode 100644 index a07ef338..00000000 --- a/src/nm-device.c +++ /dev/null @@ -1,5706 +0,0 @@ -/* -*- 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) 2005 - 2012 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#include <config.h> -#include <glib.h> -#include <glib/gi18n.h> -#include <dbus/dbus.h> -#include <netinet/in.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> -#include <linux/sockios.h> -#include <linux/ethtool.h> -#include <sys/ioctl.h> -#include <signal.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <arpa/inet.h> -#include <fcntl.h> -#include <linux/if.h> - -#include "nm-glib-compat.h" -#include "nm-device.h" -#include "nm-device-private.h" -#include "nm-device-ethernet.h" -#include "NetworkManagerUtils.h" -#include "nm-system.h" -#include "nm-dhcp-manager.h" -#include "nm-dbus-manager.h" -#include "nm-utils.h" -#include "nm-logging.h" -#include "nm-netlink-monitor.h" -#include "nm-netlink-utils.h" -#include "nm-netlink-compat.h" -#include "nm-setting-ip4-config.h" -#include "nm-setting-ip6-config.h" -#include "nm-setting-connection.h" -#include "nm-dnsmasq-manager.h" -#include "nm-dhcp4-config.h" -#include "nm-ip6-manager.h" -#include "nm-marshal.h" -#include "nm-rfkill.h" -#include "nm-firewall-manager.h" -#include "nm-properties-changed-signal.h" -#include "nm-enum-types.h" -#include "nm-settings-connection.h" -#include "nm-connection-provider.h" -#include "nm-posix-signals.h" -#include "nm-manager-auth.h" -#include "nm-dbus-glib-types.h" -#include "nm-dispatcher.h" - -static void impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context); - -#include "nm-device-interface-glue.h" - -#define PENDING_IP4_CONFIG "pending-ip4-config" -#define PENDING_IP6_CONFIG "pending-ip6-config" - -#define DBUS_G_TYPE_UINT_STRUCT (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID)) - -/***********************************************************/ -#define NM_DEVICE_ERROR (nm_device_error_quark ()) - -static GQuark -nm_device_error_quark (void) -{ - static GQuark quark = 0; - if (!quark) - quark = g_quark_from_static_string ("nm-device-error"); - return quark; -} - -/***********************************************************/ - -enum { - STATE_CHANGED, - AUTOCONNECT_ALLOWED, - AUTH_REQUEST, - IP4_CONFIG_CHANGED, - IP6_CONFIG_CHANGED, - LAST_SIGNAL, -}; -static guint signals[LAST_SIGNAL] = { 0 }; - -enum { - PROP_0, - PROP_UDI, - PROP_IFACE, - PROP_IP_IFACE, - PROP_DRIVER, - PROP_DRIVER_VERSION, - PROP_FIRMWARE_VERSION, - PROP_CAPABILITIES, - PROP_IP4_ADDRESS, - PROP_IP4_CONFIG, - PROP_DHCP4_CONFIG, - PROP_IP6_CONFIG, - PROP_DHCP6_CONFIG, - PROP_STATE, - PROP_STATE_REASON, - PROP_ACTIVE_CONNECTION, - PROP_DEVICE_TYPE, - PROP_MANAGED, - PROP_AUTOCONNECT, - PROP_FIRMWARE_MISSING, - PROP_TYPE_DESC, - PROP_RFKILL_TYPE, - PROP_IFINDEX, - PROP_AVAILABLE_CONNECTIONS, - PROP_IS_MASTER, - LAST_PROP -}; - -#define DEFAULT_AUTOCONNECT TRUE - -/***********************************************************/ - -G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, G_TYPE_OBJECT) - -#define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate)) - -typedef enum { - IP_NONE = 0, - IP_WAIT, - IP_CONF, - IP_DONE -} IpState; - -typedef struct { - NMDeviceState state; - NMDeviceStateReason reason; - guint id; -} QueuedState; - -typedef struct { - NMDevice *slave; - gboolean enslaved; - guint watch_id; -} SlaveInfo; - -typedef struct { - gboolean disposed; - gboolean initialized; - - NMDeviceState state; - NMDeviceStateReason state_reason; - QueuedState queued_state; - - char * udi; - char * path; - char * iface; /* may change, could be renamed by user */ - int ifindex; - char * ip_iface; - int ip_ifindex; - NMDeviceType type; - char * type_desc; - guint32 capabilities; - char * driver; - char * driver_version; - char * firmware_version; - gboolean managed; /* whether managed by NM or not */ - RfKillType rfkill_type; - gboolean firmware_missing; - GHashTable * available_connections; - - guint32 ip4_address; - - NMActRequest * act_request; - guint act_source_id; - gpointer act_source_func; - guint act_source6_id; - gpointer act_source6_func; - gulong secrets_updated_id; - gulong secrets_failed_id; - - /* Generic DHCP stuff */ - NMDHCPManager * dhcp_manager; - guint32 dhcp_timeout; - GByteArray * dhcp_anycast_address; - - /* IP4 configuration info */ - NMIP4Config * ip4_config; /* Config from DHCP, PPP, or system config files */ - IpState ip4_state; - NMDHCPClient * dhcp4_client; - gulong dhcp4_state_sigid; - gulong dhcp4_timeout_sigid; - NMDHCP4Config * dhcp4_config; - - /* dnsmasq stuff for shared connections */ - NMDnsMasqManager *dnsmasq_manager; - gulong dnsmasq_state_id; - - /* Firewall Manager */ - NMFirewallManager *fw_manager; - DBusGProxyCall *fw_call; - - /* avahi-autoipd stuff */ - GPid aipd_pid; - guint aipd_watch; - guint aipd_timeout; - - /* IP6 configuration info */ - NMIP6Config * ip6_config; - IpState ip6_state; - - NMIP6Manager * ip6_manager; - gulong ip6_addrconf_sigid; - gulong ip6_config_changed_sigid; - gboolean ip6_waiting_for_config; - /* IP6 config from autoconf */ - NMIP6Config * ac_ip6_config; - - char * ip6_accept_ra_path; - gint32 ip6_accept_ra_save; - - /* IPv6 privacy extensions (RFC4941) */ - char * ip6_privacy_tempaddr_path; - gint32 ip6_privacy_tempaddr_save; - - NMDHCPClient * dhcp6_client; - guint32 dhcp6_mode; - gulong dhcp6_state_sigid; - gulong dhcp6_timeout_sigid; - NMDHCP6Config * dhcp6_config; - /* IP6 config from DHCP */ - NMIP6Config * dhcp6_ip6_config; - - /* allow autoconnect feature */ - gboolean autoconnect; - - /* master interface for bridge/bond slave */ - NMDevice * master; - gboolean enslaved; - - /* slave management */ - gboolean is_master; - GSList * slaves; /* list of SlaveInfo */ - - NMConnectionProvider *con_provider; - - /* connection provider signals for available connections property */ - guint cp_added_id; - guint cp_loaded_id; - guint cp_removed_id; - guint cp_updated_id; -} NMDevicePrivate; - -static void nm_device_take_down (NMDevice *dev, gboolean wait, NMDeviceStateReason reason); - -static gboolean nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware); -static gboolean nm_device_is_up (NMDevice *self); - -static gboolean nm_device_set_ip4_config (NMDevice *dev, - NMIP4Config *config, - gboolean assumed, - NMDeviceStateReason *reason); -static gboolean nm_device_set_ip6_config (NMDevice *dev, - NMIP6Config *config, - NMDeviceStateReason *reason); - -static gboolean nm_device_activate_ip6_config_commit (gpointer user_data); - -static gboolean check_connection_available (NMDevice *device, NMConnection *connection); - -static void _clear_available_connections (NMDevice *device, gboolean do_signal); - -static void dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release); - -static const char *reason_to_string (NMDeviceStateReason reason); - -static void cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data); -static void cp_connections_loaded (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data); -static void cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data); -static void cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data); - -static const char *state_to_string (NMDeviceState state); - -static void -nm_device_init (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - priv->type = NM_DEVICE_TYPE_UNKNOWN; - priv->capabilities = NM_DEVICE_CAP_NONE; - priv->state = NM_DEVICE_STATE_UNMANAGED; - priv->state_reason = NM_DEVICE_STATE_REASON_NONE; - priv->dhcp_timeout = 0; - priv->rfkill_type = RFKILL_TYPE_UNKNOWN; - priv->autoconnect = DEFAULT_AUTOCONNECT; - priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); -} - -static void -update_accept_ra_save (NMDevice *self) -{ - NMDevicePrivate *priv; - const char *ip_iface; - char *new_path; - - g_return_if_fail (self != NULL); - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - ip_iface = nm_device_get_ip_iface (self); - - new_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/accept_ra", ip_iface); - g_assert (new_path); - - if (priv->ip6_accept_ra_path) { - /* If the IP iface is different from before, use the new value */ - if (!strcmp (new_path, priv->ip6_accept_ra_path)) { - g_free (new_path); - return; - } - g_free (priv->ip6_accept_ra_path); - } - - /* Grab the original value of "accept_ra" so we can restore it when NM exits */ - priv->ip6_accept_ra_path = new_path; - if (!nm_utils_get_proc_sys_net_value_with_bounds (priv->ip6_accept_ra_path, - ip_iface, - &priv->ip6_accept_ra_save, - 0, 2)) { - g_free (priv->ip6_accept_ra_path); - priv->ip6_accept_ra_path = NULL; - } -} - -static void -update_ip6_privacy_save (NMDevice *self) -{ - NMDevicePrivate *priv; - const char *ip_iface; - char *new_path; - - g_return_if_fail (self != NULL); - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - ip_iface = nm_device_get_ip_iface (self); - - new_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/use_tempaddr", ip_iface); - g_assert (new_path); - - if (priv->ip6_privacy_tempaddr_path) { - /* If the IP iface is different from before, use the new value */ - if (!strcmp (new_path, priv->ip6_privacy_tempaddr_path)) { - g_free (new_path); - return; - } - g_free (priv->ip6_privacy_tempaddr_path); - } - - /* Grab the original value of "use_tempaddr" so we can restore it when NM exits */ - priv->ip6_privacy_tempaddr_path = new_path; - if (!nm_utils_get_proc_sys_net_value (priv->ip6_privacy_tempaddr_path, - ip_iface, - &priv->ip6_privacy_tempaddr_save)) { - g_free (priv->ip6_privacy_tempaddr_path); - priv->ip6_privacy_tempaddr_path = NULL; - } -} - -/* - * Get driver info from SIOCETHTOOL ioctl() for 'iface' - * Returns driver and firmware versions to 'driver_version and' 'firmware_version' - */ -static gboolean -device_get_driver_info (const char *iface, char **driver_version, char **firmware_version) -{ - struct ethtool_drvinfo drvinfo; - struct ifreq req; - int fd; - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - nm_log_warn (LOGD_HW, "couldn't open control socket."); - return FALSE; - } - - /* Get driver and firmware version info */ - memset (&drvinfo, 0, sizeof (drvinfo)); - memset (&req, 0, sizeof (struct ifreq)); - strncpy (req.ifr_name, iface, IFNAMSIZ); - drvinfo.cmd = ETHTOOL_GDRVINFO; - req.ifr_data = &drvinfo; - - errno = 0; - if (ioctl (fd, SIOCETHTOOL, &req) < 0) { - nm_log_dbg (LOGD_HW, "SIOCETHTOOL ioctl() failed: cmd=ETHTOOL_GDRVINFO, iface=%s, errno=%d", - iface, errno); - close (fd); - return FALSE; - } - if (driver_version) - *driver_version = g_strdup (drvinfo.version); - if (firmware_version) - *firmware_version = g_strdup (drvinfo.fw_version); - - close (fd); - return TRUE; -} - -static GObject* -constructor (GType type, - guint n_construct_params, - GObjectConstructParam *construct_params) -{ - GObject *object; - NMDevice *dev; - NMDevicePrivate *priv; - - object = G_OBJECT_CLASS (nm_device_parent_class)->constructor (type, - n_construct_params, - construct_params); - if (!object) - return NULL; - - dev = NM_DEVICE (object); - priv = NM_DEVICE_GET_PRIVATE (dev); - - if (!priv->udi) { - nm_log_err (LOGD_DEVICE, "No device udi provided, ignoring"); - goto error; - } - - if (!priv->iface) { - nm_log_err (LOGD_DEVICE, "No device interface provided, ignoring"); - goto error; - } - - priv->capabilities |= NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities (dev); - if (!(priv->capabilities & NM_DEVICE_CAP_NM_SUPPORTED)) { - nm_log_warn (LOGD_DEVICE, "(%s): Device unsupported, ignoring.", priv->iface); - goto error; - } - - priv->dhcp_manager = nm_dhcp_manager_get (); - - priv->fw_manager = nm_firewall_manager_get (); - - device_get_driver_info (priv->iface, &priv->driver_version, &priv->firmware_version); - - update_accept_ra_save (dev); - update_ip6_privacy_save (dev); - - priv->initialized = TRUE; - return object; - -error: - g_object_unref (dev); - return NULL; -} - -static void -constructed (GObject *object) -{ - NMDevice *dev = NM_DEVICE (object); - - if (NM_DEVICE_GET_CLASS (dev)->update_hw_address) - NM_DEVICE_GET_CLASS (dev)->update_hw_address (dev); - - if (NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address) - NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address (dev); - - if (NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address) - NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address (dev); - - if (G_OBJECT_CLASS (nm_device_parent_class)->constructed) - G_OBJECT_CLASS (nm_device_parent_class)->constructed (object); -} - -static gboolean -nm_device_hw_is_up (NMDevice *self) -{ - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - - if (NM_DEVICE_GET_CLASS (self)->hw_is_up) - return NM_DEVICE_GET_CLASS (self)->hw_is_up (self); - - return TRUE; -} - -static gboolean -hw_is_up (NMDevice *device) -{ - int ifindex = nm_device_get_ip_ifindex (device); - - return ifindex > 0 ? nm_system_iface_is_up (ifindex) : TRUE; -} - -static guint32 -get_generic_capabilities (NMDevice *dev) -{ - return 0; -} - -void -nm_device_set_path (NMDevice *self, const char *path) -{ - NMDevicePrivate *priv; - - g_return_if_fail (self != NULL); - - priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->path == NULL); - - priv->path = g_strdup (path); -} - -const char * -nm_device_get_path (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NULL); - - return NM_DEVICE_GET_PRIVATE (self)->path; -} - -const char * -nm_device_get_udi (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NULL); - - return NM_DEVICE_GET_PRIVATE (self)->udi; -} - -/* - * Get/set functions for iface - */ -const char * -nm_device_get_iface (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NULL); - - return NM_DEVICE_GET_PRIVATE (self)->iface; -} - -int -nm_device_get_ifindex (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, 0); - - return NM_DEVICE_GET_PRIVATE (self)->ifindex; -} - -const char * -nm_device_get_ip_iface (NMDevice *self) -{ - NMDevicePrivate *priv; - - g_return_val_if_fail (self != NULL, NULL); - - priv = NM_DEVICE_GET_PRIVATE (self); - /* If it's not set, default to iface */ - return priv->ip_iface ? priv->ip_iface : priv->iface; -} - -int -nm_device_get_ip_ifindex (NMDevice *self) -{ - NMDevicePrivate *priv; - - g_return_val_if_fail (self != NULL, 0); - - priv = NM_DEVICE_GET_PRIVATE (self); - /* If it's not set, default to iface */ - return priv->ip_iface ? priv->ip_ifindex : priv->ifindex; -} - -void -nm_device_set_ip_iface (NMDevice *self, const char *iface) -{ - NMDevicePrivate *priv; - char *old_ip_iface; - - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - old_ip_iface = priv->ip_iface; - priv->ip_ifindex = 0; - - priv->ip_iface = g_strdup (iface); - if (priv->ip_iface) { - priv->ip_ifindex = nm_netlink_iface_to_index (priv->ip_iface); - if (priv->ip_ifindex <= 0) { - /* Device IP interface must always be a kernel network interface */ - nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", iface); - } - } - - /* Emit change notification */ - if (g_strcmp0 (old_ip_iface, priv->ip_iface)) - g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE); - g_free (old_ip_iface); -} - -const guint8 * -nm_device_get_hw_address (NMDevice *dev, guint *out_len) -{ - g_return_val_if_fail (dev != NULL, NULL); - g_return_val_if_fail (NM_IS_DEVICE (dev), NULL); - g_return_val_if_fail (out_len != NULL, NULL); - g_return_val_if_fail (*out_len == 0, NULL); - - if (NM_DEVICE_GET_CLASS (dev)->get_hw_address) - return NM_DEVICE_GET_CLASS (dev)->get_hw_address (dev, out_len); - return NULL; -} - -/* - * Get/set functions for driver - */ -const char * -nm_device_get_driver (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NULL); - - return NM_DEVICE_GET_PRIVATE (self)->driver; -} - -const char * -nm_device_get_driver_version (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NULL); - - return NM_DEVICE_GET_PRIVATE (self)->driver_version; -} - -const char * -nm_device_get_firmware_version (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NULL); - - return NM_DEVICE_GET_PRIVATE (self)->firmware_version; -} - - -/* - * Get/set functions for type - */ -NMDeviceType -nm_device_get_device_type (NMDevice *self) -{ - g_return_val_if_fail (NM_IS_DEVICE (self), NM_DEVICE_TYPE_UNKNOWN); - - return NM_DEVICE_GET_PRIVATE (self)->type; -} - - -/** - * nm_device_get_priority(): - * @dev: the #NMDevice - * - * Returns: the device's routing priority. Lower numbers means a "better" - * device, eg higher priority. - */ -int -nm_device_get_priority (NMDevice *dev) -{ - g_return_val_if_fail (NM_IS_DEVICE (dev), 100); - - /* Device 'priority' is used for two things: - * - * a) two devices on the same IP subnet: the "better" (ie, lower number) - * device is the default outgoing device for that subnet - * b) default route: the "better" device gets the default route. This can - * always be modified by setting a connection to never-default=TRUE, in - * which case that device will never take the default route when - * it's using that connection. - */ - - switch (nm_device_get_device_type (dev)) { - case NM_DEVICE_TYPE_ETHERNET: - return 1; - case NM_DEVICE_TYPE_INFINIBAND: - return 2; - case NM_DEVICE_TYPE_ADSL: - return 3; - case NM_DEVICE_TYPE_WIMAX: - return 4; - case NM_DEVICE_TYPE_BOND: - return 5; - case NM_DEVICE_TYPE_VLAN: - return 6; - case NM_DEVICE_TYPE_MODEM: - return 7; - case NM_DEVICE_TYPE_BT: - return 8; - case NM_DEVICE_TYPE_WIFI: - return 9; - case NM_DEVICE_TYPE_OLPC_MESH: - return 10; - default: - return 20; - } -} - - -/* - * Accessor for capabilities - */ -guint32 -nm_device_get_capabilities (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NM_DEVICE_CAP_NONE); - - return NM_DEVICE_GET_PRIVATE (self)->capabilities; -} - -/* - * Accessor for type-specific capabilities - */ -guint32 -nm_device_get_type_capabilities (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NM_DEVICE_CAP_NONE); - - return NM_DEVICE_GET_CLASS (self)->get_type_capabilities (self); -} - -static guint32 -get_type_capabilities (NMDevice *self) -{ - return NM_DEVICE_CAP_NONE; -} - - -const char * -nm_device_get_type_desc (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NULL); - - return NM_DEVICE_GET_PRIVATE (self)->type_desc; -} - -void -nm_device_set_connection_provider (NMDevice *device, - NMConnectionProvider *provider) -{ - NMDevicePrivate *priv; - - g_return_if_fail (device != NULL); - g_return_if_fail (provider != NULL); - g_return_if_fail (NM_IS_CONNECTION_PROVIDER (provider)); - - priv = NM_DEVICE_GET_PRIVATE (device); - g_return_if_fail (priv->con_provider == NULL); - - priv->con_provider = provider; - priv->cp_added_id = g_signal_connect (priv->con_provider, - NM_CP_SIGNAL_CONNECTION_ADDED, - G_CALLBACK (cp_connection_added), - device); - - priv->cp_loaded_id = g_signal_connect (priv->con_provider, - NM_CP_SIGNAL_CONNECTIONS_LOADED, - G_CALLBACK (cp_connections_loaded), - device); - - priv->cp_removed_id = g_signal_connect (priv->con_provider, - NM_CP_SIGNAL_CONNECTION_REMOVED, - G_CALLBACK (cp_connection_removed), - device); - - priv->cp_updated_id = g_signal_connect (priv->con_provider, - NM_CP_SIGNAL_CONNECTION_UPDATED, - G_CALLBACK (cp_connection_updated), - device); -} - -NMConnectionProvider * -nm_device_get_connection_provider (NMDevice *device) -{ - g_return_val_if_fail (device != NULL, NULL); - - return NM_DEVICE_GET_PRIVATE (device)->con_provider; -} - -static SlaveInfo * -find_slave_info (NMDevice *self, NMDevice *slave) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - SlaveInfo *info; - GSList *iter; - - for (iter = priv->slaves; iter; iter = g_slist_next (iter)) { - info = iter->data; - if (info->slave == slave) - return info; - } - return NULL; -} - -static void -free_slave_info (SlaveInfo *info) -{ - g_signal_handler_disconnect (info->slave, info->watch_id); - g_clear_object (&info->slave); - memset (info, 0, sizeof (*info)); - g_free (info); -} - -/** - * nm_device_enslave_slave: - * @dev: the master device - * @slave: the slave device to enslave - * @connection: the slave device's connection - * - * If @dev is capable of enslaving other devices (ie it's a bridge, bond, etc) - * then this function enslaves @slave. - * - * Returns: %TRUE on success, %FALSE on failure or if this device cannot enslave - * other devices. - */ -static gboolean -nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connection) -{ - SlaveInfo *info; - gboolean success = FALSE; - - g_return_val_if_fail (dev != NULL, FALSE); - g_return_val_if_fail (slave != NULL, FALSE); - g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); - g_return_val_if_fail (NM_DEVICE_GET_CLASS (dev)->enslave_slave != NULL, FALSE); - - info = find_slave_info (dev, slave); - if (!info) - return FALSE; - - g_warn_if_fail (info->enslaved == FALSE); - success = NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave, connection); - if (success) { - info->enslaved = TRUE; - nm_device_slave_notify_enslaved (info->slave, TRUE, FALSE); - } - - /* Ensure the device's hardware address is up-to-date; it often changes - * when slaves change. - */ - if (NM_DEVICE_GET_CLASS (dev)->update_hw_address) - NM_DEVICE_GET_CLASS (dev)->update_hw_address (dev); - - /* Restart IP configuration if we're waiting for slaves. Do this - * after updating the hardware address as IP config may need the - * new address. - */ - if (success) { - if (NM_DEVICE_GET_PRIVATE (dev)->ip4_state == IP_WAIT) - nm_device_activate_stage3_ip4_start (dev); - - if (NM_DEVICE_GET_PRIVATE (dev)->ip6_state == IP_WAIT) - nm_device_activate_stage3_ip6_start (dev); - } - - return success; -} - -/** - * nm_device_release_one_slave: - * @dev: the master device - * @slave: the slave device to release - * @failed: %TRUE if the release was unexpected, ie the master failed - * - * If @dev is capable of enslaving other devices (ie it's a bridge, bond, etc) - * then this function releases the previously enslaved @slave. - * - * Returns: %TRUE on success, %FALSE on failure, if this device cannot enslave - * other devices, or if @slave was never enslaved. - */ -static gboolean -nm_device_release_one_slave (NMDevice *dev, NMDevice *slave, gboolean failed) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); - SlaveInfo *info; - gboolean success = FALSE; - - g_return_val_if_fail (slave != NULL, FALSE); - g_return_val_if_fail (NM_DEVICE_GET_CLASS (dev)->release_slave != NULL, FALSE); - - info = find_slave_info (dev, slave); - if (!info) - return FALSE; - - if (info->enslaved) { - success = NM_DEVICE_GET_CLASS (dev)->release_slave (dev, slave); - g_warn_if_fail (success); - } - nm_device_slave_notify_enslaved (info->slave, FALSE, failed); - - priv->slaves = g_slist_remove (priv->slaves, info); - free_slave_info (info); - - /* Ensure the device's hardware address is up-to-date; it often changes - * when slaves change. - */ - if (NM_DEVICE_GET_CLASS (dev)->update_hw_address) - NM_DEVICE_GET_CLASS (dev)->update_hw_address (dev); - - return success; -} - -static void -slave_state_changed (NMDevice *slave, - NMDeviceState slave_new_state, - NMDeviceState slave_old_state, - NMDeviceStateReason reason, - NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean release = FALSE; - - nm_log_dbg (LOGD_DEVICE, "(%s): slave %s state change %d (%s) -> %d (%s)", - nm_device_get_iface (self), - nm_device_get_iface (slave), - slave_old_state, - state_to_string (slave_old_state), - slave_new_state, - state_to_string (slave_new_state)); - - g_assert (priv->state > NM_DEVICE_STATE_DISCONNECTED); - g_assert (priv->state <= NM_DEVICE_STATE_ACTIVATED); - - /* Don't try to enslave slaves until the master is ready */ - if (priv->state < NM_DEVICE_STATE_CONFIG) - return; - - if (slave_new_state == NM_DEVICE_STATE_IP_CONFIG) - nm_device_enslave_slave (self, slave, nm_device_get_connection (slave)); - else if (slave_new_state > NM_DEVICE_STATE_ACTIVATED) - release = TRUE; - else if ( slave_new_state <= NM_DEVICE_STATE_DISCONNECTED - && slave_old_state > NM_DEVICE_STATE_DISCONNECTED) { - /* Catch failures due to unavailable or unmanaged */ - release = TRUE; - } - - if (release) { - nm_device_release_one_slave (self, slave, FALSE); - /* Bridge/bond interfaces are left up until manually deactivated */ - if (priv->slaves == NULL && priv->state == NM_DEVICE_STATE_ACTIVATED) { - nm_log_dbg (LOGD_DEVICE, "(%s): last slave removed; remaining activated", - nm_device_get_iface (self)); - } - } -} - -/** - * nm_device_master_add_slave: - * @dev: the master device - * @slave: the slave device to enslave - * - * If @dev is capable of enslaving other devices (ie it's a bridge, bond, etc) - * then this function adds @slave to the slave list for later enslavement. - * - * Returns: %TRUE on success, %FALSE on failure - */ -gboolean -nm_device_master_add_slave (NMDevice *dev, NMDevice *slave) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); - SlaveInfo *info; - - g_return_val_if_fail (dev != NULL, FALSE); - g_return_val_if_fail (slave != NULL, FALSE); - g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); - g_return_val_if_fail (NM_DEVICE_GET_CLASS (dev)->enslave_slave != NULL, FALSE); - - if (!find_slave_info (dev, slave)) { - info = g_malloc0 (sizeof (SlaveInfo)); - info->slave = g_object_ref (slave); - info->watch_id = g_signal_connect (slave, "state-changed", - G_CALLBACK (slave_state_changed), dev); - priv->slaves = g_slist_prepend (priv->slaves, info); - } - - return TRUE; -} - - -/** - * nm_device_master_get_slaves: - * @dev: the master device - * - * Returns: any slaves of which @device is the master. Caller owns returned list. - */ -GSList * -nm_device_master_get_slaves (NMDevice *dev) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); - GSList *slaves = NULL, *iter; - - for (iter = priv->slaves; iter; iter = g_slist_next (iter)) - slaves = g_slist_prepend (slaves, ((SlaveInfo *) iter->data)->slave); - - return slaves; -} - -/** - * nm_device_master_get_slave_by_ifindex: - * @dev: the master device - * @ifindex: the slave's interface index - * - * Returns: the slave with the given @ifindex of which @device is the master, - * or %NULL if no device with @ifinidex is a slave of @device. - */ -NMDevice * -nm_device_master_get_slave_by_ifindex (NMDevice *dev, int ifindex) -{ - GSList *iter; - - for (iter = NM_DEVICE_GET_PRIVATE (dev)->slaves; iter; iter = g_slist_next (iter)) { - SlaveInfo *info = iter->data; - - if (nm_device_get_ip_ifindex (info->slave) == ifindex) - return info->slave; - } - return NULL; -} - -/** - * nm_device_is_master: - * @dev: the device - * - * Returns: whether @dev can enslave other devices (eg, bridge or bond) - */ -gboolean -nm_device_is_master (NMDevice *dev) -{ - return NM_DEVICE_GET_PRIVATE (dev)->is_master; -} - -/* release all slaves */ -static void -nm_device_master_release_slaves (NMDevice *self, gboolean failed) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - while (priv->slaves) { - SlaveInfo *info = priv->slaves->data; - - nm_device_release_one_slave (self, info->slave, failed); - } -} - - -/** - * nm_device_slave_notify_enslaved: - * @dev: the slave device - * @enslaved: %TRUE if the device is now enslaved, %FALSE if released - * @master_failed: if released, indicates whether the release was unexpected, - * ie the master device failed. - * - * Notifies a slave that it has been enslaved or released. If released, provides - * information on whether the release was expected or not, and thus whether the - * slave should fail it's activation or gracefully deactivate. - */ -void -nm_device_slave_notify_enslaved (NMDevice *dev, - gboolean enslaved, - gboolean master_failed) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); - NMConnection *connection = nm_device_get_connection (dev); - - if (enslaved) { - g_assert (priv->master); - g_warn_if_fail (priv->enslaved == FALSE); - g_warn_if_fail (priv->state == NM_DEVICE_STATE_IP_CONFIG); - - nm_log_info (LOGD_DEVICE, - "Activation (%s) connection '%s' enslaved, continuing activation", - nm_device_get_iface (dev), - nm_connection_get_id (connection)); - - /* Now that we're enslaved, proceed with activation. Remember, slaves - * don't have any IP configuration, so they skip directly to SECONDARIES. - */ - priv->enslaved = TRUE; - priv->ip4_state = IP_DONE; - priv->ip6_state = IP_DONE; - nm_device_queue_state (dev, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE); - } else { - NMDeviceState new_state = NM_DEVICE_STATE_DISCONNECTED; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - - if ( priv->state > NM_DEVICE_STATE_DISCONNECTED - && priv->state <= NM_DEVICE_STATE_ACTIVATED) { - if (master_failed) { - new_state = NM_DEVICE_STATE_FAILED; - reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED; - - nm_log_warn (LOGD_DEVICE, - "Activation (%s) connection '%s' master failed", - nm_device_get_iface (dev), - nm_connection_get_id (connection)); - } else { - nm_log_dbg (LOGD_DEVICE, - "Activation (%s) connection '%s' master deactivated", - nm_device_get_iface (dev), - nm_connection_get_id (connection)); - } - - nm_device_queue_state (dev, new_state, reason); - } - } -} - -/** - * nm_device_get_enslaved: - * @device: the #NMDevice - * - * Returns: %TRUE if the device is enslaved to a master device (eg bridge or - * bond), %FALSE if not - */ -gboolean -nm_device_get_enslaved (NMDevice *device) -{ - return NM_DEVICE_GET_PRIVATE (device)->enslaved; -} - -/* - * nm_device_get_act_request - * - * Return the devices activation request, if any. - * - */ -NMActRequest * -nm_device_get_act_request (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NULL); - - return NM_DEVICE_GET_PRIVATE (self)->act_request; -} - -NMConnection * -nm_device_get_connection (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - return priv->act_request ? nm_act_request_get_connection (priv->act_request) : NULL; -} - -gboolean -nm_device_is_available (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->firmware_missing) - return FALSE; - - if (NM_DEVICE_GET_CLASS (self)->is_available) - return NM_DEVICE_GET_CLASS (self)->is_available (self); - return TRUE; -} - -gboolean -nm_device_get_enabled (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - - if (NM_DEVICE_GET_CLASS (self)->get_enabled) - return NM_DEVICE_GET_CLASS (self)->get_enabled (self); - return TRUE; -} - -void -nm_device_set_enabled (NMDevice *self, gboolean enabled) -{ - g_return_if_fail (self != NULL); - g_return_if_fail (NM_IS_DEVICE (self)); - - if (NM_DEVICE_GET_CLASS (self)->set_enabled) - NM_DEVICE_GET_CLASS (self)->set_enabled (self, enabled); -} - -RfKillType -nm_device_get_rfkill_type (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - - return NM_DEVICE_GET_PRIVATE (self)->rfkill_type; -} - -static gboolean -autoconnect_allowed_accumulator (GSignalInvocationHint *ihint, - GValue *return_accu, - const GValue *handler_return, gpointer data) -{ - if (!g_value_get_boolean (handler_return)) - g_value_set_boolean (return_accu, FALSE); - return TRUE; -} - -gboolean -nm_device_autoconnect_allowed (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - GValue instance = { 0, }; - GValue retval = { 0, }; - - g_value_init (&instance, G_TYPE_OBJECT); - g_value_take_object (&instance, self); - - g_value_init (&retval, G_TYPE_BOOLEAN); - if (priv->autoconnect) - g_value_set_boolean (&retval, TRUE); - else - g_value_set_boolean (&retval, FALSE); - - /* Use g_signal_emitv() rather than g_signal_emit() to avoid the return - * value being changed if no handlers are connected */ - g_signal_emitv (&instance, signals[AUTOCONNECT_ALLOWED], 0, &retval); - return g_value_get_boolean (&retval); -} - -NMConnection * -nm_device_get_best_auto_connection (NMDevice *dev, - GSList *connections, - char **specific_object) -{ - guint32 caps; - GSList *iter, *available_conns; - NMConnection *best_connection; - - g_return_val_if_fail (NM_IS_DEVICE (dev), NULL); - g_return_val_if_fail (specific_object != NULL, NULL); - g_return_val_if_fail (*specific_object == NULL, NULL); - - caps = nm_device_get_capabilities (dev); - /* Don't use devices that SUCK */ - if (!(caps & NM_DEVICE_CAP_NM_SUPPORTED)) - return NULL; - - if (!NM_DEVICE_GET_CLASS (dev)->get_best_auto_connection) - return NULL; - - available_conns = NULL; - for (iter = connections; iter; iter = iter->next) { - NMConnection *connection = NM_CONNECTION (iter->data); - NMSettingConnection *s_con; - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - if (nm_setting_connection_get_autoconnect (s_con)) - available_conns = g_slist_prepend (available_conns, connection); - } - - if (!available_conns) - return NULL; - - best_connection = NM_DEVICE_GET_CLASS (dev)->get_best_auto_connection (dev, available_conns, specific_object); - - g_slist_free (available_conns); - return best_connection; -} - -gboolean -nm_device_complete_connection (NMDevice *self, - NMConnection *connection, - const char *specific_object, - const GSList *existing_connections, - GError **error) -{ - gboolean success = FALSE; - - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (connection != NULL, FALSE); - - if (!NM_DEVICE_GET_CLASS (self)->complete_connection) { - g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CONNECTION_INVALID, - "Device class %s had no complete_connection method", - G_OBJECT_TYPE_NAME (self)); - return FALSE; - } - - success = NM_DEVICE_GET_CLASS (self)->complete_connection (self, - connection, - specific_object, - existing_connections, - error); - if (success) - success = nm_connection_verify (connection, error); - - return success; -} - -gboolean -nm_device_check_connection_compatible (NMDevice *device, - NMConnection *connection, - GError **error) -{ - g_return_val_if_fail (device != NULL, FALSE); - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - g_return_val_if_fail (connection != NULL, FALSE); - g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); - - if (NM_DEVICE_GET_CLASS (device)->check_connection_compatible) - return NM_DEVICE_GET_CLASS (device)->check_connection_compatible (device, connection, error); - return TRUE; -} - -gboolean -nm_device_can_assume_connections (NMDevice *device) -{ - g_return_val_if_fail (device != NULL, FALSE); - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - - return !!NM_DEVICE_GET_CLASS (device)->connection_match_config; -} - -static void -dnsmasq_state_changed_cb (NMDnsMasqManager *manager, guint32 status, gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - - switch (status) { - case NM_DNSMASQ_STATUS_DEAD: - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED); - break; - default: - break; - } -} - -static void -activation_source_clear (NMDevice *self, gboolean remove_source, int family) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - guint *act_source_id; - gpointer *act_source_func; - - if (family == AF_INET6) { - act_source_id = &priv->act_source6_id; - act_source_func = &priv->act_source6_func; - } else { - act_source_id = &priv->act_source_id; - act_source_func = &priv->act_source_func; - } - - if (*act_source_id) { - if (remove_source) - g_source_remove (*act_source_id); - *act_source_id = 0; - *act_source_func = NULL; - } -} - -static void -activation_source_schedule (NMDevice *self, GSourceFunc func, int family) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - guint *act_source_id; - gpointer *act_source_func; - - if (family == AF_INET6) { - act_source_id = &priv->act_source6_id; - act_source_func = &priv->act_source6_func; - } else { - act_source_id = &priv->act_source_id; - act_source_func = &priv->act_source_func; - } - - if (*act_source_id) { - nm_log_err (LOGD_DEVICE, "activation stage already scheduled"); - } - - /* Don't bother rescheduling the same function that's about to - * run anyway. Fixes issues with crappy wireless drivers sending - * streams of associate events before NM has had a chance to process - * the first one. - */ - if (!*act_source_id || (*act_source_func != func)) { - activation_source_clear (self, TRUE, family); - *act_source_id = g_idle_add (func, self); - *act_source_func = func; - } -} - -gboolean -nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6) -{ - NMConnection *connection; - NMSettingIP4Config *s_ip4; - NMSettingIP6Config *s_ip6; - - g_return_val_if_fail (self != NULL, TRUE); - - connection = nm_device_get_connection (self); - g_assert (connection); - - /* Fail the connection if the failed IP method is required to complete */ - if (ip6) { - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (s_ip6 && !nm_setting_ip6_config_get_may_fail (s_ip6)) - return TRUE; - } else { - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (s_ip4 && !nm_setting_ip4_config_get_may_fail (s_ip4)) - return TRUE; - } - - return FALSE; -} - -static NMActStageReturn -act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason) -{ - return NM_ACT_STAGE_RETURN_SUCCESS; -} - -/* - * nm_device_activate_stage1_device_prepare - * - * Prepare for device activation - * - */ -static gboolean -nm_device_activate_stage1_device_prepare (gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *iface; - NMActStageReturn ret; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - - /* Clear the activation source ID now that this stage has run */ - activation_source_clear (self, FALSE, 0); - - priv->ip4_state = priv->ip6_state = IP_NONE; - - iface = nm_device_get_iface (self); - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface); - nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); - - ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason); - if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { - goto out; - } else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - goto out; - } - g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); - - nm_device_activate_schedule_stage2_device_config (self); - -out: - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) complete.", iface); - return FALSE; -} - - -/* - * nm_device_activate_schedule_stage1_device_prepare - * - * Prepare a device for activation - * - */ -void -nm_device_activate_schedule_stage1_device_prepare (NMDevice *self) -{ - NMDevicePrivate *priv; - - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->act_request); - - activation_source_schedule (self, nm_device_activate_stage1_device_prepare, 0); - - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) scheduled...", - nm_device_get_iface (self)); -} - -static NMActStageReturn -act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); - GSList *iter; - - /* If we have slaves that aren't yet enslaved, do that now */ - for (iter = priv->slaves; iter; iter = g_slist_next (iter)) { - SlaveInfo *info = iter->data; - - if (nm_device_get_state (info->slave) == NM_DEVICE_STATE_IP_CONFIG) - nm_device_enslave_slave (dev, info->slave, nm_device_get_connection (info->slave)); - } - - /* Nothing to do */ - return NM_ACT_STAGE_RETURN_SUCCESS; -} - -/* - * nm_device_activate_stage2_device_config - * - * Determine device parameters and set those on the device, ie - * for wireless devices, set SSID, keys, etc. - * - */ -static gboolean -nm_device_activate_stage2_device_config (gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - const char * iface; - NMActStageReturn ret; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - gboolean no_firmware = FALSE; - - /* Clear the activation source ID now that this stage has run */ - activation_source_clear (self, FALSE, 0); - - iface = nm_device_get_iface (self); - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) starting...", iface); - nm_device_state_changed (self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE); - - if (!nm_device_bring_up (self, FALSE, &no_firmware)) { - if (no_firmware) - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_FIRMWARE_MISSING); - else - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED); - goto out; - } - - ret = NM_DEVICE_GET_CLASS (self)->act_stage2_config (self, &reason); - if (ret == NM_ACT_STAGE_RETURN_POSTPONE) - goto out; - else if (ret == NM_ACT_STAGE_RETURN_FAILURE) - { - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - goto out; - } - g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); - - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) successful.", iface); - - nm_device_activate_schedule_stage3_ip_config_start (self); - -out: - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) complete.", iface); - return FALSE; -} - - -/* - * nm_device_activate_schedule_stage2_device_config - * - * Schedule setup of the hardware device - * - */ -void -nm_device_activate_schedule_stage2_device_config (NMDevice *self) -{ - NMDevicePrivate *priv; - - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->act_request); - - activation_source_schedule (self, nm_device_activate_stage2_device_config, 0); - - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) scheduled...", - nm_device_get_iface (self)); -} - -/*********************************************/ -/* avahi-autoipd stuff */ - -static void -aipd_timeout_remove (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->aipd_timeout) { - g_source_remove (priv->aipd_timeout); - priv->aipd_timeout = 0; - } -} - -static void -aipd_cleanup (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->aipd_watch) { - g_source_remove (priv->aipd_watch); - priv->aipd_watch = 0; - } - - if (priv->aipd_pid > 0) { - kill (priv->aipd_pid, SIGKILL); - - /* ensure the child is reaped */ - nm_log_dbg (LOGD_AUTOIP4, "waiting for avahi-autoipd pid %d to exit", priv->aipd_pid); - waitpid (priv->aipd_pid, NULL, 0); - nm_log_dbg (LOGD_AUTOIP4, "avahi-autoip pid %d cleaned up", priv->aipd_pid); - - priv->aipd_pid = -1; - } - - aipd_timeout_remove (self); -} - -static NMIP4Config * -aipd_get_ip4_config (NMDevice *self, struct in_addr lla) -{ - NMIP4Config *config = NULL; - NMIP4Address *addr; - NMIP4Route *route; - - config = nm_ip4_config_new (); - g_assert (config); - - addr = nm_ip4_address_new (); - nm_ip4_address_set_address (addr, (guint32) lla.s_addr); - nm_ip4_address_set_prefix (addr, 16); - nm_ip4_config_take_address (config, addr); - - /* Add a multicast route for link-local connections: destination= 224.0.0.0, netmask=240.0.0.0 */ - route = nm_ip4_route_new (); - nm_ip4_route_set_dest (route, (guint32) htonl (0xE0000000L)); - nm_ip4_route_set_prefix (route, 4); - nm_ip4_route_set_next_hop (route, (guint32) 0); - nm_ip4_route_set_metric (route, 0); - nm_ip4_config_take_route (config, route); - - return config; -} - -static void -autoip_changed (NMDevice *self, - NMIP4Config *config, - NMSettingIP4Config *s_ip4) -{ - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - - nm_utils_merge_ip4_config (config, s_ip4); - if (!nm_device_set_ip4_config (self, config, FALSE, &reason)) { - nm_log_err (LOGD_AUTOIP4, "(%s): failed to update IP4 config in response to autoip event.", - nm_device_get_iface (self)); - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - } -} - -#define IPV4LL_NETWORK (htonl (0xA9FE0000L)) -#define IPV4LL_NETMASK (htonl (0xFFFF0000L)) - -void -nm_device_handle_autoip4_event (NMDevice *self, - const char *event, - const char *address) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection = NULL; - NMSettingIP4Config *s_ip4 = NULL; - const char *iface, *method = NULL; - - g_return_if_fail (event != NULL); - - if (priv->act_request == NULL) - return; - - connection = nm_act_request_get_connection (priv->act_request); - g_assert (connection); - - /* Ignore if the connection isn't an AutoIP connection */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (s_ip4) - method = nm_setting_ip4_config_get_method (s_ip4); - - if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) != 0) - return; - - iface = nm_device_get_iface (self); - - if (strcmp (event, "BIND") == 0) { - struct in_addr lla; - NMIP4Config *config; - - if (inet_pton (AF_INET, address, &lla) <= 0) { - nm_log_err (LOGD_AUTOIP4, "(%s): invalid address %s received from avahi-autoipd.", - iface, address); - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_ERROR); - return; - } - - if ((lla.s_addr & IPV4LL_NETMASK) != IPV4LL_NETWORK) { - nm_log_err (LOGD_AUTOIP4, "(%s): invalid address %s received from avahi-autoipd (not link-local).", - iface, address); - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_ERROR); - return; - } - - config = aipd_get_ip4_config (self, lla); - if (config == NULL) { - nm_log_err (LOGD_AUTOIP4, "failed to get autoip config"); - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return; - } - - if (priv->ip4_state == IP_CONF) { - aipd_timeout_remove (self); - nm_device_activate_schedule_ip4_config_result (self, config); - } else if (priv->ip4_state == IP_DONE) { - autoip_changed (self, config, s_ip4); - } else - g_assert_not_reached (); - - g_object_unref (config); - } else { - nm_log_warn (LOGD_AUTOIP4, "(%s): autoip address %s no longer valid because '%s'.", - iface, address, event); - - /* The address is gone; terminate the connection or fail activation */ - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); - } -} - -static void -aipd_watch_cb (GPid pid, gint status, gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMDeviceState state; - const char *iface; - - if (!priv->aipd_watch) - return; - priv->aipd_watch = 0; - - iface = nm_device_get_iface (self); - - if (WIFEXITED (status)) { - nm_log_dbg (LOGD_AUTOIP4, "(%s): avahi-autoipd exited with error code %d", - iface, WEXITSTATUS (status)); - } else if (WIFSTOPPED (status)) { - nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd stopped unexpectedly with signal %d", - iface, WSTOPSIG (status)); - } else if (WIFSIGNALED (status)) { - nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd died with signal %d", - iface, WTERMSIG (status)); - } else { - nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd died from an unknown cause", iface); - } - - aipd_cleanup (self); - - state = nm_device_get_state (self); - if (nm_device_is_activating (self) || (state == NM_DEVICE_STATE_ACTIVATED)) - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_FAILED); -} - -static gboolean -aipd_timeout_cb (gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->aipd_timeout) { - nm_log_info (LOGD_AUTOIP4, "(%s): avahi-autoipd timed out.", nm_device_get_iface (self)); - priv->aipd_timeout = 0; - aipd_cleanup (self); - - if (priv->ip4_state == IP_CONF) - nm_device_activate_schedule_ip4_config_timeout (self); - } - - return FALSE; -} - -static void -aipd_child_setup (gpointer user_data G_GNUC_UNUSED) -{ - /* We are in the child process at this point. - * Give child it's own program group for signal - * separation. - */ - pid_t pid = getpid (); - setpgid (pid, pid); - - /* - * We blocked signals in main(). We need to restore original signal - * mask for avahi-autoipd here so that it can receive signals. - */ - nm_unblock_posix_signals (NULL); -} - -static NMActStageReturn -aipd_start (NMDevice *self, NMDeviceStateReason *reason) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *iface = nm_device_get_iface (self); - char *argv[6], *cmdline; - const char **aipd_binary = NULL; - static const char *aipd_paths[] = { - "/usr/sbin/avahi-autoipd", - "/usr/local/sbin/avahi-autoipd", - NULL - }; - int i = 0; - GError *error = NULL; - - aipd_cleanup (self); - - /* Find avahi-autoipd */ - aipd_binary = aipd_paths; - while (*aipd_binary != NULL) { - if (g_file_test (*aipd_binary, G_FILE_TEST_EXISTS)) - break; - aipd_binary++; - } - - if (!*aipd_binary) { - nm_log_warn (LOGD_DEVICE | LOGD_AUTOIP4, - "Activation (%s) Stage 3 of 5 (IP Configure Start) failed" - " to start avahi-autoipd: not found", iface); - *reason = NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED; - return NM_ACT_STAGE_RETURN_FAILURE; - } - - argv[i++] = (char *) (*aipd_binary); - argv[i++] = "--script"; - argv[i++] = LIBEXECDIR "/nm-avahi-autoipd.action"; - if (nm_logging_level_enabled (LOGL_DEBUG)) - argv[i++] = "--debug"; - argv[i++] = (char *) nm_device_get_ip_iface (self); - argv[i++] = NULL; - - cmdline = g_strjoinv (" ", argv); - nm_log_dbg (LOGD_AUTOIP4, "running: %s", cmdline); - g_free (cmdline); - - if (!g_spawn_async ("/", argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, - &aipd_child_setup, NULL, &(priv->aipd_pid), &error)) { - nm_log_warn (LOGD_DEVICE | LOGD_AUTOIP4, - "Activation (%s) Stage 3 of 5 (IP Configure Start) failed" - " to start avahi-autoipd: %s", - iface, - error && error->message ? error->message : "(unknown)"); - g_clear_error (&error); - aipd_cleanup (self); - return NM_ACT_STAGE_RETURN_FAILURE; - } - - nm_log_info (LOGD_DEVICE | LOGD_AUTOIP4, - "Activation (%s) Stage 3 of 5 (IP Configure Start) started" - " avahi-autoipd...", iface); - - /* Monitor the child process so we know when it dies */ - priv->aipd_watch = g_child_watch_add (priv->aipd_pid, aipd_watch_cb, self); - - /* Start a timeout to bound the address attempt */ - priv->aipd_timeout = g_timeout_add_seconds (20, aipd_timeout_cb, self); - - return NM_ACT_STAGE_RETURN_POSTPONE; -} - -/*********************************************/ -/* DHCPv4 stuff */ - -static void -dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data) -{ - nm_dhcp4_config_add_option (NM_DHCP4_CONFIG (user_data), - (const char *) key, - (const char *) value); -} - -static void -dhcp4_lease_change (NMDevice *device, NMIP4Config *config) -{ - NMConnection *connection; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - - if (config == NULL) { - nm_log_warn (LOGD_DHCP4, "(%s): failed to get DHCPv4 config for rebind", - nm_device_get_ip_iface (device)); - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); - return; - } - - connection = nm_device_get_connection (device); - g_assert (connection); - - /* Merge with user overrides */ - nm_utils_merge_ip4_config (config, nm_connection_get_setting_ip4_config (connection)); - - if (!nm_device_set_ip4_config (device, config, FALSE, &reason)) { - nm_log_warn (LOGD_DHCP4, "(%s): failed to update IPv4 config in response to DHCP event.", - nm_device_get_ip_iface (device)); - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); - return; - } - - /* Notify dispatcher scripts of new DHCP4 config */ - nm_dispatcher_call (DISPATCHER_ACTION_DHCP4_CHANGE, connection, device, NULL, NULL); -} - -static void -dhcp4_fail (NMDevice *device, gboolean timeout) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - - nm_dhcp4_config_reset (priv->dhcp4_config); - - if (timeout || (priv->ip4_state == IP_CONF)) - nm_device_activate_schedule_ip4_config_timeout (device); - else if (priv->ip4_state == IP_DONE) - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); -} - -static void -dhcp4_state_changed (NMDHCPClient *client, - NMDHCPState state, - gpointer user_data) -{ - NMDevice *device = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - NMDeviceState dev_state; - NMIP4Config *config; - - g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == FALSE); - - nm_log_dbg (LOGD_DHCP4, "(%s): new DHCPv4 client state %d", - nm_device_get_iface (device), state); - - dev_state = nm_device_get_state (device); - - switch (state) { - case DHC_BOUND4: /* lease obtained */ - case DHC_RENEW4: /* lease renewed */ - case DHC_REBOOT: /* have valid lease, but now obtained a different one */ - case DHC_REBIND4: /* new, different lease */ - config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE); - - /* Update the DHCP4 config object with new DHCP options */ - nm_dhcp4_config_reset (priv->dhcp4_config); - if (config) { - nm_dhcp_client_foreach_option (priv->dhcp4_client, - dhcp4_add_option_cb, - priv->dhcp4_config); - } - g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP4_CONFIG); - - if (priv->ip4_state == IP_CONF) - nm_device_activate_schedule_ip4_config_result (device, config); - else if (priv->ip4_state == IP_DONE) - dhcp4_lease_change (device, config); - - if (config) - g_object_unref (config); - break; - case DHC_TIMEOUT: /* timed out contacting DHCP server */ - dhcp4_fail (device, TRUE); - break; - case DHC_END: /* dhclient exited normally */ - case DHC_FAIL: /* all attempts to contact server timed out, sleeping */ - case DHC_ABEND: /* dhclient exited abnormally */ - /* dhclient quit and can't get/renew a lease; so kill the connection */ - dhcp4_fail (device, FALSE); - break; - default: - break; - } -} - -static void -dhcp4_timeout (NMDHCPClient *client, gpointer user_data) -{ - NMDevice *device = NM_DEVICE (user_data); - - g_return_if_fail (nm_device_get_act_request (device) != NULL); - g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == FALSE); - - nm_dhcp_client_stop (client, FALSE); - dhcp4_fail (device, TRUE); -} - -static NMActStageReturn -dhcp4_start (NMDevice *self, - NMConnection *connection, - NMDeviceStateReason *reason) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMSettingIP4Config *s_ip4; - guint8 *anycast = NULL; - GByteArray *tmp = NULL; - guint hwaddr_len = 0; - const guint8 *hwaddr; - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - - if (priv->dhcp_anycast_address) - anycast = priv->dhcp_anycast_address->data; - - /* Clear old exported DHCP options */ - if (priv->dhcp4_config) - g_object_unref (priv->dhcp4_config); - priv->dhcp4_config = nm_dhcp4_config_new (); - - hwaddr = nm_device_get_hw_address (self, &hwaddr_len); - if (hwaddr) { - tmp = g_byte_array_sized_new (hwaddr_len); - g_byte_array_append (tmp, hwaddr, hwaddr_len); - } - - /* Begin DHCP on the interface */ - g_warn_if_fail (priv->dhcp4_client == NULL); - priv->dhcp4_client = nm_dhcp_manager_start_ip4 (priv->dhcp_manager, - nm_device_get_ip_iface (self), - tmp, - nm_connection_get_uuid (connection), - s_ip4, - priv->dhcp_timeout, - anycast); - - if (tmp) - g_byte_array_free (tmp, TRUE); - - if (!priv->dhcp4_client) { - *reason = NM_DEVICE_STATE_REASON_DHCP_START_FAILED; - return NM_ACT_STAGE_RETURN_FAILURE; - } - - priv->dhcp4_state_sigid = g_signal_connect (priv->dhcp4_client, - "state-changed", - G_CALLBACK (dhcp4_state_changed), - self); - priv->dhcp4_timeout_sigid = g_signal_connect (priv->dhcp4_client, - "timeout", - G_CALLBACK (dhcp4_timeout), - self); - - /* DHCP devices will be notified by the DHCP manager when stuff happens */ - return NM_ACT_STAGE_RETURN_POSTPONE; -} - -gboolean -nm_device_dhcp4_renew (NMDevice *self, gboolean release) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActStageReturn ret; - NMDeviceStateReason reason; - NMConnection *connection; - - g_return_val_if_fail (priv->dhcp4_client != NULL, FALSE); - - nm_log_info (LOGD_DHCP4, "(%s): DHCPv4 lease renewal requested", - nm_device_get_iface (self)); - - /* Terminate old DHCP instance and release the old lease */ - dhcp4_cleanup (self, TRUE, release); - - connection = nm_device_get_connection (self); - g_assert (connection); - - /* Start DHCP again on the interface */ - ret = dhcp4_start (self, connection, &reason); - - return (ret != NM_ACT_STAGE_RETURN_FAILURE); -} - -/*********************************************/ - -static GHashTable *shared_ips = NULL; - -static void -release_shared_ip (gpointer data) -{ - g_hash_table_remove (shared_ips, data); -} - -static guint32 -reserve_shared_ip (void) -{ - guint32 start = (guint32) ntohl (0x0a2a0001); /* 10.42.0.1 */ - guint32 count = 0; - - while (g_hash_table_lookup (shared_ips, GUINT_TO_POINTER (start + count))) { - count += ntohl (0x100); - if (count > ntohl (0xFE00)) { - nm_log_err (LOGD_SHARING, "ran out of shared IP addresses!"); - return 0; - } - } - - g_hash_table_insert (shared_ips, GUINT_TO_POINTER (start + count), GUINT_TO_POINTER (TRUE)); - return start + count; -} - -static NMIP4Config * -shared4_new_config (NMDevice *self, NMDeviceStateReason *reason) -{ - NMIP4Config *config = NULL; - NMIP4Address *addr; - guint32 tmp_addr; - - g_return_val_if_fail (self != NULL, NULL); - - if (G_UNLIKELY (shared_ips == NULL)) - shared_ips = g_hash_table_new (g_direct_hash, g_direct_equal); - - tmp_addr = reserve_shared_ip (); - if (!tmp_addr) { - *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; - return NULL; - } - - config = nm_ip4_config_new (); - addr = nm_ip4_address_new (); - nm_ip4_address_set_address (addr, tmp_addr); - nm_ip4_address_set_prefix (addr, 24); - nm_ip4_config_take_address (config, addr); - - /* Remove the address lock when the object gets disposed */ - g_object_set_data_full (G_OBJECT (config), "shared-ip", - GUINT_TO_POINTER (tmp_addr), release_shared_ip); - - return config; -} - -/*********************************************/ - -static gboolean -have_any_ready_slaves (NMDevice *device, const GSList *slaves) -{ - const GSList *iter; - - /* Any enslaved slave is "ready" in the generic case as it's - * at least >= NM_DEVCIE_STATE_IP_CONFIG and has had Layer 2 - * properties set up. - */ - for (iter = slaves; iter; iter = g_slist_next (iter)) { - if (nm_device_get_enslaved (iter->data)) - return TRUE; - } - return FALSE; -} - -static gboolean -ip4_requires_slaves (NMConnection *connection) -{ - NMSettingIP4Config *s_ip4; - const char *method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (s_ip4) - method = nm_setting_ip4_config_get_method (s_ip4); - - return g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0; -} - -static NMActStageReturn -act_stage3_ip4_config_start (NMDevice *self, - NMIP4Config **out_config, - NMDeviceStateReason *reason) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; - NMSettingIP4Config *s_ip4; - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; - const char *method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; - GSList *slaves; - gboolean ready_slaves; - - g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); - - connection = nm_device_get_connection (self); - g_assert (connection); - - if (priv->is_master && ip4_requires_slaves (connection)) { - /* If the master has no ready slaves, and depends on slaves for - * a successful IPv4 attempt, then postpone IPv4 addressing. - */ - slaves = nm_device_master_get_slaves (self); - ready_slaves = NM_DEVICE_GET_CLASS (self)->have_any_ready_slaves (self, slaves); - g_slist_free (slaves); - - if (ready_slaves == FALSE) { - nm_log_info (LOGD_DEVICE | LOGD_IP4, - "(%s): IPv4 config waiting until slaves are ready", - nm_device_get_ip_iface (self)); - return NM_ACT_STAGE_RETURN_WAIT; - } - } - - /* If we did not receive IP4 configuration information, default to DHCP. - * Slaves, on the other hand, never have any IP configuration themselves, - * since the master handles all of that. - */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (priv->master) /* eg, device is a slave */ - method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - else if (s_ip4) - method = nm_setting_ip4_config_get_method (s_ip4); - else - method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; - - /* Start IPv4 addressing based on the method requested */ - if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) - ret = dhcp4_start (self, connection, reason); - else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) == 0) - ret = aipd_start (self, reason); - else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0) { - /* Use only IPv4 config from the connection data */ - *out_config = nm_ip4_config_new (); - g_assert (*out_config); - ret = NM_ACT_STAGE_RETURN_SUCCESS; - } else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) { - *out_config = shared4_new_config (self, reason); - if (*out_config) { - priv->dnsmasq_manager = nm_dnsmasq_manager_new (nm_device_get_ip_iface (self)); - ret = NM_ACT_STAGE_RETURN_SUCCESS; - } else - ret = NM_ACT_STAGE_RETURN_FAILURE; - } else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) { - /* Nothing to do... */ - ret = NM_ACT_STAGE_RETURN_STOP; - } else { - nm_log_warn (LOGD_IP4, "(%s): unhandled IPv4 config method '%s'; will fail", - nm_device_get_ip_iface (self), method); - } - - return ret; -} - -/*********************************************/ -/* DHCPv6 stuff */ - -static void -dhcp6_add_option_cb (gpointer key, gpointer value, gpointer user_data) -{ - nm_dhcp6_config_add_option (NM_DHCP6_CONFIG (user_data), - (const char *) key, - (const char *) value); -} - -static void -merge_ip6_configs (NMIP6Config *dst, NMIP6Config *src) -{ - guint32 i; - - g_return_if_fail (src != NULL); - g_return_if_fail (dst != NULL); - - /* addresses */ - for (i = 0; i < nm_ip6_config_get_num_addresses (src); i++) - nm_ip6_config_add_address (dst, nm_ip6_config_get_address (src, i)); - - /* ptp address; only replace if src doesn't have one */ - if (!nm_ip6_config_get_ptp_address (dst)) - nm_ip6_config_set_ptp_address (dst, nm_ip6_config_get_ptp_address (src)); - - /* nameservers */ - for (i = 0; i < nm_ip6_config_get_num_nameservers (src); i++) - nm_ip6_config_add_nameserver (dst, nm_ip6_config_get_nameserver (src, i)); - - /* default gateway */ - if (!nm_ip6_config_get_gateway (dst)) - nm_ip6_config_set_gateway (dst, nm_ip6_config_get_gateway (src)); - - /* routes */ - for (i = 0; i < nm_ip6_config_get_num_routes (src); i++) - nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i)); - - /* domains */ - for (i = 0; i < nm_ip6_config_get_num_domains (src); i++) - nm_ip6_config_add_domain (dst, nm_ip6_config_get_domain (src, i)); - - /* dns searches */ - for (i = 0; i < nm_ip6_config_get_num_searches (src); i++) - nm_ip6_config_add_search (dst, nm_ip6_config_get_search (src, i)); - - if (!nm_ip6_config_get_mss (dst)) - nm_ip6_config_set_mss (dst, nm_ip6_config_get_mss (src)); -} - -static gboolean -ip6_config_merge_and_apply (NMDevice *self, - NMIP6Config *src_config, - NMDeviceStateReason *out_reason) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; - gboolean success; - NMIP6Config *composite; - - connection = nm_device_get_connection (self); - g_assert (connection); - - /* If no config was passed in, create a new one */ - composite = nm_ip6_config_new (); - g_assert (composite); - - /* Merge in the given config first, if any */ - if (src_config) - merge_ip6_configs (composite, src_config); - - /* Merge RA and DHCPv6 configs into the composite config */ - if (priv->ac_ip6_config && (src_config != priv->ac_ip6_config)) - merge_ip6_configs (composite, priv->ac_ip6_config); - if (priv->dhcp6_ip6_config && (src_config != priv->dhcp6_ip6_config)) - merge_ip6_configs (composite, priv->dhcp6_ip6_config); - - /* Merge user overrides into the composite config */ - nm_utils_merge_ip6_config (composite, nm_connection_get_setting_ip6_config (connection)); - - success = nm_device_set_ip6_config (self, composite, out_reason); - g_object_unref (composite); - return success; -} - -static void -dhcp6_lease_change (NMDevice *device) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - NMConnection *connection; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - - if (priv->dhcp6_ip6_config == NULL) { - nm_log_warn (LOGD_DHCP6, "(%s): failed to get DHCPv6 config for rebind", - nm_device_get_ip_iface (device)); - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); - return; - } - - g_assert (priv->dhcp6_client); /* sanity check */ - - connection = nm_device_get_connection (device); - g_assert (connection); - - /* Apply the updated config */ - if (ip6_config_merge_and_apply (device, NULL, &reason) == FALSE) { - nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv6 config in response to DHCP event.", - nm_device_get_ip_iface (device)); - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); - } else { - /* Notify dispatcher scripts of new DHCPv6 config */ - nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE, connection, device, NULL, NULL); - } -} - -static void -dhcp6_fail (NMDevice *device, gboolean timeout) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - - nm_dhcp6_config_reset (priv->dhcp6_config); - - if (timeout || (priv->ip6_state == IP_CONF)) - nm_device_activate_schedule_ip6_config_timeout (device); - else if (priv->ip6_state == IP_DONE) - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); -} - -static void -dhcp6_state_changed (NMDHCPClient *client, - NMDHCPState state, - gpointer user_data) -{ - NMDevice *device = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - NMDeviceState dev_state; - - g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE); - - nm_log_dbg (LOGD_DHCP6, "(%s): new DHCPv6 client state %d", - nm_device_get_iface (device), state); - - dev_state = nm_device_get_state (device); - - switch (state) { - case DHC_BOUND6: - case DHC_RENEW6: /* lease renewed */ - case DHC_REBOOT: /* have valid lease, but now obtained a different one */ - case DHC_REBIND6: /* new, different lease */ - if (priv->dhcp6_ip6_config) - g_object_unref (priv->dhcp6_ip6_config); - priv->dhcp6_ip6_config = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE); - - /* Update the DHCP6 config object with new DHCP options */ - nm_dhcp6_config_reset (priv->dhcp6_config); - if (priv->dhcp6_ip6_config) { - nm_dhcp_client_foreach_option (priv->dhcp6_client, - dhcp6_add_option_cb, - priv->dhcp6_config); - } - g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP6_CONFIG); - - if (priv->ip6_state == IP_CONF) - nm_device_activate_schedule_ip6_config_result (device, priv->dhcp6_ip6_config); - else if (priv->ip6_state == IP_DONE) - dhcp6_lease_change (device); - break; - case DHC_TIMEOUT: /* timed out contacting DHCP server */ - dhcp6_fail (device, TRUE); - break; - case DHC_END: /* dhclient exited normally */ - /* In IPv6 info-only mode, the client doesn't handle leases so it - * may exit right after getting a response from the server. That's - * normal. In that case we just ignore the exit. - */ - if (priv->dhcp6_mode == IP6_DHCP_OPT_OTHERCONF) - break; - /* Otherwise, fall through */ - case DHC_FAIL: /* all attempts to contact server timed out, sleeping */ - case DHC_ABEND: /* dhclient exited abnormally */ - /* dhclient quit and can't get/renew a lease; so kill the connection */ - dhcp6_fail (device, FALSE); - break; - default: - break; - } -} - -static void -dhcp6_timeout (NMDHCPClient *client, gpointer user_data) -{ - NMDevice *device = NM_DEVICE (user_data); - - g_return_if_fail (nm_device_get_act_request (device) != NULL); - g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE); - - nm_dhcp_client_stop (client, FALSE); - dhcp6_fail (device, TRUE); -} - -static NMActStageReturn -dhcp6_start (NMDevice *self, - NMConnection *connection, - guint32 dhcp_opt, - NMDeviceStateReason *reason) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; - guint8 *anycast = NULL; - GByteArray *tmp = NULL; - guint hwaddr_len = 0; - const guint8 *hwaddr; - - if (!connection) { - connection = nm_device_get_connection (self); - g_assert (connection); - } - - /* Begin a DHCP transaction on the interface */ - - if (priv->dhcp_anycast_address) - anycast = priv->dhcp_anycast_address->data; - - /* Clear old exported DHCP options */ - if (priv->dhcp6_config) - g_object_unref (priv->dhcp6_config); - priv->dhcp6_config = nm_dhcp6_config_new (); - - g_warn_if_fail (priv->dhcp6_ip6_config == NULL); - if (priv->dhcp6_ip6_config) { - g_object_unref (priv->dhcp6_ip6_config); - priv->dhcp6_ip6_config = NULL; - } - - hwaddr = nm_device_get_hw_address (self, &hwaddr_len); - if (hwaddr) { - tmp = g_byte_array_sized_new (hwaddr_len); - g_byte_array_append (tmp, hwaddr, hwaddr_len); - } - - priv->dhcp6_client = nm_dhcp_manager_start_ip6 (priv->dhcp_manager, - nm_device_get_ip_iface (self), - tmp, - nm_connection_get_uuid (connection), - nm_connection_get_setting_ip6_config (connection), - priv->dhcp_timeout, - anycast, - (dhcp_opt == IP6_DHCP_OPT_OTHERCONF) ? TRUE : FALSE); - if (tmp) - g_byte_array_free (tmp, TRUE); - - if (priv->dhcp6_client) { - priv->dhcp6_state_sigid = g_signal_connect (priv->dhcp6_client, - "state-changed", - G_CALLBACK (dhcp6_state_changed), - self); - priv->dhcp6_timeout_sigid = g_signal_connect (priv->dhcp6_client, - "timeout", - G_CALLBACK (dhcp6_timeout), - self); - - /* DHCP devices will be notified by the DHCP manager when stuff happens */ - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else { - *reason = NM_DEVICE_STATE_REASON_DHCP_START_FAILED; - ret = NM_ACT_STAGE_RETURN_FAILURE; - } - - return ret; -} - -/******************************************/ - -static void -ip6_addrconf_complete (NMIP6Manager *ip6_manager, - int ifindex, - guint dhcp_opts, - gboolean success, - gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; - NMActStageReturn ret; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - - if (ifindex != nm_device_get_ip_ifindex (self)) - return; - g_return_if_fail (priv->act_request != NULL); - connection = nm_device_get_connection (self); - g_assert (connection); - - if (!priv->ip6_waiting_for_config) - return; - - priv->ip6_waiting_for_config = FALSE; - - if (!success) { - nm_device_activate_schedule_ip6_config_timeout (self); - return; - } - - priv->dhcp6_mode = dhcp_opts; - - /* If addrconf is all that's required, we're done */ - if (priv->dhcp6_mode == IP6_DHCP_OPT_NONE) { - priv->ac_ip6_config = nm_ip6_manager_get_ip6_config (ip6_manager, ifindex); - nm_device_activate_schedule_ip6_config_result (self, priv->ac_ip6_config); - return; - } - - /* If the router said to use DHCP for managed or otherconf, do it */ - - /* Don't re-start DHCPv6 if it's already in progress */ - if (priv->ip6_state != IP_CONF) - return; - - nm_log_info (LOGD_DEVICE | LOGD_DHCP6, - "Activation (%s) Stage 3 of 5 (IP Configure Start) starting DHCPv6" - " as requested by IPv6 router...", - priv->iface); - - ret = dhcp6_start (self, connection, priv->dhcp6_mode, &reason); - switch (ret) { - case NM_ACT_STAGE_RETURN_SUCCESS: - /* Shouldn't get this, but handle it anyway */ - g_warn_if_reached (); - priv->ac_ip6_config = nm_ip6_manager_get_ip6_config (ip6_manager, ifindex); - nm_device_activate_schedule_ip6_config_result (self, priv->ac_ip6_config); - break; - case NM_ACT_STAGE_RETURN_POSTPONE: - /* Cache acquired autoconf config and wait for DHCPv6 to complete */ - priv->ac_ip6_config = nm_ip6_manager_get_ip6_config (ip6_manager, ifindex); - break; - default: - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - break; - } -} - -static void -ip6_config_changed (NMIP6Manager *ip6_manager, - int ifindex, - guint dhcp_opts, - gboolean success, - gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - - if (ifindex != nm_device_get_ip_ifindex (self)) - return; - g_return_if_fail (priv->act_request != NULL); - - /* If autoconf failed and IPv6 previously succeeded, fail */ - if (!success && (priv->ip6_state == IP_DONE)) { - nm_device_state_changed (self, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return; - } - - /* FIXME: re-run DHCPv6 here to get any new nameservers or whatever */ - - if (priv->ac_ip6_config) - g_object_unref (priv->ac_ip6_config); - priv->ac_ip6_config = nm_ip6_manager_get_ip6_config (ip6_manager, ifindex); - - if (ip6_config_merge_and_apply (self, NULL, &reason) == FALSE) { - nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv6 config in response to Router Advertisement.", - nm_device_get_ip_iface (self)); - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - } -} - -static gboolean -addrconf6_start (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; - gboolean success; - const guint8 *hwaddr; - guint hwaddr_len = 0; - - connection = nm_device_get_connection (self); - g_assert (connection); - - g_warn_if_fail (priv->ac_ip6_config == NULL); - if (priv->ac_ip6_config) { - g_object_unref (priv->ac_ip6_config); - priv->ac_ip6_config = NULL; - } - - if (!priv->ip6_manager) { - priv->ip6_manager = nm_ip6_manager_get (); - priv->ip6_addrconf_sigid = g_signal_connect (priv->ip6_manager, - "addrconf-complete", - G_CALLBACK (ip6_addrconf_complete), - self); - priv->ip6_config_changed_sigid = g_signal_connect (priv->ip6_manager, - "config-changed", - G_CALLBACK (ip6_config_changed), - self); - } - - hwaddr = nm_device_get_hw_address (self, &hwaddr_len); - g_warn_if_fail (hwaddr != NULL); - success = nm_ip6_manager_prepare_interface (priv->ip6_manager, - nm_device_get_ip_ifindex (self), - hwaddr, - hwaddr_len, - nm_connection_get_setting_ip6_config (connection), - priv->ip6_accept_ra_path); - if (success) { - priv->ip6_waiting_for_config = TRUE; - nm_ip6_manager_begin_addrconf (priv->ip6_manager, nm_device_get_ip_ifindex (self)); - } - - return success; -} - -static void -addrconf6_cleanup (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->ac_ip6_config) { - g_object_unref (priv->ac_ip6_config); - priv->ac_ip6_config = NULL; - } - - if (!priv->ip6_manager) - return; - - if (priv->ip6_addrconf_sigid) { - g_signal_handler_disconnect (priv->ip6_manager, - priv->ip6_addrconf_sigid); - priv->ip6_addrconf_sigid = 0; - } - if (priv->ip6_config_changed_sigid) { - g_signal_handler_disconnect (priv->ip6_manager, - priv->ip6_config_changed_sigid); - priv->ip6_config_changed_sigid = 0; - } - - nm_ip6_manager_cancel_addrconf (priv->ip6_manager, nm_device_get_ip_ifindex (self)); - g_object_unref (priv->ip6_manager); - priv->ip6_manager = NULL; -} - -/******************************************/ - -/* Get net.ipv6.conf.default.use_tempaddr value from /etc/sysctl.conf or - * /lib/sysctl.d/sysctl.conf - */ -static int -ip6_use_tempaddr (void) -{ - char *contents = NULL; - gsize len = 0; - const char *group_name = "[forged_group]\n"; - char *sysctl_data = NULL; - GKeyFile *keyfile; - GError *error = NULL; - int tmp, ret = -1; - - /* Read file contents to a string. */ - if (!g_file_get_contents ("/etc/sysctl.conf", &contents, &len, NULL)) - if (!g_file_get_contents ("/lib/sysctl.d/sysctl.conf", &contents, &len, NULL)) - return -1; - - /* Prepend a group so that we can use GKeyFile parser. */ - sysctl_data = g_strdup_printf ("%s%s", group_name, contents); - - keyfile = g_key_file_new (); - if (keyfile == NULL) - goto done; - - if (!g_key_file_load_from_data (keyfile, sysctl_data, len + strlen (group_name), G_KEY_FILE_NONE, NULL)) - goto done; - - tmp = g_key_file_get_integer (keyfile, "forged_group", "net.ipv6.conf.default.use_tempaddr", &error); - if (error == NULL) - ret = tmp; - -done: - g_free (contents); - g_free (sysctl_data); - g_clear_error (&error); - g_key_file_free (keyfile); - - return ret; -} - -static gboolean -ip6_requires_slaves (NMConnection *connection) -{ - NMSettingIP6Config *s_ip6; - const char *method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; - - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (s_ip6) - method = nm_setting_ip6_config_get_method (s_ip6); - - /* SLAAC, DHCP, and Link-Local depend on connectivity (and thus slaves) - * to complete addressing. SLAAC and DHCP obviously need a peer to - * provide a prefix, while Link-Local must perform DAD on the local link. - */ - return g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 - || g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0 - || g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0; -} - -static NMActStageReturn -act_stage3_ip6_config_start (NMDevice *self, - NMIP6Config **out_config, - NMDeviceStateReason *reason) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *ip_iface; - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; - NMConnection *connection; - NMSettingIP6Config *s_ip6; - const char *method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; - int conf_use_tempaddr; - NMSettingIP6ConfigPrivacy ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; - const char *ip6_privacy_str = "0\n"; - GSList *slaves; - gboolean ready_slaves; - - g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); - - connection = nm_device_get_connection (self); - g_assert (connection); - - ip_iface = nm_device_get_ip_iface (self); - - if (priv->is_master && ip6_requires_slaves (connection)) { - /* If the master has no ready slaves, and depends on slaves for - * a successful IPv6 attempt, then postpone IPv6 addressing. - */ - slaves = nm_device_master_get_slaves (self); - ready_slaves = NM_DEVICE_GET_CLASS (self)->have_any_ready_slaves (self, slaves); - g_slist_free (slaves); - - if (ready_slaves == FALSE) { - nm_log_info (LOGD_DEVICE | LOGD_IP6, - "(%s): IPv6 config waiting until slaves are ready", - ip_iface); - return NM_ACT_STAGE_RETURN_WAIT; - } - } - - update_accept_ra_save (self); - update_ip6_privacy_save (self); - - priv->dhcp6_mode = IP6_DHCP_OPT_NONE; - - /* If we did not receive IP6 configuration information, default to AUTO. - * Slaves, on the other hand, never have any IP configuration themselves, - * since the master handles all of that. - */ - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (priv->master) /* eg, device is a slave */ - method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; - else if (s_ip6) - method = nm_setting_ip6_config_get_method (s_ip6); - else - method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; - - if ( strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 - || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) { - if (!addrconf6_start (self)) { - *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; - ret = NM_ACT_STAGE_RETURN_FAILURE; - } else - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) { - /* Router advertisements shouldn't be used in pure DHCP mode */ - if (priv->ip6_accept_ra_path) - nm_utils_do_sysctl (priv->ip6_accept_ra_path, "0"); - - priv->dhcp6_mode = IP6_DHCP_OPT_MANAGED; - ret = dhcp6_start (self, connection, priv->dhcp6_mode, reason); - } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) { - /* reset the saved RA value when ipv6 is ignored */ - if (priv->ip6_accept_ra_path) { - nm_utils_do_sysctl (priv->ip6_accept_ra_path, - priv->ip6_accept_ra_save ? "1" : "0"); - } - ret = NM_ACT_STAGE_RETURN_STOP; - } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) == 0) { - /* New blank config */ - *out_config = nm_ip6_config_new (); - g_assert (*out_config); - - /* Router advertisements shouldn't be used in manual mode */ - if (priv->ip6_accept_ra_path) - nm_utils_do_sysctl (priv->ip6_accept_ra_path, "0"); - ret = NM_ACT_STAGE_RETURN_SUCCESS; - } else { - nm_log_warn (LOGD_IP6, "(%s): unhandled IPv6 config method '%s'; will fail", - nm_device_get_ip_iface (self), method); - } - - /* Other methods (shared) aren't implemented yet */ - - /* Enable/disable IPv6 Privacy Extensions. - * If a global value is configured by sysadmin (e.g. /etc/sysctl.conf), - * use that value instead of per-connection value. - */ - conf_use_tempaddr = ip6_use_tempaddr (); - if (conf_use_tempaddr >= 0) - ip6_privacy = conf_use_tempaddr; - else if (s_ip6) - ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (s_ip6); - ip6_privacy = CLAMP (ip6_privacy, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR); - - switch (ip6_privacy) { - case NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN: - case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED: - ip6_privacy_str = "0"; - break; - case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR: - ip6_privacy_str = "1"; - break; - case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR: - ip6_privacy_str = "2"; - break; - } - if (priv->ip6_privacy_tempaddr_path) - nm_utils_do_sysctl (priv->ip6_privacy_tempaddr_path, ip6_privacy_str); - - return ret; -} - -/** - * nm_device_activate_stage3_ip4_start: - * @self: the device - * - * Try starting IPv4 configuration. - */ -gboolean -nm_device_activate_stage3_ip4_start (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActStageReturn ret; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - NMIP4Config *ip4_config = NULL; - - g_assert (priv->ip4_state == IP_WAIT); - - priv->ip4_state = IP_CONF; - ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip4_config_start (self, &ip4_config, &reason); - if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { - g_assert (ip4_config); - nm_device_activate_schedule_ip4_config_result (self, ip4_config); - g_object_unref (ip4_config); - } else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - return FALSE; - } else if (ret == NM_ACT_STAGE_RETURN_STOP) { - /* Early finish */ - priv->ip4_state = IP_DONE; - } else if (ret == NM_ACT_STAGE_RETURN_WAIT) { - /* Wait for something to try IP config again */ - priv->ip4_state = IP_WAIT; - } else - g_assert (ret == NM_ACT_STAGE_RETURN_POSTPONE); - - return TRUE; -} - -/** - * nm_device_activate_stage3_ip6_start: - * @self: the device - * - * Try starting IPv6 configuration. - */ -gboolean -nm_device_activate_stage3_ip6_start (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActStageReturn ret; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - NMIP6Config *ip6_config = NULL; - - g_assert (priv->ip6_state == IP_WAIT); - - priv->ip6_state = IP_CONF; - ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &ip6_config, &reason); - if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { - g_assert (ip6_config); - nm_device_activate_schedule_ip6_config_result (self, ip6_config); - g_object_unref (ip6_config); - } else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - return FALSE; - } else if (ret == NM_ACT_STAGE_RETURN_STOP) { - /* Early finish */ - priv->ip6_state = IP_DONE; - } else if (ret == NM_ACT_STAGE_RETURN_WAIT) { - /* Wait for something to try IP config again */ - priv->ip6_state = IP_WAIT; - } else - g_assert (ret == NM_ACT_STAGE_RETURN_POSTPONE); - - return TRUE; -} - -/* - * nm_device_activate_stage3_ip_config_start - * - * Begin automatic/manual IP configuration - * - */ -static gboolean -nm_device_activate_stage3_ip_config_start (gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *iface; - int ifindex; - NMDevice *master; - - /* Clear the activation source ID now that this stage has run */ - activation_source_clear (self, FALSE, 0); - - iface = nm_device_get_iface (self); - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) started...", iface); - nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); - - /* Make sure the interface is up before trying to do anything with it */ - ifindex = nm_device_get_ip_ifindex (self); - if ((ifindex > 0) && (nm_system_iface_is_up (ifindex) == FALSE)) - nm_system_iface_set_up (ifindex, TRUE, NULL); - - priv->ip4_state = priv->ip6_state = IP_WAIT; - - /* If the device is a slave, then we don't do any IP configuration but we - * use the IP config stage to indicate to the master we're ready for - * enslavement. Either the master has already enslaved us, in which case - * our state transition to SECONDARIES is already queued courtesy of - * nm_device_slave_notify_enslaved(), or the master is still activating, - * in which case we postpone activation here until the master enslaves us, - * which calls nm_device_slave_notify_enslaved(). - */ - master = nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request)); - if (master) { - if (priv->enslaved == FALSE) { - nm_log_info (LOGD_DEVICE, "Activation (%s) connection '%s' waiting on master '%s'", - nm_device_get_iface (self), - nm_connection_get_id (nm_device_get_connection (self)), - nm_device_get_iface (master)); - } - goto out; - } - - /* IPv4 */ - if (!nm_device_activate_stage3_ip4_start (self)) - goto out; - - /* IPv6 */ - if (!nm_device_activate_stage3_ip6_start (self)) - goto out; - -out: - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) complete.", iface); - return FALSE; -} - - -static void -fw_add_to_zone_cb (GError *error, gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - priv->fw_call = NULL; - - if (error) { - /* FIXME: fail the device activation? */ - } - - activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0); - - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) scheduled.", - nm_device_get_iface (self)); -} - -/* - * nm_device_activate_schedule_stage3_ip_config_start - * - * Schedule IP configuration start - */ -void -nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self) -{ - NMDevicePrivate *priv; - NMConnection *connection; - NMSettingConnection *s_con = NULL; - NMDeviceState state; - const char *zone; - - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->act_request); - - state = nm_device_get_state (self); - if (nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (priv->act_request)) == FALSE) - g_warn_if_fail (state >= NM_DEVICE_STATE_PREPARE && state <= NM_DEVICE_STATE_NEED_AUTH); - - /* Add the interface to the specified firewall zone */ - connection = nm_device_get_connection (self); - g_assert (connection); - s_con = nm_connection_get_setting_connection (connection); - - zone = nm_setting_connection_get_zone (s_con); - nm_log_dbg (LOGD_DEVICE, "Activation (%s) setting firewall zone '%s'", - nm_device_get_iface (self), zone ? zone : "default"); - priv->fw_call = nm_firewall_manager_add_or_change_zone (priv->fw_manager, - nm_device_get_ip_iface (self), - zone, - TRUE, - fw_add_to_zone_cb, - self); -} - -static NMActStageReturn -act_stage4_ip4_config_timeout (NMDevice *self, NMDeviceStateReason *reason) -{ - if (nm_device_ip_config_should_fail (self, FALSE)) { - *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; - return NM_ACT_STAGE_RETURN_FAILURE; - } - return NM_ACT_STAGE_RETURN_SUCCESS; -} - - -/* - * nm_device_activate_stage4_ip4_config_timeout - * - * Time out on retrieving the IPv4 config. - * - */ -static gboolean -nm_device_activate_ip4_config_timeout (gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *iface; - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - - /* Clear the activation source ID now that this stage has run */ - activation_source_clear (self, FALSE, AF_INET); - - iface = nm_device_get_iface (self); - nm_log_info (LOGD_DEVICE | LOGD_IP4, - "Activation (%s) Stage 4 of 5 (IPv4 Configure Timeout) started...", - iface); - - ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip4_config_timeout (self, &reason); - if (ret == NM_ACT_STAGE_RETURN_POSTPONE) - goto out; - else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - goto out; - } - g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); - - priv->ip4_state = IP_DONE; - - /* If IPv4 failed and IPv6 failed, the activation fails */ - if ((priv->ip6_state == IP_DONE) && (priv->ip6_config == NULL)) { - nm_device_state_changed (self, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } - -out: - nm_log_info (LOGD_DEVICE | LOGD_IP4, - "Activation (%s) Stage 4 of 5 (IPv4 Configure Timeout) complete.", - iface); - return FALSE; -} - - -/* - * nm_device_activate_schedule_ip4_config_timeout - * - * Deal with a timeout of the IPv4 configuration - * - */ -void -nm_device_activate_schedule_ip4_config_timeout (NMDevice *self) -{ - NMDevicePrivate *priv; - - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->act_request); - - activation_source_schedule (self, nm_device_activate_ip4_config_timeout, AF_INET); - - nm_log_info (LOGD_DEVICE | LOGD_IP4, - "Activation (%s) Stage 4 of 5 (IPv4 Configure Timeout) scheduled...", - nm_device_get_iface (self)); -} - - -static NMActStageReturn -act_stage4_ip6_config_timeout (NMDevice *self, NMDeviceStateReason *reason) -{ - if (nm_device_ip_config_should_fail (self, TRUE)) { - *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; - return NM_ACT_STAGE_RETURN_FAILURE; - } - - return NM_ACT_STAGE_RETURN_SUCCESS; -} - - -/* - * nm_device_activate_ip6_config_timeout - * - * Time out on retrieving the IPv6 config. - * - */ -static gboolean -nm_device_activate_ip6_config_timeout (gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *iface; - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - - /* Clear the activation source ID now that this stage has run */ - activation_source_clear (self, FALSE, AF_INET6); - - iface = nm_device_get_iface (self); - nm_log_info (LOGD_DEVICE | LOGD_IP6, - "Activation (%s) Stage 4 of 5 (IPv6 Configure Timeout) started...", - iface); - - ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip6_config_timeout (self, &reason); - if (ret == NM_ACT_STAGE_RETURN_POSTPONE) - goto out; - else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - goto out; - } - g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); - - priv->ip6_state = IP_DONE; - - /* If IPv6 failed and IPv4 failed, the activation fails */ - if ((priv->ip4_state == IP_DONE) && (priv->ip4_config == NULL)) { - nm_device_state_changed (self, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } - -out: - nm_log_info (LOGD_DEVICE | LOGD_IP6, - "Activation (%s) Stage 4 of 5 (IPv6 Configure Timeout) complete.", - iface); - return FALSE; -} - - -/* - * nm_device_activate_schedule_ip6_config_timeout - * - * Deal with a timeout of the IPv6 configuration - * - */ -void -nm_device_activate_schedule_ip6_config_timeout (NMDevice *self) -{ - NMDevicePrivate *priv; - - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->act_request); - - activation_source_schedule (self, nm_device_activate_ip6_config_timeout, AF_INET6); - - nm_log_info (LOGD_DEVICE | LOGD_IP6, - "Activation (%s) Stage 4 of 5 (IPv6 Configure Timeout) scheduled...", - nm_device_get_iface (self)); -} - -static void -share_child_setup (gpointer user_data G_GNUC_UNUSED) -{ - /* We are in the child process at this point */ - pid_t pid = getpid (); - setpgid (pid, pid); - - nm_unblock_posix_signals (NULL); -} - -static gboolean -share_init (void) -{ - int status; - char *modules[] = { "ip_tables", "iptable_nat", "nf_nat_ftp", "nf_nat_irc", - "nf_nat_sip", "nf_nat_tftp", "nf_nat_pptp", "nf_nat_h323", - NULL }; - char **iter; - - if (!nm_utils_do_sysctl ("/proc/sys/net/ipv4/ip_forward", "1")) { - nm_log_err (LOGD_SHARING, "Error starting IP forwarding: (%d) %s", - errno, strerror (errno)); - return FALSE; - } - - if (!nm_utils_do_sysctl ("/proc/sys/net/ipv4/ip_dynaddr", "1")) { - nm_log_err (LOGD_SHARING, "error starting IP forwarding: (%d) %s", - errno, strerror (errno)); - } - - for (iter = modules; *iter; iter++) { - char *argv[3] = { "/sbin/modprobe", *iter, NULL }; - char *envp[1] = { NULL }; - GError *error = NULL; - - if (!g_spawn_sync ("/", argv, envp, G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, - share_child_setup, NULL, NULL, NULL, &status, &error)) { - nm_log_err (LOGD_SHARING, "error loading NAT module %s: (%d) %s", - *iter, error ? error->code : 0, - (error && error->message) ? error->message : "unknown"); - if (error) - g_error_free (error); - } - } - - return TRUE; -} - -static void -add_share_rule (NMActRequest *req, const char *table, const char *fmt, ...) -{ - va_list args; - char *cmd; - - va_start (args, fmt); - cmd = g_strdup_vprintf (fmt, args); - va_end (args); - - nm_act_request_add_share_rule (req, table, cmd); - g_free (cmd); -} - -static gboolean -start_sharing (NMDevice *self, NMIP4Config *config) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActRequest *req; - GError *error = NULL; - char str_addr[INET_ADDRSTRLEN + 1]; - char str_mask[INET_ADDRSTRLEN + 1]; - guint32 netmask, network; - NMIP4Address *ip4_addr; - const char *ip_iface; - - g_return_val_if_fail (config != NULL, FALSE); - - ip_iface = nm_device_get_ip_iface (self); - - ip4_addr = nm_ip4_config_get_address (config, 0); - if (!ip4_addr || !nm_ip4_address_get_address (ip4_addr)) - return FALSE; - - netmask = nm_utils_ip4_prefix_to_netmask (nm_ip4_address_get_prefix (ip4_addr)); - if (!inet_ntop (AF_INET, &netmask, str_mask, sizeof (str_mask))) - return FALSE; - - network = nm_ip4_address_get_address (ip4_addr) & netmask; - if (!inet_ntop (AF_INET, &network, str_addr, sizeof (str_addr))) - return FALSE; - - if (!share_init ()) - return FALSE; - - req = nm_device_get_act_request (self); - g_assert (req); - - add_share_rule (req, "filter", "INPUT --in-interface %s --protocol tcp --destination-port 53 --jump ACCEPT", ip_iface); - add_share_rule (req, "filter", "INPUT --in-interface %s --protocol udp --destination-port 53 --jump ACCEPT", ip_iface); - add_share_rule (req, "filter", "INPUT --in-interface %s --protocol tcp --destination-port 67 --jump ACCEPT", ip_iface); - add_share_rule (req, "filter", "INPUT --in-interface %s --protocol udp --destination-port 67 --jump ACCEPT", ip_iface); - add_share_rule (req, "filter", "FORWARD --in-interface %s --jump REJECT", ip_iface); - add_share_rule (req, "filter", "FORWARD --out-interface %s --jump REJECT", ip_iface); - add_share_rule (req, "filter", "FORWARD --in-interface %s --out-interface %s --jump ACCEPT", ip_iface, ip_iface); - add_share_rule (req, "filter", "FORWARD --source %s/%s --in-interface %s --jump ACCEPT", str_addr, str_mask, ip_iface); - add_share_rule (req, "filter", "FORWARD --destination %s/%s --out-interface %s --match state --state ESTABLISHED,RELATED --jump ACCEPT", str_addr, str_mask, ip_iface); - add_share_rule (req, "nat", "POSTROUTING --source %s/%s ! --destination %s/%s --jump MASQUERADE", str_addr, str_mask, str_addr, str_mask); - - nm_act_request_set_shared (req, TRUE); - - if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, config, &error)) { - nm_log_err (LOGD_SHARING, "(%s/%s): failed to start dnsmasq: %s", - nm_device_get_iface (self), ip_iface, - (error && error->message) ? error->message : "(unknown)"); - g_error_free (error); - nm_act_request_set_shared (req, FALSE); - return FALSE; - } - - priv->dnsmasq_state_id = g_signal_connect (priv->dnsmasq_manager, "state-changed", - G_CALLBACK (dnsmasq_state_changed_cb), - self); - return TRUE; -} - -static gboolean -nm_device_activate_ip4_config_commit (gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActRequest *req; - NMIP4Config *config = NULL; - const char *iface, *method = NULL; - NMConnection *connection; - NMSettingIP4Config *s_ip4; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - gboolean assumed; - int ifindex; - - /* Clear the activation source ID now that this stage has run */ - activation_source_clear (self, FALSE, AF_INET); - - iface = nm_device_get_iface (self); - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IPv4 Commit) started...", - iface); - - req = nm_device_get_act_request (self); - g_assert (req); - connection = nm_act_request_get_connection (req); - g_assert (connection); - - config = g_object_get_data (G_OBJECT (req), PENDING_IP4_CONFIG); - g_assert (config); - - /* Make sure the interface is up again just because */ - ifindex = nm_device_get_ip_ifindex (self); - if ((ifindex > 0) && (nm_system_iface_is_up (ifindex) == FALSE)) - nm_system_iface_set_up (ifindex, TRUE, NULL); - - /* Allow setting MTU etc */ - if (NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit) - NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, config); - - /* Merge with user overrides */ - nm_utils_merge_ip4_config (config, nm_connection_get_setting_ip4_config (connection)); - - assumed = nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (priv->act_request)); - if (!nm_device_set_ip4_config (self, config, assumed, &reason)) { - nm_log_info (LOGD_DEVICE | LOGD_IP4, - "Activation (%s) Stage 5 of 5 (IPv4 Commit) failed", - iface); - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - goto out; - } - - /* Start IPv4 sharing if we need it */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (s_ip4) - method = nm_setting_ip4_config_get_method (s_ip4); - - if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) { - if (!start_sharing (self, config)) { - nm_log_warn (LOGD_SHARING, "Activation (%s) Stage 5 of 5 (IPv4 Commit) start sharing failed.", iface); - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED); - goto out; - } - } - - /* Enter the SECONDARIES state if this is the first method to complete */ - priv->ip4_state = IP_DONE; - if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG) - nm_device_state_changed (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE); - -out: - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IPv4 Commit) complete.", - iface); - - /* Balance IP config creation; nm_device_set_ip4_config() takes a reference */ - g_object_set_data (G_OBJECT (req), PENDING_IP4_CONFIG, NULL); - - return FALSE; -} - -void -nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *config) -{ - NMDevicePrivate *priv; - - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->act_request); - - if (config == NULL) { - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return; - } - - g_object_set_data_full (G_OBJECT (priv->act_request), - PENDING_IP4_CONFIG, - g_object_ref (config), - g_object_unref); - - activation_source_schedule (self, nm_device_activate_ip4_config_commit, AF_INET); - - nm_log_info (LOGD_DEVICE | LOGD_IP4, - "Activation (%s) Stage 5 of 5 (IPv4 Configure Commit) scheduled...", - nm_device_get_iface (self)); -} - -gboolean -nm_device_activate_ip4_state_in_conf (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_CONF; -} - -gboolean -nm_device_activate_ip4_state_in_wait (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_WAIT; -} - -static gboolean -nm_device_activate_ip6_config_commit (gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActRequest *req; - NMIP6Config *config = NULL; - const char *iface; - NMConnection *connection; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - int ifindex; - - /* Clear the activation source ID now that this stage has run */ - activation_source_clear (self, FALSE, AF_INET6); - - iface = nm_device_get_iface (self); - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IPv6 Commit) started...", - iface); - - req = nm_device_get_act_request (self); - g_assert (req); - connection = nm_act_request_get_connection (req); - g_assert (connection); - - config = g_object_get_data (G_OBJECT (req), PENDING_IP6_CONFIG); - g_assert (config); - - /* Make sure the interface is up again just because */ - ifindex = nm_device_get_ip_ifindex (self); - if ((ifindex > 0) && (nm_system_iface_is_up (ifindex) == FALSE)) - nm_system_iface_set_up (ifindex, TRUE, NULL); - - /* Allow setting MTU etc */ - if (NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit) - NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit (self, config); - - if (ip6_config_merge_and_apply (self, config, &reason)) { - /* Enter the SECONDARIES state if this is the first method to complete */ - priv->ip6_state = IP_DONE; - if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG) - nm_device_state_changed (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE); - } else { - nm_log_info (LOGD_DEVICE | LOGD_IP6, - "Activation (%s) Stage 5 of 5 (IPv6 Commit) failed", - iface); - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - } - - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IPv6 Commit) complete.", - iface); - - /* Balance IP config creation; nm_device_set_ip6_config() takes a reference */ - g_object_set_data (G_OBJECT (req), PENDING_IP6_CONFIG, NULL); - - return FALSE; -} - -void -nm_device_activate_schedule_ip6_config_result (NMDevice *self, NMIP6Config *config) -{ - NMDevicePrivate *priv; - - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->act_request); - - if (config == NULL) { - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return; - } - - /* Save the pending config */ - g_object_set_data_full (G_OBJECT (priv->act_request), - PENDING_IP6_CONFIG, - g_object_ref (config), - g_object_unref); - - activation_source_schedule (self, nm_device_activate_ip6_config_commit, AF_INET6); - - nm_log_info (LOGD_DEVICE | LOGD_IP4, - "Activation (%s) Stage 5 of 5 (IPv6 Commit) scheduled...", - nm_device_get_iface (self)); -} - -gboolean -nm_device_activate_ip6_state_in_conf (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - return NM_DEVICE_GET_PRIVATE (self)->ip6_state == IP_CONF; -} - -gboolean -nm_device_activate_ip6_state_in_wait (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - return NM_DEVICE_GET_PRIVATE (self)->ip6_state == IP_WAIT; -} - -static void -clear_act_request (NMDevice *self) -{ - NMDevicePrivate * priv; - - g_return_if_fail (self != NULL); - - priv = NM_DEVICE_GET_PRIVATE (self); - - if (!priv->act_request) - return; - - if (priv->secrets_updated_id) { - g_signal_handler_disconnect (priv->act_request, - priv->secrets_updated_id); - priv->secrets_updated_id = 0; - } - - if (priv->secrets_failed_id) { - g_signal_handler_disconnect (priv->act_request, - priv->secrets_failed_id); - priv->secrets_failed_id = 0; - } - - nm_active_connection_set_default (NM_ACTIVE_CONNECTION (priv->act_request), FALSE); - - g_clear_object (&priv->act_request); - g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION); -} - -static void -dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->dhcp4_config) { - g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG); - g_object_unref (priv->dhcp4_config); - priv->dhcp4_config = NULL; - } - - if (priv->dhcp4_client) { - /* Stop any ongoing DHCP transaction on this device */ - if (priv->dhcp4_state_sigid) { - g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_state_sigid); - priv->dhcp4_state_sigid = 0; - } - - if (priv->dhcp4_timeout_sigid) { - g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_timeout_sigid); - priv->dhcp4_timeout_sigid = 0; - } - - if (stop) - nm_dhcp_client_stop (priv->dhcp4_client, release); - - g_object_unref (priv->dhcp4_client); - priv->dhcp4_client = NULL; - } -} - -static void -dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - priv->dhcp6_mode = IP6_DHCP_OPT_NONE; - - if (priv->dhcp6_ip6_config) { - g_object_unref (priv->dhcp6_ip6_config); - priv->dhcp6_ip6_config = NULL; - } - - if (priv->dhcp6_config) { - g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG); - g_object_unref (priv->dhcp6_config); - priv->dhcp6_config = NULL; - } - - if (priv->dhcp6_client) { - if (priv->dhcp6_state_sigid) { - g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_state_sigid); - priv->dhcp6_state_sigid = 0; - } - - if (priv->dhcp6_timeout_sigid) { - g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_timeout_sigid); - priv->dhcp6_timeout_sigid = 0; - } - - if (stop) - nm_dhcp_client_stop (priv->dhcp6_client, release); - - g_object_unref (priv->dhcp6_client); - priv->dhcp6_client = NULL; - } -} - -static void -dnsmasq_cleanup (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (!priv->dnsmasq_manager) - return; - - if (priv->dnsmasq_state_id) { - g_signal_handler_disconnect (priv->dnsmasq_manager, priv->dnsmasq_state_id); - priv->dnsmasq_state_id = 0; - } - - nm_dnsmasq_manager_stop (priv->dnsmasq_manager); - g_object_unref (priv->dnsmasq_manager); - priv->dnsmasq_manager = NULL; -} - -static void -_update_ip4_address (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - struct ifreq req; - guint32 new_address; - int fd; - - g_return_if_fail (self != NULL); - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - nm_log_err (LOGD_IP4, "couldn't open control socket."); - return; - } - - memset (&req, 0, sizeof (struct ifreq)); - strncpy (req.ifr_name, nm_device_get_ip_iface (self), IFNAMSIZ); - if (ioctl (fd, SIOCGIFADDR, &req) == 0) { - new_address = ((struct sockaddr_in *)(&req.ifr_addr))->sin_addr.s_addr; - if (new_address != priv->ip4_address) - priv->ip4_address = new_address; - } - close (fd); -} - -/* - * nm_device_deactivate - * - * Remove a device's routing table entries and IP address. - * - */ -static void -nm_device_deactivate (NMDevice *self, NMDeviceStateReason reason) -{ - NMDevicePrivate *priv; - NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; - NMConnection *connection = NULL; - NMSettingConnection *s_con = NULL; - gboolean tried_ipv6 = FALSE; - int ifindex, family; - - g_return_if_fail (self != NULL); - g_return_if_fail (NM_IS_DEVICE (self)); - - nm_log_info (LOGD_DEVICE, "(%s): deactivating device (reason '%s') [%d]", - nm_device_get_iface (self), reason_to_string (reason), reason); - - /* Save whether or not we tried IPv6 for later */ - priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->ip6_manager || priv->ip6_config) - tried_ipv6 = TRUE; - - /* Clean up when device was deactivated during call to firewall */ - if (priv->fw_call) { - nm_firewall_manager_cancel_call (priv->fw_manager, priv->fw_call); - priv->fw_call = NULL; - } - - if (priv->act_request) - connection = nm_act_request_get_connection (priv->act_request); - if (connection) { - s_con = nm_connection_get_setting_connection (connection); - nm_firewall_manager_remove_from_zone (priv->fw_manager, - nm_device_get_ip_iface (self), - nm_setting_connection_get_zone (s_con)); - } - - /* Break the activation chain */ - activation_source_clear (self, TRUE, AF_INET); - activation_source_clear (self, TRUE, AF_INET6); - - /* Clear any queued transitions */ - nm_device_queued_state_clear (self); - - priv->ip4_state = priv->ip6_state = IP_NONE; - - dhcp4_cleanup (self, TRUE, FALSE); - dhcp6_cleanup (self, TRUE, FALSE); - addrconf6_cleanup (self); - dnsmasq_cleanup (self); - aipd_cleanup (self); - - /* Turn off router advertisements until they are needed */ - if (priv->ip6_accept_ra_path) - nm_utils_do_sysctl (priv->ip6_accept_ra_path, "0"); - - /* Turn off IPv6 privacy extensions */ - if (priv->ip6_privacy_tempaddr_path) - nm_utils_do_sysctl (priv->ip6_privacy_tempaddr_path, "0"); - - /* Call device type-specific deactivation */ - if (NM_DEVICE_GET_CLASS (self)->deactivate) - NM_DEVICE_GET_CLASS (self)->deactivate (self); - - /* master: release slaves */ - nm_device_master_release_slaves (self, FALSE); - - /* slave: mark no longer enslaved */ - g_clear_object (&priv->master); - priv->enslaved = FALSE; - - /* Tear down an existing activation request */ - clear_act_request (self); - - /* Take out any entries in the routing table and any IP address the device had. */ - ifindex = nm_device_get_ip_ifindex (self); - family = tried_ipv6 ? AF_UNSPEC : AF_INET; - if (ifindex > 0) { - nm_system_iface_flush_routes (ifindex, family); - nm_system_iface_flush_addresses (ifindex, family); - } - - /* Clean up nameservers and addresses */ - nm_device_set_ip4_config (self, NULL, FALSE, &ignored); - nm_device_set_ip6_config (self, NULL, &ignored); - - /* Clear legacy IPv4 address property */ - priv->ip4_address = 0; - g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_ADDRESS); - - /* Only clear ip_iface after flushing all routes and addreses, since - * those are identified by ip_iface, not by iface (which might be a tty - * or ATM device). - */ - nm_device_set_ip_iface (self, NULL); -} - -static void -disconnect_cb (NMDevice *device, - DBusGMethodInvocation *context, - GError *error, - gpointer user_data) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - GError *local = NULL; - - if (error) - dbus_g_method_return_error (context, error); - else { - /* Authorized */ - if (priv->state <= NM_DEVICE_STATE_DISCONNECTED) { - local = g_error_new_literal (NM_DEVICE_ERROR, - NM_DEVICE_ERROR_NOT_ACTIVE, - "Device is not active"); - dbus_g_method_return_error (context, local); - g_error_free (local); - } else { - priv->autoconnect = FALSE; - nm_device_state_changed (device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_USER_REQUESTED); - dbus_g_method_return (context); - } - } -} - -static void -impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context) -{ - NMConnection *connection; - GError *error = NULL; - - if (NM_DEVICE_GET_PRIVATE (device)->act_request == NULL) { - error = g_error_new_literal (NM_DEVICE_ERROR, - NM_DEVICE_ERROR_NOT_ACTIVE, - "This device is not active"); - dbus_g_method_return_error (context, error); - g_error_free (error); - return; - } - - connection = nm_device_get_connection (device); - g_assert (connection); - - /* Ask the manager to authenticate this request for us */ - g_signal_emit (device, signals[AUTH_REQUEST], 0, - context, - connection, - NM_AUTH_PERMISSION_NETWORK_CONTROL, - TRUE, - disconnect_cb, - NULL); -} - -void -nm_device_activate (NMDevice *self, NMActRequest *req) -{ - NMDevicePrivate *priv; - NMConnection *connection; - - g_return_if_fail (self != NULL); - g_return_if_fail (NM_IS_DEVICE (self)); - g_return_if_fail (req != NULL); - g_return_if_fail (NM_IS_ACT_REQUEST (req)); - - priv = NM_DEVICE_GET_PRIVATE (self); - - connection = nm_act_request_get_connection (req); - g_assert (connection); - - nm_log_info (LOGD_DEVICE, "Activation (%s) starting connection '%s'", - nm_device_get_iface (self), - nm_connection_get_id (connection)); - - g_warn_if_fail (priv->state == NM_DEVICE_STATE_DISCONNECTED); - - priv->act_request = g_object_ref (req); - g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION); - - if (nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (req))) { - /* If it's an assumed connection, let the device subclass short-circuit - * the normal connection process and just copy its IP configs from the - * interface. - */ - nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); - nm_device_activate_schedule_stage3_ip_config_start (self); - } else { - NMDevice *master; - - /* HACK: update the state a bit early to avoid a race between the - * scheduled stage1 handler and nm_policy_device_change_check() thinking - * that the activation request isn't deferred because the deferred bit - * gets cleared a bit too early, when the connection becomes valid. - */ - nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); - - /* Handle any dependencies this connection might have */ - master = nm_active_connection_get_master (NM_ACTIVE_CONNECTION (req)); - if (master) { - /* Master should at least already be activating */ - g_assert (nm_device_get_state (master) > NM_DEVICE_STATE_DISCONNECTED); - - g_assert (priv->master == NULL); - priv->master = g_object_ref (master); - nm_device_master_add_slave (master, self); - } - - nm_device_activate_schedule_stage1_device_prepare (self); - } -} - -/* - * nm_device_is_activating - * - * Return whether or not the device is currently activating itself. - * - */ -gboolean -nm_device_is_activating (NMDevice *device) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - NMDeviceState state; - - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - - state = nm_device_get_state (device); - if (state >= NM_DEVICE_STATE_PREPARE && state <= NM_DEVICE_STATE_SECONDARIES) - return TRUE; - - /* There's a small race between the time when stage 1 is scheduled - * and when the device actually sets STATE_PREPARE when the activation - * handler is actually run. If there's an activation handler scheduled - * we're activating anyway. - */ - return priv->act_source_id ? TRUE : FALSE; -} - - -gboolean -nm_device_can_interrupt_activation (NMDevice *self) -{ - gboolean interrupt = FALSE; - - g_return_val_if_fail (self != NULL, FALSE); - - if (NM_DEVICE_GET_CLASS (self)->can_interrupt_activation) - interrupt = NM_DEVICE_GET_CLASS (self)->can_interrupt_activation (self); - return interrupt; -} - -/* IP Configuration stuff */ - -NMDHCP4Config * -nm_device_get_dhcp4_config (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - - return NM_DEVICE_GET_PRIVATE (self)->dhcp4_config; -} - -NMIP4Config * -nm_device_get_ip4_config (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - - return NM_DEVICE_GET_PRIVATE (self)->ip4_config; -} - - -static gboolean -nm_device_set_ip4_config (NMDevice *self, - NMIP4Config *new_config, - gboolean assumed, - NMDeviceStateReason *reason) -{ - NMDevicePrivate *priv; - const char *ip_iface; - NMIP4Config *old_config = NULL; - gboolean success = TRUE; - NMIP4ConfigCompareFlags diff = NM_IP4_COMPARE_FLAG_ALL; - int ip_ifindex; - - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - g_return_val_if_fail (reason != NULL, FALSE); - - priv = NM_DEVICE_GET_PRIVATE (self); - ip_iface = nm_device_get_ip_iface (self); - ip_ifindex = nm_device_get_ip_ifindex (self); - - old_config = priv->ip4_config; - - if (new_config && old_config) { - diff = nm_ip4_config_diff (new_config, old_config); - if (diff == NM_IP4_COMPARE_FLAG_NONE) - return TRUE; /* no actual change */ - } else if (!new_config && !old_config) - return TRUE; - - priv->ip4_config = NULL; - - if (new_config) { - priv->ip4_config = g_object_ref (new_config); - - /* Don't touch the device's actual IP config if the connection is - * assumed when NM starts. - */ - if (!assumed) - success = nm_system_apply_ip4_config (ip_ifindex, new_config, nm_device_get_priority (self), diff); - - if (success || assumed) { - /* Export over D-Bus */ - if (!nm_ip4_config_get_dbus_path (new_config)) - nm_ip4_config_export (new_config); - _update_ip4_address (self); - } - - if (!success && reason) - *reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED; - } - - g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_CONFIG); - g_signal_emit (self, signals[IP4_CONFIG_CHANGED], 0, priv->ip4_config, old_config); - - if (old_config) - g_object_unref (old_config); - - return success; -} - -static gboolean -nm_device_set_ip6_config (NMDevice *self, - NMIP6Config *new_config, - NMDeviceStateReason *reason) -{ - NMDevicePrivate *priv; - const char *ip_iface; - NMIP6Config *old_config = NULL; - gboolean success = TRUE; - NMIP6ConfigCompareFlags diff = NM_IP6_COMPARE_FLAG_ALL; - int ip_ifindex; - - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - g_return_val_if_fail (reason != NULL, FALSE); - - priv = NM_DEVICE_GET_PRIVATE (self); - ip_iface = nm_device_get_ip_iface (self); - ip_ifindex = nm_device_get_ip_ifindex (self); - - old_config = priv->ip6_config; - - if (new_config && old_config) { - diff = nm_ip6_config_diff (new_config, old_config); - if (diff == NM_IP6_COMPARE_FLAG_NONE) - return TRUE; /* no actual change */ - } else if (!new_config && !old_config) - return TRUE; - - priv->ip6_config = NULL; - - if (new_config) { - priv->ip6_config = g_object_ref (new_config); - - success = nm_system_apply_ip6_config (ip_ifindex, new_config, nm_device_get_priority (self), diff); - - if (success) { - /* Export over D-Bus */ - if (!nm_ip6_config_get_dbus_path (new_config)) - nm_ip6_config_export (new_config); - } - - if (!success && reason) - *reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED; - } - - g_object_notify (G_OBJECT (self), NM_DEVICE_IP6_CONFIG); - g_signal_emit (self, signals[IP6_CONFIG_CHANGED], 0, priv->ip6_config, old_config); - - if (old_config) - g_object_unref (old_config); - - return success; -} - -NMDHCP6Config * -nm_device_get_dhcp6_config (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - - return NM_DEVICE_GET_PRIVATE (self)->dhcp6_config; -} - -NMIP6Config * -nm_device_get_ip6_config (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - - return NM_DEVICE_GET_PRIVATE (self)->ip6_config; -} - -static gboolean -nm_device_is_up (NMDevice *self) -{ - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - - if (NM_DEVICE_GET_CLASS (self)->is_up) - return NM_DEVICE_GET_CLASS (self)->is_up (self); - - return TRUE; -} - -gboolean -nm_device_hw_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) -{ - gboolean success; - guint32 tries = 0; - - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - - if (nm_device_hw_is_up (self)) - goto out; - - nm_log_info (LOGD_HW, "(%s): bringing up device.", nm_device_get_iface (self)); - - if (NM_DEVICE_GET_CLASS (self)->hw_bring_up) { - success = NM_DEVICE_GET_CLASS (self)->hw_bring_up (self, no_firmware); - if (!success) - return FALSE; - } - - /* Wait for the device to come up if requested */ - while (block && !nm_device_hw_is_up (self) && (tries++ < 50)) - g_usleep (200); - - if (!nm_device_hw_is_up (self)) { - nm_log_warn (LOGD_HW, "(%s): device not up after timeout!", nm_device_get_iface (self)); - return FALSE; - } - -out: - /* Can only get HW address of some devices when they are up */ - if (NM_DEVICE_GET_CLASS (self)->update_hw_address) - NM_DEVICE_GET_CLASS (self)->update_hw_address (self); - - _update_ip4_address (self); - return TRUE; -} - -static gboolean -hw_bring_up (NMDevice *device, gboolean *no_firmware) -{ - int ifindex = nm_device_get_ip_ifindex (device); - - return ifindex > 0 ? nm_system_iface_set_up (ifindex, TRUE, no_firmware) : TRUE; -} - -void -nm_device_hw_take_down (NMDevice *self, gboolean block) -{ - guint32 tries = 0; - - g_return_if_fail (NM_IS_DEVICE (self)); - - if (!nm_device_hw_is_up (self)) - return; - - nm_log_info (LOGD_HW, "(%s): taking down device.", nm_device_get_iface (self)); - - if (NM_DEVICE_GET_CLASS (self)->hw_take_down) - NM_DEVICE_GET_CLASS (self)->hw_take_down (self); - - /* Wait for the device to come up if requested */ - while (block && nm_device_hw_is_up (self) && (tries++ < 50)) - g_usleep (200); -} - -static void -hw_take_down (NMDevice *device) -{ - int ifindex = nm_device_get_ip_ifindex (device); - - if (ifindex > 0) - nm_system_iface_set_up (ifindex, FALSE, NULL); -} - -static gboolean -nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) -{ - gboolean success = FALSE; - - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - - if (!nm_device_hw_bring_up (self, block, no_firmware)) - return FALSE; - - if (nm_device_is_up (self)) - return TRUE; - - nm_log_info (LOGD_HW, "(%s): preparing device.", nm_device_get_iface (self)); - - if (NM_DEVICE_GET_CLASS (self)->bring_up) - success = NM_DEVICE_GET_CLASS (self)->bring_up (self); - - return success; -} - -static void -nm_device_take_down (NMDevice *self, gboolean block, NMDeviceStateReason reason) -{ - g_return_if_fail (NM_IS_DEVICE (self)); - - if (nm_device_get_act_request (self)) - nm_device_deactivate (self, reason); - - if (nm_device_is_up (self)) { - nm_log_info (LOGD_HW, "(%s): cleaning up...", nm_device_get_iface (self)); - - if (NM_DEVICE_GET_CLASS (self)->take_down) - NM_DEVICE_GET_CLASS (self)->take_down (self); - } - - nm_device_hw_take_down (self, block); -} - -static void -dispose (GObject *object) -{ - NMDevice *self = NM_DEVICE (object); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean take_down = TRUE; - - if (priv->disposed || !priv->initialized) - goto out; - - priv->disposed = TRUE; - - /* Don't down can-assume-connection capable devices that are activated with - * a connection that can be assumed. - */ - if (nm_device_can_assume_connections (self) && (priv->state == NM_DEVICE_STATE_ACTIVATED)) { - NMConnection *connection; - NMSettingIP4Config *s_ip4 = NULL; - const char *method = NULL; - - connection = nm_device_get_connection (self); - if (connection) { - /* Only static or DHCP IPv4 connections can be left up. - * All IPv6 connections can be left up, so we don't have - * to check that. - */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (s_ip4) - method = nm_setting_ip4_config_get_method (s_ip4); - if ( !method - || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) - || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) - || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) - take_down = FALSE; - } - } - - /* Clear any queued transitions */ - nm_device_queued_state_clear (self); - - /* Clean up and stop DHCP */ - dhcp4_cleanup (self, take_down, FALSE); - dhcp6_cleanup (self, take_down, FALSE); - addrconf6_cleanup (self); - dnsmasq_cleanup (self); - - g_warn_if_fail (priv->slaves == NULL); - - /* Take the device itself down and clear its IPv4 configuration */ - if (priv->managed && take_down) { - NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; - - nm_device_take_down (self, FALSE, NM_DEVICE_STATE_REASON_REMOVED); - nm_device_set_ip4_config (self, NULL, FALSE, &ignored); - } - - /* reset the saved RA value */ - if ( priv->ip6_accept_ra_path - && g_file_test (priv->ip6_accept_ra_path, G_FILE_TEST_EXISTS)) { - nm_utils_do_sysctl (priv->ip6_accept_ra_path, - priv->ip6_accept_ra_save ? "1" : "0"); - } - g_free (priv->ip6_accept_ra_path); - - /* reset the saved use_tempaddr value */ - if ( priv->ip6_privacy_tempaddr_path - && g_file_test (priv->ip6_privacy_tempaddr_path, G_FILE_TEST_EXISTS)) { - char tmp[16]; - - snprintf (tmp, sizeof (tmp), "%d", priv->ip6_privacy_tempaddr_save); - nm_utils_do_sysctl (priv->ip6_privacy_tempaddr_path, tmp); - } - g_free (priv->ip6_privacy_tempaddr_path); - - if (priv->cp_added_id) { - g_signal_handler_disconnect (priv->con_provider, priv->cp_added_id); - priv->cp_added_id = 0; - } - - if (priv->cp_loaded_id) { - g_signal_handler_disconnect (priv->con_provider, priv->cp_loaded_id); - priv->cp_loaded_id = 0; - } - - if (priv->cp_removed_id) { - g_signal_handler_disconnect (priv->con_provider, priv->cp_removed_id); - priv->cp_removed_id = 0; - } - - if (priv->cp_updated_id) { - g_signal_handler_disconnect (priv->con_provider, priv->cp_updated_id); - priv->cp_updated_id = 0; - } - - g_hash_table_unref (priv->available_connections); - - activation_source_clear (self, TRUE, AF_INET); - activation_source_clear (self, TRUE, AF_INET6); - - clear_act_request (self); - -out: - G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); -} - -static void -finalize (GObject *object) -{ - NMDevice *self = NM_DEVICE (object); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->dhcp_manager) - g_object_unref (priv->dhcp_manager); - - if (priv->fw_manager) - g_object_unref (priv->fw_manager); - - g_free (priv->udi); - g_free (priv->path); - g_free (priv->iface); - g_free (priv->ip_iface); - g_free (priv->driver); - g_free (priv->driver_version); - g_free (priv->firmware_version); - g_free (priv->type_desc); - if (priv->dhcp_anycast_address) - g_byte_array_free (priv->dhcp_anycast_address, TRUE); - - G_OBJECT_CLASS (nm_device_parent_class)->finalize (object); -} - - -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object); - - switch (prop_id) { - case PROP_UDI: - /* Only virtual interfaces can set UDI post-construction */ - if (priv->initialized) - g_return_if_fail (nm_system_get_iface_type (priv->ifindex, NULL) != NM_IFACE_TYPE_UNSPEC); - - g_free (priv->udi); - priv->udi = g_strdup (g_value_get_string (value)); - break; - case PROP_IFACE: - g_free (priv->iface); - priv->ifindex = 0; - priv->iface = g_value_dup_string (value); - - /* Only look up the ifindex if it appears to be an actual kernel - * interface name. eg Bluetooth devices won't have one until we know - * the IP interface. - */ - if (priv->iface && !strchr (priv->iface, ':')) { - priv->ifindex = nm_netlink_iface_to_index (priv->iface); - if (priv->ifindex <= 0) - nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", priv->iface); - } - break; - case PROP_IP_IFACE: - break; - case PROP_DRIVER: - g_free (priv->driver); - priv->driver = g_strdup (g_value_get_string (value)); - break; - case PROP_DRIVER_VERSION: - g_free (priv->driver_version); - priv->driver_version = g_strdup (g_value_get_string (value)); - break; - case PROP_FIRMWARE_VERSION: - g_free (priv->firmware_version); - priv->firmware_version = g_strdup (g_value_get_string (value)); - break; - case PROP_CAPABILITIES: - priv->capabilities = g_value_get_uint (value); - break; - case PROP_IP4_ADDRESS: - priv->ip4_address = g_value_get_uint (value); - break; - case PROP_MANAGED: - priv->managed = g_value_get_boolean (value); - break; - case PROP_AUTOCONNECT: - priv->autoconnect = g_value_get_boolean (value); - break; - case PROP_FIRMWARE_MISSING: - priv->firmware_missing = g_value_get_boolean (value); - break; - case PROP_DEVICE_TYPE: - g_return_if_fail (priv->type == NM_DEVICE_TYPE_UNKNOWN); - priv->type = g_value_get_uint (value); - break; - case PROP_TYPE_DESC: - g_free (priv->type_desc); - priv->type_desc = g_value_dup_string (value); - break; - case PROP_RFKILL_TYPE: - priv->rfkill_type = g_value_get_uint (value); - break; - case PROP_IS_MASTER: - priv->is_master = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static gboolean -_is_connected (NMDeviceState state) -{ - return (state >= NM_DEVICE_STATE_IP_CONFIG && state <= NM_DEVICE_STATE_DEACTIVATING); -} - -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - NMDevice *self = NM_DEVICE (object); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMDeviceState state; - const char *ac_path = NULL; - GPtrArray *array; - GHashTableIter iter; - NMConnection *connection; - - state = nm_device_get_state (self); - - switch (prop_id) { - case PROP_UDI: - g_value_set_string (value, priv->udi); - break; - case PROP_IFACE: - g_value_set_string (value, priv->iface); - break; - case PROP_IP_IFACE: - if (_is_connected (state)) - g_value_set_string (value, nm_device_get_ip_iface (self)); - else - g_value_set_string (value, NULL); - break; - case PROP_IFINDEX: - g_value_set_int (value, priv->ifindex); - break; - case PROP_DRIVER: - g_value_set_string (value, priv->driver); - break; - case PROP_DRIVER_VERSION: - g_value_set_string (value, priv->driver_version); - break; - case PROP_FIRMWARE_VERSION: - g_value_set_string (value, priv->firmware_version); - break; - case PROP_CAPABILITIES: - g_value_set_uint (value, priv->capabilities); - break; - case PROP_IP4_ADDRESS: - g_value_set_uint (value, priv->ip4_address); - break; - case PROP_IP4_CONFIG: - if (_is_connected (state) && priv->ip4_config) - g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config)); - else - g_value_set_boxed (value, "/"); - break; - case PROP_DHCP4_CONFIG: - if (_is_connected (state) && priv->dhcp4_client) - g_value_set_boxed (value, nm_dhcp4_config_get_dbus_path (priv->dhcp4_config)); - else - g_value_set_boxed (value, "/"); - break; - case PROP_IP6_CONFIG: - if (_is_connected (state) && priv->ip6_config) - g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config)); - else - g_value_set_boxed (value, "/"); - break; - case PROP_DHCP6_CONFIG: - if (_is_connected (state) && priv->dhcp6_client) - g_value_set_boxed (value, nm_dhcp6_config_get_dbus_path (priv->dhcp6_config)); - else - g_value_set_boxed (value, "/"); - break; - case PROP_STATE: - g_value_set_uint (value, priv->state); - break; - case PROP_STATE_REASON: - g_value_take_boxed (value, dbus_g_type_specialized_construct (DBUS_G_TYPE_UINT_STRUCT)); - dbus_g_type_struct_set (value, - 0, priv->state, - 1, priv->state_reason, - G_MAXUINT); - break; - case PROP_ACTIVE_CONNECTION: - if (priv->act_request) - ac_path = nm_active_connection_get_path (NM_ACTIVE_CONNECTION (priv->act_request)); - g_value_set_boxed (value, ac_path ? ac_path : "/"); - break; - case PROP_DEVICE_TYPE: - g_value_set_uint (value, priv->type); - break; - case PROP_MANAGED: - g_value_set_boolean (value, priv->managed); - break; - case PROP_AUTOCONNECT: - g_value_set_boolean (value, priv->autoconnect); - break; - case PROP_FIRMWARE_MISSING: - g_value_set_boolean (value, priv->firmware_missing); - break; - case PROP_TYPE_DESC: - g_value_set_string (value, priv->type_desc); - break; - case PROP_RFKILL_TYPE: - g_value_set_uint (value, priv->rfkill_type); - break; - case PROP_AVAILABLE_CONNECTIONS: - array = g_ptr_array_sized_new (g_hash_table_size (priv->available_connections)); - g_hash_table_iter_init (&iter, priv->available_connections); - while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL)) - g_ptr_array_add (array, g_strdup (nm_connection_get_path (connection))); - g_value_take_boxed (value, array); - break; - case PROP_IS_MASTER: - g_value_set_boolean (value, priv->is_master); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - -static void -nm_device_class_init (NMDeviceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (object_class, sizeof (NMDevicePrivate)); - - /* Virtual methods */ - object_class->dispose = dispose; - object_class->finalize = finalize; - object_class->set_property = set_property; - object_class->get_property = get_property; - object_class->constructor = constructor; - object_class->constructed = constructed; - - klass->get_type_capabilities = get_type_capabilities; - klass->get_generic_capabilities = get_generic_capabilities; - klass->act_stage1_prepare = act_stage1_prepare; - klass->act_stage2_config = act_stage2_config; - klass->act_stage3_ip4_config_start = act_stage3_ip4_config_start; - klass->act_stage3_ip6_config_start = act_stage3_ip6_config_start; - klass->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout; - klass->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout; - klass->have_any_ready_slaves = have_any_ready_slaves; - - klass->check_connection_available = check_connection_available; - klass->hw_is_up = hw_is_up; - klass->hw_bring_up = hw_bring_up; - klass->hw_take_down = hw_take_down; - - /* Properties */ - g_object_class_install_property - (object_class, PROP_UDI, - g_param_spec_string (NM_DEVICE_UDI, - "UDI", - "Unique Device Identifier", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - - g_object_class_install_property - (object_class, PROP_IFACE, - g_param_spec_string (NM_DEVICE_IFACE, - "Interface", - "Interface", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_IP_IFACE, - g_param_spec_string (NM_DEVICE_IP_IFACE, - "IP Interface", - "IP Interface", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_DRIVER, - g_param_spec_string (NM_DEVICE_DRIVER, - "Driver", - "Driver", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_DRIVER_VERSION, - g_param_spec_string (NM_DEVICE_DRIVER_VERSION, - "Driver Version", - "Driver Version", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_FIRMWARE_VERSION, - g_param_spec_string (NM_DEVICE_FIRMWARE_VERSION, - "Firmware Version", - "Firmware Version", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_CAPABILITIES, - g_param_spec_uint (NM_DEVICE_CAPABILITIES, - "Capabilities", - "Capabilities", - 0, G_MAXUINT32, NM_DEVICE_CAP_NONE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_IP4_ADDRESS, - g_param_spec_uint (NM_DEVICE_IP4_ADDRESS, - "IP4 address", - "IP4 address", - 0, G_MAXUINT32, 0, /* FIXME */ - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_IP4_CONFIG, - g_param_spec_boxed (NM_DEVICE_IP4_CONFIG, - "IP4 Config", - "IP4 Config", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_DHCP4_CONFIG, - g_param_spec_boxed (NM_DEVICE_DHCP4_CONFIG, - "DHCP4 Config", - "DHCP4 Config", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_IP6_CONFIG, - g_param_spec_boxed (NM_DEVICE_IP6_CONFIG, - "IP6 Config", - "IP6 Config", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_DHCP6_CONFIG, - g_param_spec_boxed (NM_DEVICE_DHCP6_CONFIG, - "DHCP6 Config", - "DHCP6 Config", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_STATE, - g_param_spec_uint (NM_DEVICE_STATE, - "State", - "State", - 0, G_MAXUINT32, NM_DEVICE_STATE_UNKNOWN, - G_PARAM_READABLE)); - g_object_class_install_property - (object_class, PROP_STATE_REASON, - g_param_spec_boxed (NM_DEVICE_STATE_REASON, - "StateReason", - "StateReason", - DBUS_G_TYPE_UINT_STRUCT, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_ACTIVE_CONNECTION, - g_param_spec_boxed (NM_DEVICE_ACTIVE_CONNECTION, - "ActiveConnection", - "ActiveConnection", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_DEVICE_TYPE, - g_param_spec_uint (NM_DEVICE_DEVICE_TYPE, - "DeviceType", - "DeviceType", - 0, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); - - g_object_class_install_property - (object_class, PROP_MANAGED, - g_param_spec_boolean (NM_DEVICE_MANAGED, - "Managed", - "Managed", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_AUTOCONNECT, - g_param_spec_boolean (NM_DEVICE_AUTOCONNECT, - "Autoconnect", - "Autoconnect", - DEFAULT_AUTOCONNECT, - G_PARAM_READWRITE)); - - g_object_class_install_property - (object_class, PROP_FIRMWARE_MISSING, - g_param_spec_boolean (NM_DEVICE_FIRMWARE_MISSING, - "FirmwareMissing", - "Firmware missing", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_TYPE_DESC, - g_param_spec_string (NM_DEVICE_TYPE_DESC, - "Type Description", - "Device type description", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); - - g_object_class_install_property - (object_class, PROP_RFKILL_TYPE, - g_param_spec_uint (NM_DEVICE_RFKILL_TYPE, - "Rfkill Type", - "Type of rfkill switch (if any) supported by this device", - RFKILL_TYPE_WLAN, - RFKILL_TYPE_MAX, - RFKILL_TYPE_UNKNOWN, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); - - g_object_class_install_property - (object_class, PROP_IFINDEX, - g_param_spec_int (NM_DEVICE_IFINDEX, - "Ifindex", - "Ifindex", - 0, G_MAXINT, 0, - G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT)); - - g_object_class_install_property - (object_class, PROP_AVAILABLE_CONNECTIONS, - g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS, - "AvailableConnections", - "AvailableConnections", - DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_IS_MASTER, - g_param_spec_boolean (NM_DEVICE_IS_MASTER, - "IsMaster", - "IsMaster", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); - - /* Signals */ - signals[STATE_CHANGED] = - g_signal_new ("state-changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NMDeviceClass, state_changed), - NULL, NULL, - _nm_marshal_VOID__UINT_UINT_UINT, - G_TYPE_NONE, 3, - G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); - - signals[AUTOCONNECT_ALLOWED] = - g_signal_new ("autoconnect-allowed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - autoconnect_allowed_accumulator, NULL, - _nm_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); - - signals[AUTH_REQUEST] = - g_signal_new (NM_DEVICE_AUTH_REQUEST, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, - /* dbus-glib context, connection, permission, allow_interaction, callback, user_data */ - _nm_marshal_VOID__POINTER_POINTER_STRING_BOOLEAN_POINTER_POINTER, - G_TYPE_NONE, 6, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER, G_TYPE_POINTER); - - signals[IP4_CONFIG_CHANGED] = - g_signal_new (NM_DEVICE_IP4_CONFIG_CHANGED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, - _nm_marshal_VOID__OBJECT_OBJECT, - G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT); - - signals[IP6_CONFIG_CHANGED] = - g_signal_new (NM_DEVICE_IP6_CONFIG_CHANGED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, - _nm_marshal_VOID__OBJECT_OBJECT, - G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT); - - dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), - &dbus_glib_nm_device_interface_object_info); - - dbus_g_error_domain_register (NM_DEVICE_ERROR, NULL, NM_TYPE_DEVICE_ERROR); -} - -void -nm_device_set_firmware_missing (NMDevice *self, gboolean new_missing) -{ - NMDevicePrivate *priv; - - g_return_if_fail (self != NULL); - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->firmware_missing != new_missing) { - priv->firmware_missing = new_missing; - g_object_notify (G_OBJECT (self), NM_DEVICE_FIRMWARE_MISSING); - } -} - -gboolean -nm_device_get_firmware_missing (NMDevice *self) -{ - return NM_DEVICE_GET_PRIVATE (self)->firmware_missing; -} - -static const char * -state_to_string (NMDeviceState state) -{ - switch (state) { - case NM_DEVICE_STATE_UNMANAGED: - return "unmanaged"; - case NM_DEVICE_STATE_UNAVAILABLE: - return "unavailable"; - case NM_DEVICE_STATE_DISCONNECTED: - return "disconnected"; - case NM_DEVICE_STATE_PREPARE: - return "prepare"; - case NM_DEVICE_STATE_CONFIG: - return "config"; - case NM_DEVICE_STATE_NEED_AUTH: - return "need-auth"; - case NM_DEVICE_STATE_IP_CONFIG: - return "ip-config"; - case NM_DEVICE_STATE_IP_CHECK: - return "ip-check"; - case NM_DEVICE_STATE_SECONDARIES: - return "secondaries"; - case NM_DEVICE_STATE_ACTIVATED: - return "activated"; - case NM_DEVICE_STATE_DEACTIVATING: - return "deactivating"; - case NM_DEVICE_STATE_FAILED: - return "failed"; - default: - break; - } - return "unknown"; -} - -static const char * -reason_to_string (NMDeviceStateReason reason) -{ - switch (reason) { - case NM_DEVICE_STATE_REASON_NONE: - return "none"; - case NM_DEVICE_STATE_REASON_NOW_MANAGED: - return "managed"; - case NM_DEVICE_STATE_REASON_NOW_UNMANAGED: - return "unmanaged"; - case NM_DEVICE_STATE_REASON_CONFIG_FAILED: - return "config-failed"; - case NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE: - return "ip-config-unavailable"; - case NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED: - return "ip-config-expired"; - case NM_DEVICE_STATE_REASON_NO_SECRETS: - return "no-secrets"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT: - return "supplicant-disconnect"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED: - return "supplicant-config-failed"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED: - return "supplicant-failed"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT: - return "supplicant-timeout"; - case NM_DEVICE_STATE_REASON_PPP_START_FAILED: - return "ppp-start-failed"; - case NM_DEVICE_STATE_REASON_PPP_DISCONNECT: - return "ppp-disconnect"; - case NM_DEVICE_STATE_REASON_PPP_FAILED: - return "ppp-failed"; - case NM_DEVICE_STATE_REASON_DHCP_START_FAILED: - return "dhcp-start-failed"; - case NM_DEVICE_STATE_REASON_DHCP_ERROR: - return "dhcp-error"; - case NM_DEVICE_STATE_REASON_DHCP_FAILED: - return "dhcp-failed"; - case NM_DEVICE_STATE_REASON_SHARED_START_FAILED: - return "sharing-start-failed"; - case NM_DEVICE_STATE_REASON_SHARED_FAILED: - return "sharing-failed"; - case NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED: - return "autoip-start-failed"; - case NM_DEVICE_STATE_REASON_AUTOIP_ERROR: - return "autoip-error"; - case NM_DEVICE_STATE_REASON_AUTOIP_FAILED: - return "autoip-failed"; - case NM_DEVICE_STATE_REASON_MODEM_BUSY: - return "modem-busy"; - case NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE: - return "modem-no-dialtone"; - case NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER: - return "modem-no-carrier"; - case NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT: - return "modem-dial-timeout"; - case NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED: - return "modem-dial-failed"; - case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED: - return "modem-init-failed"; - case NM_DEVICE_STATE_REASON_GSM_APN_FAILED: - return "gsm-apn-failed"; - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING: - return "gsm-registration-idle"; - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED: - return "gsm-registration-denied"; - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT: - return "gsm-registration-timeout"; - case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED: - return "gsm-registration-failed"; - case NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED: - return "gsm-pin-check-failed"; - case NM_DEVICE_STATE_REASON_FIRMWARE_MISSING: - return "firmware-missing"; - case NM_DEVICE_STATE_REASON_REMOVED: - return "removed"; - case NM_DEVICE_STATE_REASON_SLEEPING: - return "sleeping"; - case NM_DEVICE_STATE_REASON_CONNECTION_REMOVED: - return "connection-removed"; - case NM_DEVICE_STATE_REASON_USER_REQUESTED: - return "user-requested"; - case NM_DEVICE_STATE_REASON_CARRIER: - return "carrier-changed"; - case NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED: - return "connection-assumed"; - case NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE: - return "supplicant-available"; - case NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND: - return "modem-not-found"; - case NM_DEVICE_STATE_REASON_BT_FAILED: - return "bluetooth-failed"; - case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED: - return "gsm-sim-not-inserted"; - case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: - return "gsm-sim-pin-required"; - case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED: - return "gsm-sim-puk-required"; - case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG: - return "gsm-sim-wrong"; - case NM_DEVICE_STATE_REASON_INFINIBAND_MODE: - return "infiniband-mode"; - case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED: - return "dependency-failed"; - case NM_DEVICE_STATE_REASON_BR2684_FAILED: - return "br2684-bridge-failed"; - case NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE: - return "modem-manager-unavailable"; - case NM_DEVICE_STATE_REASON_SSID_NOT_FOUND: - return "SSID not found"; - case NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED: - return "secondary-connection-failed"; - default: - break; - } - return "unknown"; -} - -void -nm_device_state_changed (NMDevice *device, - NMDeviceState state, - NMDeviceStateReason reason) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - NMDeviceState old_state; - NMActRequest *req; - gboolean no_firmware = FALSE; - NMConnection *connection; - - /* Track re-entry */ - static gboolean in_state_changed = FALSE; - - g_warn_if_fail (in_state_changed == FALSE); - in_state_changed = TRUE; - - g_return_if_fail (NM_IS_DEVICE (device)); - - /* Do nothing if state isn't changing, but as a special case allow - * re-setting UNAVAILABLE if the device is missing firmware so that we - * can retry device initialization. - */ - if ( (priv->state == state) - && !(state == NM_DEVICE_STATE_UNAVAILABLE && priv->firmware_missing)) { - in_state_changed = FALSE; - return; - } - - old_state = priv->state; - priv->state = state; - priv->state_reason = reason; - - nm_log_info (LOGD_DEVICE, "(%s): device state change: %s -> %s (reason '%s') [%d %d %d]", - nm_device_get_iface (device), - state_to_string (old_state), - state_to_string (state), - reason_to_string (reason), - old_state, - state, - reason); - - /* Clear any queued transitions */ - nm_device_queued_state_clear (device); - - /* Cache the activation request for the dispatcher */ - req = priv->act_request ? g_object_ref (priv->act_request) : NULL; - - if (state <= NM_DEVICE_STATE_UNAVAILABLE) - _clear_available_connections (device, TRUE); - - /* Update the available connections list when a device first becomes available */ - if ( state >= NM_DEVICE_STATE_DISCONNECTED - && old_state < NM_DEVICE_STATE_DISCONNECTED) - nm_device_recheck_available_connections (device); - - /* Handle the new state here; but anything that could trigger - * another state change should be done below. - */ - switch (state) { - case NM_DEVICE_STATE_UNMANAGED: - nm_device_set_firmware_missing (device, FALSE); - if (old_state > NM_DEVICE_STATE_UNMANAGED) - nm_device_take_down (device, TRUE, reason); - break; - case NM_DEVICE_STATE_UNAVAILABLE: - if (old_state == NM_DEVICE_STATE_UNMANAGED || priv->firmware_missing) { - if (!nm_device_bring_up (device, TRUE, &no_firmware) && no_firmware) - nm_log_warn (LOGD_HW, "(%s): firmware may be missing.", nm_device_get_iface (device)); - nm_device_set_firmware_missing (device, no_firmware ? TRUE : FALSE); - } - /* Ensure the device gets deactivated in response to stuff like - * carrier changes or rfkill. But don't deactivate devices that are - * about to assume a connection since that defeats the purpose of - * assuming the device's existing connection. - */ - if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) - nm_device_deactivate (device, reason); - break; - case NM_DEVICE_STATE_DISCONNECTED: - if (old_state != NM_DEVICE_STATE_UNAVAILABLE) - nm_device_deactivate (device, reason); - break; - default: - priv->autoconnect = TRUE; - break; - } - - g_object_notify (G_OBJECT (device), NM_DEVICE_STATE); - g_object_notify (G_OBJECT (device), NM_DEVICE_STATE_REASON); - g_signal_emit_by_name (device, "state-changed", state, old_state, reason); - - /* Post-process the event after internal notification */ - - switch (state) { - case NM_DEVICE_STATE_UNAVAILABLE: - /* If the device can activate now (ie, it's got a carrier, the supplicant - * is active, or whatever) schedule a delayed transition to DISCONNECTED - * to get things rolling. The device can't transition immediately because - * we can't change states again from the state handler for a variety of - * reasons. - */ - if (nm_device_is_available (device)) { - nm_log_dbg (LOGD_DEVICE, "(%s): device is available, will transition to DISCONNECTED", - nm_device_get_iface (device)); - nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE); - } else { - nm_log_dbg (LOGD_DEVICE, "(%s): device not yet available for transition to DISCONNECTED", - nm_device_get_iface (device)); - } - break; - case NM_DEVICE_STATE_ACTIVATED: - nm_log_info (LOGD_DEVICE, "Activation (%s) successful, device activated.", - nm_device_get_iface (device)); - nm_dispatcher_call (DISPATCHER_ACTION_UP, nm_act_request_get_connection (req), device, NULL, NULL); - break; - case NM_DEVICE_STATE_FAILED: - connection = nm_device_get_connection (device); - nm_log_warn (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s) failed for connection '%s'", - nm_device_get_iface (device), - connection ? nm_connection_get_id (connection) : "<unknown>"); - - /* Notify any slaves of the unexpected failure */ - nm_device_master_release_slaves (device, TRUE); - - /* If the connection doesn't yet have a timestamp, set it to zero so that - * we can distinguish between connections we've tried to activate and have - * failed (zero timestamp), connections that succeeded (non-zero timestamp), - * and those we haven't tried yet (no timestamp). - */ - if (connection && !nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), NULL)) { - nm_settings_connection_update_timestamp (NM_SETTINGS_CONNECTION (connection), - (guint64) 0, - TRUE); - } - - /* Schedule the transition to DISCONNECTED. The device can't transition - * immediately because we can't change states again from the state - * handler for a variety of reasons. - */ - nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE); - break; - case NM_DEVICE_STATE_SECONDARIES: - nm_log_dbg (LOGD_DEVICE, "(%s): device entered SECONDARIES state", - nm_device_get_iface (device)); - break; - default: - break; - } - - if (old_state == NM_DEVICE_STATE_ACTIVATED) - nm_dispatcher_call (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), device, NULL, NULL); - - /* Dispose of the cached activation request */ - if (req) - g_object_unref (req); - - in_state_changed = FALSE; -} - -static gboolean -queued_set_state (gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMDeviceState new_state; - NMDeviceStateReason new_reason; - - if (priv->queued_state.id) { - nm_log_dbg (LOGD_DEVICE, "(%s): running queued state change to %s (id %d)", - nm_device_get_iface (self), - state_to_string (priv->queued_state.state), - priv->queued_state.id); - - /* Clear queued state struct before triggering state change, since - * the state change may queue another state. - */ - priv->queued_state.id = 0; - new_state = priv->queued_state.state; - new_reason = priv->queued_state.reason; - nm_device_queued_state_clear (self); - - nm_device_state_changed (self, new_state, new_reason); - } else { - g_warn_if_fail (priv->queued_state.state == NM_DEVICE_STATE_UNKNOWN); - g_warn_if_fail (priv->queued_state.reason == NM_DEVICE_STATE_REASON_NONE); - } - return FALSE; -} - -void -nm_device_queue_state (NMDevice *self, - NMDeviceState state, - NMDeviceStateReason reason) -{ - NMDevicePrivate *priv; - - g_return_if_fail (self != NULL); - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - - /* We should only ever have one delayed state transition at a time */ - if (priv->queued_state.id) { - g_warn_if_fail (priv->queued_state.id == 0); - nm_device_queued_state_clear (self); - } - - priv->queued_state.state = state; - priv->queued_state.reason = reason; - priv->queued_state.id = g_idle_add (queued_set_state, self); - - nm_log_dbg (LOGD_DEVICE, "(%s): queued state change to %s (id %d)", - nm_device_get_iface (self), state_to_string (state), - priv->queued_state.id); -} - -NMDeviceState -nm_device_queued_state_peek (NMDevice *self) -{ - NMDevicePrivate *priv; - - g_return_val_if_fail (self != NULL, NM_DEVICE_STATE_UNKNOWN); - g_return_val_if_fail (NM_IS_DEVICE (self), NM_DEVICE_STATE_UNKNOWN); - - priv = NM_DEVICE_GET_PRIVATE (self); - - return priv->queued_state.id ? priv->queued_state.state : NM_DEVICE_STATE_UNKNOWN; -} - -void -nm_device_queued_state_clear (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->queued_state.id) { - nm_log_dbg (LOGD_DEVICE, "(%s): clearing queued state transition (id %d)", - nm_device_get_iface (self), priv->queued_state.id); - g_source_remove (priv->queued_state.id); - } - memset (&priv->queued_state, 0, sizeof (priv->queued_state)); -} - -NMDeviceState -nm_device_get_state (NMDevice *device) -{ - g_return_val_if_fail (NM_IS_DEVICE (device), NM_DEVICE_STATE_UNKNOWN); - - return NM_DEVICE_GET_PRIVATE (device)->state; -} - -gboolean -nm_device_get_managed (NMDevice *device) -{ - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - - return NM_DEVICE_GET_PRIVATE (device)->managed; -} - -void -nm_device_set_managed (NMDevice *device, - gboolean managed, - NMDeviceStateReason reason) -{ - NMDevicePrivate *priv; - - g_return_if_fail (NM_IS_DEVICE (device)); - - priv = NM_DEVICE_GET_PRIVATE (device); - if (priv->managed == managed) - return; - - priv->managed = managed; - nm_log_dbg (LOGD_DEVICE, "(%s): now %s", - nm_device_get_iface (device), - managed ? "managed" : "unmanaged"); - - g_object_notify (G_OBJECT (device), NM_DEVICE_MANAGED); - - /* If now managed, jump to unavailable */ - if (managed) - nm_device_state_changed (device, NM_DEVICE_STATE_UNAVAILABLE, reason); - else - nm_device_state_changed (device, NM_DEVICE_STATE_UNMANAGED, reason); -} - -gboolean -nm_device_spec_match_list (NMDevice *device, const GSList *specs) -{ - g_return_val_if_fail (device != NULL, FALSE); - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - - if (NM_DEVICE_GET_CLASS (device)->spec_match_list) - return NM_DEVICE_GET_CLASS (device)->spec_match_list (device, specs); - return FALSE; -} - -static gboolean -ip4_match_config (NMDevice *self, NMConnection *connection) -{ - NMSettingIP4Config *s_ip4; - int i, num; - GSList *leases, *iter; - NMDHCPManager *dhcp_mgr; - const char *method; - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - - /* Get any saved leases that apply to this connection */ - dhcp_mgr = nm_dhcp_manager_get (); - leases = nm_dhcp_manager_get_lease_config (dhcp_mgr, - nm_device_get_iface (self), - nm_connection_get_uuid (connection), - FALSE); - g_object_unref (dhcp_mgr); - - method = s_ip4 ? nm_setting_ip4_config_get_method (s_ip4) : NM_SETTING_IP4_CONFIG_METHOD_AUTO; - if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { - gboolean found = FALSE; - - /* Find at least one lease's address on the device */ - for (iter = leases; iter; iter = g_slist_next (iter)) { - NMIP4Config *ip4_config = iter->data; - NMIP4Address *addr = nm_ip4_config_get_address (ip4_config, 0); - struct in_addr tmp = { .s_addr = nm_ip4_address_get_address (addr) }; - - if (addr && nm_netlink_find_address (nm_device_get_ip_ifindex (self), - AF_INET, - &tmp, - nm_ip4_address_get_prefix (addr))) { - found = TRUE; /* Yay, device has same address as a lease */ - break; - } - } - g_slist_foreach (leases, (GFunc) g_object_unref, NULL); - g_slist_free (leases); - return found; - } else { - /* Maybe the connection used to be DHCP and there are stale leases; ignore them */ - g_slist_foreach (leases, (GFunc) g_object_unref, NULL); - g_slist_free (leases); - } - - if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) { - // FIXME: Enforce no ipv4 addresses? - return TRUE; - } - - /* 'shared' and 'link-local' aren't supported methods because 'shared' - * requires too much iptables and dnsmasq state to be reclaimed, and - * avahi-autoipd isn't smart enough to allow the link-local address to be - * determined at any point other than when it was first assigned. - */ - if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) - return FALSE; - - /* Everything below for static addressing */ - - /* Find all IP4 addresses of this connection on the device */ - if (s_ip4) { - num = nm_setting_ip4_config_get_num_addresses (s_ip4); - for (i = 0; i < num; i++) { - NMIP4Address *addr = nm_setting_ip4_config_get_address (s_ip4, i); - struct in_addr tmp = { .s_addr = nm_ip4_address_get_address (addr) }; - - if (!nm_netlink_find_address (nm_device_get_ip_ifindex (self), - AF_INET, - &tmp, - nm_ip4_address_get_prefix (addr))) - return FALSE; - } - } - - /* Success; all the connection's static IP addresses are assigned to the device */ - return TRUE; -} - -gboolean -nm_device_match_ip_config (NMDevice *device, NMConnection *connection) -{ - if (!ip4_match_config (device, connection)) - return FALSE; - - /* FIXME: match IPv6 config */ - - return TRUE; -} - -NMConnection * -nm_device_connection_match_config (NMDevice *device, const GSList *connections) -{ - g_return_val_if_fail (device != NULL, NULL); - g_return_val_if_fail (NM_IS_DEVICE (device), NULL); - - if (NM_DEVICE_GET_CLASS (device)->connection_match_config) - return NM_DEVICE_GET_CLASS (device)->connection_match_config (NM_DEVICE (device), connections); - return NULL; -} - -/** - * nm_device_hwaddr_matches: - * @device: the device to use when matching the hardware address - * @connection: the connection which supplies the hardware address - * @other_hwaddr: if given, use this address instead of the device's actual - * hardware address - * @other_hwaddr_len: length in bytes of @other_hwaddr - * @fail_if_no_hwaddr: whether to fail the match if @connection does not contain - * a hardware address - * - * Matches a the devices hardware address (or @other_hwaddr if given) against - * the hardware-specific setting in @connection. Allows for device-agnostic - * hardware address matching without having to know the internal details of - * the connection and which settings are used by each device subclass. - * - * Returns: %TRUE if the @device 's hardware address or @other_hwaddr matches - * a hardware address in a hardware-specific setting in @connection - */ -gboolean -nm_device_hwaddr_matches (NMDevice *device, - NMConnection *connection, - const guint8 *other_hwaddr, - guint other_hwaddr_len, - gboolean fail_if_no_hwaddr) -{ - g_return_val_if_fail (device != NULL, FALSE); - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - if (other_hwaddr) - g_return_val_if_fail (other_hwaddr_len > 0, FALSE); - - if (NM_DEVICE_GET_CLASS (device)->hwaddr_matches) { - return NM_DEVICE_GET_CLASS (device)->hwaddr_matches (device, - connection, - other_hwaddr, - other_hwaddr_len, - fail_if_no_hwaddr); - } - return FALSE; -} - -void -nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout) -{ - g_return_if_fail (NM_IS_DEVICE (device)); - - NM_DEVICE_GET_PRIVATE (device)->dhcp_timeout = timeout; -} - -void -nm_device_set_dhcp_anycast_address (NMDevice *device, guint8 *addr) -{ - NMDevicePrivate *priv; - - g_return_if_fail (NM_IS_DEVICE (device)); - - priv = NM_DEVICE_GET_PRIVATE (device); - - if (priv->dhcp_anycast_address) { - g_byte_array_free (priv->dhcp_anycast_address, TRUE); - priv->dhcp_anycast_address = NULL; - } - - if (addr) { - priv->dhcp_anycast_address = g_byte_array_sized_new (ETH_ALEN); - g_byte_array_append (priv->dhcp_anycast_address, addr, ETH_ALEN); - } -} - -gboolean -nm_device_get_autoconnect (NMDevice *device) -{ - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - - return NM_DEVICE_GET_PRIVATE (device)->autoconnect; -} - -static void -_signal_available_connections_changed (NMDevice *device) -{ - g_object_notify (G_OBJECT (device), NM_DEVICE_AVAILABLE_CONNECTIONS); -} - -static void -_clear_available_connections (NMDevice *device, gboolean do_signal) -{ - g_hash_table_remove_all (NM_DEVICE_GET_PRIVATE (device)->available_connections); - if (do_signal == TRUE) - _signal_available_connections_changed (device); -} - -static gboolean -_try_add_available_connection (NMDevice *self, NMConnection *connection) -{ - if (nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED) - return FALSE; - - if (nm_device_check_connection_compatible (self, connection, NULL)) { - /* Let subclasses implement additional checks on the connection */ - if ( NM_DEVICE_GET_CLASS (self)->check_connection_available - && NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection)) { - - g_hash_table_insert (NM_DEVICE_GET_PRIVATE (self)->available_connections, - g_object_ref (connection), - GUINT_TO_POINTER (1)); - return TRUE; - } - } - return FALSE; -} - -static gboolean -_del_available_connection (NMDevice *device, NMConnection *connection) -{ - return g_hash_table_remove (NM_DEVICE_GET_PRIVATE (device)->available_connections, connection); -} - -static gboolean -check_connection_available (NMDevice *device, NMConnection *connection) -{ - /* Default is to assume the connection is available unless a subclass - * overrides this with more specific checks. - */ - return TRUE; -} - -void -nm_device_recheck_available_connections (NMDevice *device) -{ - NMDevicePrivate *priv; - const GSList *connections, *iter; - - g_return_if_fail (device != NULL); - g_return_if_fail (NM_IS_DEVICE (device)); - - priv = NM_DEVICE_GET_PRIVATE(device); - - _clear_available_connections (device, FALSE); - - connections = nm_connection_provider_get_connections (priv->con_provider); - for (iter = connections; iter; iter = g_slist_next (iter)) - _try_add_available_connection (device, NM_CONNECTION (iter->data)); - - _signal_available_connections_changed (device); -} - -static void -cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) -{ - if (_try_add_available_connection (NM_DEVICE (user_data), connection)) - _signal_available_connections_changed (NM_DEVICE (user_data)); -} - -static void -cp_connections_loaded (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) -{ - const GSList *connections, *iter; - gboolean added = FALSE; - - connections = nm_connection_provider_get_connections (cp); - for (iter = connections; iter; iter = g_slist_next (iter)) - added |= _try_add_available_connection (NM_DEVICE (user_data), NM_CONNECTION (iter->data)); - - if (added) - _signal_available_connections_changed (NM_DEVICE (user_data)); -} - -static void -cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) -{ - if (_del_available_connection (NM_DEVICE (user_data), connection)) - _signal_available_connections_changed (NM_DEVICE (user_data)); -} - -static void -cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) -{ - gboolean added, deleted; - - /* FIXME: don't remove it from the hash if it's just going to get re-added */ - deleted = _del_available_connection (NM_DEVICE (user_data), connection); - added = _try_add_available_connection (NM_DEVICE (user_data), connection); - - /* Only signal if the connection was removed OR added, but not both */ - if (added != deleted) - _signal_available_connections_changed (NM_DEVICE (user_data)); -} - -gboolean -nm_device_supports_vlans (NMDevice *device) -{ - /* At the moment, NM's VLAN code assumes all VLANs are over ethernet. */ - return NM_IS_DEVICE_ETHERNET (device); -} - -/** - * nm_device_read_hwaddr: - * @dev: the device - * @buf: an allocated buffer which on success holds the device's hardware - * address - * @buf_len: the size of @buf - * @out_changed: on success, %TRUE if the contents of @buf are different from - * the original contents of @buf when this function was called - * - * Reads the device's hardware address from the kernel and copies it into - * @buf, returning the size of the data copied into @buf. On failure - * @buf is not modified. - * - * Returns: the size of the hardware address in bytes on success, 0 on failure - */ -gsize -nm_device_read_hwaddr (NMDevice *dev, - guint8 *buf, - gsize buf_len, - gboolean *out_changed) -{ - struct rtnl_link *rtnl; - struct nl_addr *addr; - int idx; - gsize addrlen = 0; - const guint8 *binaddr; - - g_return_val_if_fail (dev != NULL, 0); - g_return_val_if_fail (buf != NULL, 0); - g_return_val_if_fail (buf_len > 0, 0); - - idx = nm_device_get_ip_ifindex (dev); - g_return_val_if_fail (idx > 0, 0); - - rtnl = nm_netlink_index_to_rtnl_link (idx); - if (!rtnl) { - nm_log_err (LOGD_HW | LOGD_DEVICE, - "(%s): failed to read hardware address (error %d)", - nm_device_get_iface (dev), errno); - return 0; - } - - addr = rtnl_link_get_addr (rtnl); - if (!addr) { - nm_log_err (LOGD_HW | LOGD_DEVICE, - "(%s): no hardware address?", - nm_device_get_iface (dev)); - goto out; - } - - addrlen = nl_addr_get_len (addr); - if (addrlen > buf_len) { - nm_log_err (LOGD_HW | LOGD_DEVICE, - "(%s): hardware address is wrong length (got %zd max %zd)", - nm_device_get_iface (dev), addrlen, buf_len); - addrlen = 0; - } else { - binaddr = nl_addr_get_binary_addr (addr); - if (out_changed) - *out_changed = memcmp (buf, binaddr, addrlen) ? TRUE : FALSE; - memcpy (buf, binaddr, addrlen); - } - -out: - rtnl_link_put (rtnl); - return addrlen; -} |