diff options
Diffstat (limited to 'src/devices/nm-device.c')
| -rw-r--r-- | src/devices/nm-device.c | 3971 |
1 files changed, 2706 insertions, 1265 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index bb39ca55..93782a45 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -21,9 +21,6 @@ #include "config.h" -#include <glib.h> -#include <glib/gi18n.h> -#include <dbus/dbus.h> #include <netinet/in.h> #include <string.h> #include <unistd.h> @@ -35,9 +32,9 @@ #include <arpa/inet.h> #include <fcntl.h> #include <netlink/route/addr.h> +#include <linux/if_addr.h> -#include "gsystem-local-alloc.h" -#include "nm-glib-compat.h" +#include "nm-default.h" #include "nm-device.h" #include "nm-device-private.h" #include "NetworkManagerUtils.h" @@ -46,8 +43,6 @@ #include "nm-rdisc.h" #include "nm-lndp-rdisc.h" #include "nm-dhcp-manager.h" -#include "nm-dbus-manager.h" -#include "nm-logging.h" #include "nm-activation-request.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" @@ -56,35 +51,35 @@ #include "nm-dhcp6-config.h" #include "nm-rfkill-manager.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-auth-utils.h" -#include "nm-dbus-glib-types.h" #include "nm-dispatcher.h" #include "nm-config.h" #include "nm-dns-manager.h" #include "nm-core-internal.h" #include "nm-default-route-manager.h" #include "nm-route-manager.h" +#include "nm-lldp-listener.h" +#include "sd-ipv4ll.h" +#include "nm-audit-manager.h" #include "nm-device-logging.h" _LOG_DECLARE_SELF (NMDevice); -static void impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context); -static void impl_device_delete (NMDevice *self, DBusGMethodInvocation *context); +#include "nmdbus-device.h" + static void ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data); static gboolean ip_config_valid (NMDeviceState state); -static void nm_device_update_metered (NMDevice *self); static NMActStageReturn dhcp4_start (NMDevice *self, NMConnection *connection, NMDeviceStateReason *reason); static gboolean dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason); +static void nm_device_start_ip_check (NMDevice *self); +static void realize_start_setup (NMDevice *self, const NMPlatformLink *plink); -#include "nm-device-glue.h" - -G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, G_TYPE_OBJECT) +G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, NM_TYPE_EXPORTED_OBJECT) -#define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate)) +#define NM_DEVICE_GET_PRIVATE(o) ((o)->priv) enum { STATE_CHANGED, @@ -95,14 +90,12 @@ enum { REMOVED, RECHECK_AUTO_ACTIVATE, RECHECK_ASSUME, - LINK_INITIALIZED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = { 0 }; enum { PROP_0, - PROP_PLATFORM_DEVICE, PROP_UDI, PROP_IFACE, PROP_IP_IFACE, @@ -121,9 +114,11 @@ enum { PROP_STATE_REASON, PROP_ACTIVE_CONNECTION, PROP_DEVICE_TYPE, + PROP_LINK_TYPE, PROP_MANAGED, PROP_AUTOCONNECT, PROP_FIRMWARE_MISSING, + PROP_NM_PLUGIN_MISSING, PROP_TYPE_DESC, PROP_RFKILL_TYPE, PROP_IFINDEX, @@ -134,15 +129,27 @@ enum { PROP_HW_ADDRESS, PROP_HAS_PENDING_ACTION, PROP_METERED, + PROP_LLDP_NEIGHBORS, + PROP_REAL, + PROP_SLAVES, LAST_PROP }; +#define DEFAULT_AUTOCONNECT TRUE + /***********************************************************/ #define PENDING_ACTION_DHCP4 "dhcp4" #define PENDING_ACTION_DHCP6 "dhcp6" #define PENDING_ACTION_AUTOCONF6 "autoconf6" +typedef void (*ActivationHandleFunc) (NMDevice *self); + +typedef struct { + ActivationHandleFunc func; + guint id; +} ActivationHandleData; + typedef enum { CLEANUP_TYPE_DECONFIGURE, CLEANUP_TYPE_KEEP, @@ -165,13 +172,13 @@ typedef struct { typedef struct { NMDevice *slave; - gboolean enslaved; + gboolean slave_is_enslaved; gboolean configure; - guint watch_id; + gulong watch_id; } SlaveInfo; typedef struct { - guint log_domain; + NMLogDomain log_domain; guint timeout; guint watch; GPid pid; @@ -186,7 +193,7 @@ typedef struct { int ifindex; } DeleteOnDeactivateData; -typedef struct { +typedef struct _NMDevicePrivate { gboolean in_state_changed; gboolean initialized; gboolean platform_link_initialized; @@ -200,22 +207,25 @@ typedef struct { guint queued_ip4_config_id; guint queued_ip6_config_id; GSList *pending_actions; + GSList *dad6_failed_addrs; char * udi; - char * path; char * iface; /* may change, could be renamed by user */ int ifindex; + gboolean real; char * ip_iface; int ip_ifindex; NMDeviceType type; char * type_desc; char * type_description; + NMLinkType link_type; NMDeviceCapabilities capabilities; char * driver; char * driver_version; char * firmware_version; RfKillType rfkill_type; gboolean firmware_missing; + gboolean nm_plugin_missing; GHashTable * available_connections; char * hw_addr; guint hw_addr_len; @@ -224,6 +234,7 @@ typedef struct { char * physical_port_id; guint dev_id; + gboolean managed_touched_by_user; NMUnmanagedFlags unmanaged_flags; gboolean is_nm_owned; /* whether the device is a device owned and created by NM */ DeleteOnDeactivateData *delete_on_deactivate_data; /* data for scheduled cleanup when deleting link (g_idle_add) */ @@ -235,13 +246,11 @@ typedef struct { NMActRequest * queued_act_request; gboolean queued_act_request_is_waiting_for_carrier; NMActRequest * act_request; - guint act_source_id; - gpointer act_source_func; - guint act_source6_id; - gpointer act_source6_func; + ActivationHandleData act_handle4; /* for layer2 and IPv4. */ + ActivationHandleData act_handle6; guint recheck_assume_id; struct { - guint call_id; + guint call_id; NMDeviceStateReason available_reason; NMDeviceStateReason unavailable_reason; } recheck_available; @@ -272,6 +281,7 @@ typedef struct { NMIP4Config * dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */ NMIP4Config * ext_ip4_config; /* Stuff added outside NM */ NMIP4Config * wwan_ip4_config; /* WWAN configuration */ + GSList * vpn4_configs; /* VPNs which use this device */ struct { gboolean v4_has; gboolean v4_is_assumed; @@ -289,7 +299,6 @@ typedef struct { gulong dhcp4_state_sigid; NMDhcp4Config * dhcp4_config; guint dhcp4_restart_id; - NMIP4Config * vpn4_config; /* routes added by a VPN which uses this device */ guint arp_round2_id; PingInfo gw_ping; @@ -299,20 +308,20 @@ typedef struct { gulong dnsmasq_state_id; /* Firewall */ - NMFirewallPendingCall fw_call; + gboolean fw_ready; + NMFirewallManagerCallId fw_call; - /* avahi-autoipd stuff */ - GPid aipd_pid; - guint aipd_watch; - guint aipd_timeout; + /* IPv4LL stuff */ + sd_ipv4ll * ipv4ll; + guint ipv4ll_timeout; /* IP6 configuration info */ NMIP6Config * ip6_config; IpState ip6_state; NMIP6Config * con_ip6_config; /* config from the setting */ - NMIP6Config * vpn6_config; /* routes added by a VPN which uses this device */ NMIP6Config * wwan_ip6_config; NMIP6Config * ext_ip6_config; /* Stuff added outside NM */ + GSList * vpn6_configs; /* VPNs which use this device */ gboolean nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */ guint32 ip6_mtu; @@ -324,6 +333,7 @@ typedef struct { NMIP6Config * ac_ip6_config; guint linklocal6_timeout_id; + guint8 linklocal6_dad_counter; GHashTable * ip6_saved_properties; @@ -342,9 +352,9 @@ typedef struct { /* master interface for bridge/bond/team slave */ NMDevice * master; - gboolean enslaved; + gboolean is_enslaved; gboolean master_ready_handled; - guint master_ready_id; + gulong master_ready_id; /* slave management */ gboolean is_master; @@ -353,6 +363,9 @@ typedef struct { NMMetered metered; NMConnectionProvider *con_provider; + NMLldpListener *lldp_listener; + + guint check_delete_unrealized_id; } NMDevicePrivate; static gboolean nm_device_set_ip4_config (NMDevice *self, @@ -372,7 +385,7 @@ static gboolean nm_device_set_ip6_config (NMDevice *self, gboolean routes_full_sync, NMDeviceStateReason *reason); -static gboolean nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure); +static void nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure); static void nm_device_slave_notify_enslave (NMDevice *self, gboolean success); static void nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason); @@ -384,12 +397,20 @@ static void _carrier_wait_check_queued_act_request (NMDevice *self); static gboolean nm_device_get_default_unmanaged (NMDevice *self); +static const char *_activation_func_to_string (ActivationHandleFunc func); +static void activation_source_handle_cb (NMDevice *self, int family); + static void _set_state_full (NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, gboolean quitting); -static void nm_device_update_hw_address (NMDevice *self); +static gboolean queued_ip4_config_change (gpointer user_data); +static gboolean queued_ip6_config_change (gpointer user_data); + +static void _set_unmanaged_flags (NMDevice *self, + NMUnmanagedFlags flags, + gboolean unmanaged); /***********************************************************/ @@ -425,7 +446,7 @@ state_to_string (NMDeviceState state) return queued_state_to_string (state) + strlen (QUEUED_PREFIX); } -static const char *reason_table[] = { +NM_UTILS_STRING_LOOKUP_TABLE_DEFINE_STATIC (_reason_to_string, NMDeviceStateReason, NULL, [NM_DEVICE_STATE_REASON_UNKNOWN] = "unknown", [NM_DEVICE_STATE_REASON_NONE] = "none", [NM_DEVICE_STATE_REASON_NOW_MANAGED] = "managed", @@ -489,15 +510,10 @@ static const char *reason_table[] = { [NM_DEVICE_STATE_REASON_NEW_ACTIVATION] = "new-activation", [NM_DEVICE_STATE_REASON_PARENT_CHANGED] = "parent-changed", [NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED] = "parent-managed-changed", -}; +); -static const char * -reason_to_string (NMDeviceStateReason reason) -{ - if ((gsize) reason < G_N_ELEMENTS (reason_table)) - return reason_table[reason]; - return reason_table[NM_DEVICE_STATE_REASON_UNKNOWN]; -} +#define reason_to_string(reason) \ + NM_UTILS_STRING_LOOKUP_TABLE (_reason_to_string, reason) /***********************************************************/ @@ -521,30 +537,6 @@ nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps) /***********************************************************/ -void -nm_device_dbus_export (NMDevice *self) -{ - static guint32 devcount = 0; - NMDevicePrivate *priv; - - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->path == NULL); - - priv->path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++); - _LOGD (LOGD_DEVICE, "exported as %s", priv->path); - nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, self); -} - -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) { @@ -564,17 +556,40 @@ nm_device_get_iface (NMDevice *self) int nm_device_get_ifindex (NMDevice *self) { - g_return_val_if_fail (self != NULL, 0); + g_return_val_if_fail (NM_IS_DEVICE (self), 0); return NM_DEVICE_GET_PRIVATE (self)->ifindex; } +/** + * nm_device_is_software: + * @self: the #NMDevice + * + * Indicates if the device is a software-based virtual device without + * backing hardware, which can be added and removed programmatically. + * + * Returns: %TRUE if the device is a software-based device + */ gboolean nm_device_is_software (NMDevice *self) { return NM_FLAGS_HAS (NM_DEVICE_GET_PRIVATE (self)->capabilities, NM_DEVICE_CAP_IS_SOFTWARE); } +/** + * nm_device_is_real: + * @self: the #NMDevice + * + * Returns: %TRUE if the device exists, %FALSE if the device is a placeholder + */ +gboolean +nm_device_is_real (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + return NM_DEVICE_GET_PRIVATE (self)->real; +} + const char * nm_device_get_ip_iface (NMDevice *self) { @@ -701,13 +716,21 @@ nm_device_get_device_type (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->type; } +NMLinkType +nm_device_get_link_type (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), NM_LINK_TYPE_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE (self)->link_type; +} + /** * nm_device_get_metered: * @setting: the #NMDevice * * Returns: the #NMDevice:metered property of the device. * - * Since: 1.0.6 + * Since: 1.2 **/ NMMetered nm_device_get_metered (NMDevice *self) @@ -762,12 +785,20 @@ nm_device_get_priority (NMDevice *self) return 350; case NM_DEVICE_TYPE_VLAN: return 400; + case NM_DEVICE_TYPE_MACVLAN: + return 410; case NM_DEVICE_TYPE_BRIDGE: return 425; + case NM_DEVICE_TYPE_TUN: + return 450; + case NM_DEVICE_TYPE_VXLAN: + return 500; case NM_DEVICE_TYPE_WIFI: return 600; case NM_DEVICE_TYPE_OLPC_MESH: return 650; + case NM_DEVICE_TYPE_IP_TUNNEL: + return 675; case NM_DEVICE_TYPE_MODEM: return 700; case NM_DEVICE_TYPE_BT: @@ -795,7 +826,7 @@ _get_ipx_route_metric (NMDevice *self, g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32); - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); if (connection) { s_ip = is_v4 ? nm_connection_get_setting_ip4_config (connection) @@ -923,12 +954,31 @@ nm_device_get_act_request (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->act_request; } +NMSettingsConnection * +nm_device_get_settings_connection (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + return priv->act_request ? nm_act_request_get_settings_connection (priv->act_request) : NULL; +} + NMConnection * -nm_device_get_connection (NMDevice *self) +nm_device_get_applied_connection (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - return priv->act_request ? nm_act_request_get_connection (priv->act_request) : NULL; + return priv->act_request ? nm_act_request_get_applied_connection (priv->act_request) : NULL; +} + +gboolean +nm_device_has_unmodified_applied_connection (NMDevice *self, NMSettingCompareFlags compare_flags) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (!priv->act_request) + return FALSE; + + return nm_active_connection_has_unmodified_applied_connection ((NMActiveConnection *) priv->act_request, compare_flags); } RfKillType @@ -951,13 +1001,13 @@ static gboolean nm_device_uses_generated_assumed_connection (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; + NMSettingsConnection *connection; if ( priv->act_request && nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (priv->act_request))) { - connection = nm_act_request_get_connection (priv->act_request); + connection = nm_act_request_get_settings_connection (priv->act_request); if ( connection - && nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) + && nm_settings_connection_get_nm_generated_assumed (connection)) return TRUE; } return FALSE; @@ -989,17 +1039,8 @@ find_slave_info (NMDevice *self, NMDevice *slave) 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: + * nm_device_master_enslave_slave: * @self: the master device * @slave: the slave device to enslave * @connection: (allow-none): the slave device's connection @@ -1011,7 +1052,7 @@ free_slave_info (SlaveInfo *info) * other devices. */ static gboolean -nm_device_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connection) +nm_device_master_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connection) { SlaveInfo *info; gboolean success = FALSE; @@ -1025,7 +1066,7 @@ nm_device_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connecti if (!info) return FALSE; - if (info->enslaved) + if (info->slave_is_enslaved) success = TRUE; else { configure = (info->configure && connection != NULL); @@ -1033,7 +1074,7 @@ nm_device_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connecti g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); success = NM_DEVICE_GET_CLASS (self)->enslave_slave (self, slave, connection, configure); - info->enslaved = success; + info->slave_is_enslaved = success; } nm_device_slave_notify_enslave (info->slave, success); @@ -1059,7 +1100,7 @@ nm_device_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connecti } /** - * nm_device_release_one_slave: + * nm_device_master_release_one_slave: * @self: the master device * @slave: the slave device to release * @configure: whether @self needs to actually release @slave @@ -1068,49 +1109,55 @@ nm_device_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connecti * If @self is capable of enslaving other devices (ie it's a bridge, bond, team, * etc) then this function releases the previously enslaved @slave and/or * updates the state of @self and @slave to reflect its release. - * - * 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 *self, NMDevice *slave, gboolean configure, NMDeviceStateReason reason) +static void +nm_device_master_release_one_slave (NMDevice *self, NMDevice *slave, gboolean configure, NMDeviceStateReason reason) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; + NMDevicePrivate *slave_priv; SlaveInfo *info; - gboolean success = FALSE; + gs_unref_object NMDevice *self_free = NULL; - g_return_val_if_fail (slave != NULL, FALSE); - g_return_val_if_fail (NM_DEVICE_GET_CLASS (self)->release_slave != NULL, FALSE); + g_return_if_fail (NM_DEVICE (self)); + g_return_if_fail (NM_DEVICE (slave)); + g_return_if_fail (NM_DEVICE_GET_CLASS (self)->release_slave != NULL); info = find_slave_info (self, slave); + + _LOGt (LOGD_CORE, "master: release one slave %p/%s%s", slave, nm_device_get_iface (slave), + !info ? " (not registered)" : ""); + if (!info) - return FALSE; - priv->slaves = g_slist_remove (priv->slaves, info); + g_return_if_reached (); - if (info->enslaved) { - success = NM_DEVICE_GET_CLASS (self)->release_slave (self, slave, configure); - /* The release_slave() implementation logs success/failure (in the - * correct device-specific log domain), so we don't have to do anything. - */ - } + priv = NM_DEVICE_GET_PRIVATE (self); + slave_priv = NM_DEVICE_GET_PRIVATE (slave); - if (!configure) { - g_warn_if_fail (reason == NM_DEVICE_STATE_REASON_NONE || reason == NM_DEVICE_STATE_REASON_REMOVED); - reason = NM_DEVICE_STATE_REASON_NONE; - } else if (reason == NM_DEVICE_STATE_REASON_NONE) { - g_warn_if_reached (); - reason = NM_DEVICE_STATE_REASON_UNKNOWN; - } - nm_device_slave_notify_release (info->slave, reason); + g_return_if_fail (self == slave_priv->master); + nm_assert (slave == info->slave); + + /* first, let subclasses handle the release ... */ + if (info->slave_is_enslaved) + NM_DEVICE_GET_CLASS (self)->release_slave (self, slave, configure); + + /* raise notifications about the release, including clearing is_enslaved. */ + nm_device_slave_notify_release (slave, reason); + + /* keep both alive until the end of the function. + * Transfers ownership from slave_priv->master. */ + self_free = self; + + priv->slaves = g_slist_remove (priv->slaves, info); + slave_priv->master = NULL; - free_slave_info (info); + g_signal_handler_disconnect (slave, info->watch_id); + g_object_unref (slave); + g_slice_free (SlaveInfo, info); /* Ensure the device's hardware address is up-to-date; it often changes * when slaves change. */ nm_device_update_hw_address (self); - - return success; } /** @@ -1123,8 +1170,7 @@ nm_device_release_one_slave (NMDevice *self, NMDevice *slave, gboolean configure static gboolean can_unmanaged_external_down (NMDevice *self) { - return nm_device_is_software (self) - && !nm_device_get_is_nm_owned (self); + return nm_device_is_software (self) && !NM_DEVICE_GET_PRIVATE (self)->is_nm_owned; } /** @@ -1138,7 +1184,6 @@ void nm_device_finish_init (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean platform_unmanaged = FALSE; g_assert (priv->initialized == FALSE); @@ -1147,29 +1192,29 @@ nm_device_finish_init (NMDevice *self) && priv->ifindex > 0 && ( !priv->up || !priv->platform_link_initialized)) - nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_EXTERNAL_DOWN, TRUE); + nm_device_set_unmanaged_flags_initial (self, NM_UNMANAGED_EXTERNAL_DOWN, TRUE); if (priv->master) - nm_device_enslave_slave (priv->master, self, NULL); + nm_device_master_enslave_slave (priv->master, self, NULL); if (priv->ifindex > 0) { if (priv->ifindex == 1) { - /* keep 'lo' as default-unmanaged. */ - - /* FIXME: either find a better way to unmange 'lo' that cannot be changed - * by user configuration (NM_UNMANGED_LOOPBACK?) or fix managing 'lo'. - * Currently it can happen that NM deletes 127.0.0.1 address. */ - nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_DEFAULT, TRUE); + /* Unmanaged the loopback device with an explicit NM_UNMANAGED_LOOPBACK flag. + * Later we might want to manage 'lo' too. Currently that doesn't work because + * NetworkManager might down the interface or remove the 127.0.0.1 address. */ + nm_device_set_unmanaged_flags_initial (self, NM_UNMANAGED_LOOPBACK, TRUE); } else if (priv->platform_link_initialized || (priv->is_nm_owned && nm_device_is_software (self))) { + gboolean platform_unmanaged = FALSE; + if (nm_platform_link_get_unmanaged (NM_PLATFORM_GET, priv->ifindex, &platform_unmanaged)) - nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_DEFAULT, platform_unmanaged); + nm_device_set_unmanaged_flags_initial (self, NM_UNMANAGED_DEFAULT, platform_unmanaged); } else { /* Hardware and externally-created software links stay unmanaged * until they are fully initialized by the platform. NM created * links must be available for activation immediately and thus * do not get the PLATFORM_INIT unmanaged flag set. */ - nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_PLATFORM_INIT, TRUE); + nm_device_set_unmanaged_flags_initial (self, NM_UNMANAGED_PLATFORM_INIT, TRUE); } } @@ -1180,6 +1225,9 @@ static void update_dynamic_ip_setup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + GError *error; + gconstpointer addr; + size_t addr_length; g_hash_table_remove_all (priv->ip6_saved_properties); @@ -1205,6 +1253,18 @@ update_dynamic_ip_setup (NMDevice *self) if (priv->dnsmasq_manager) { /* FIXME: todo */ } + + if (priv->lldp_listener && nm_lldp_listener_is_running (priv->lldp_listener)) { + nm_lldp_listener_stop (priv->lldp_listener); + addr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &addr_length); + + if (!nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), + nm_device_get_iface (self), addr, addr_length, &error)) { + _LOGD (LOGD_DEVICE, "LLDP listener %p could not be restarted: %s", + priv->lldp_listener, error->message); + g_clear_error (&error); + } + } } static void @@ -1342,23 +1402,40 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier) } static void -device_set_master (NMDevice *self, int ifindex) +device_recheck_slave_status (NMDevice *self, const NMPlatformLink *plink) { - NMDevice *master; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - master = nm_manager_get_device_by_ifindex (nm_manager_get (), ifindex); - if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) { - g_clear_object (&priv->master); - priv->master = g_object_ref (master); - nm_device_master_add_slave (master, self, FALSE); - } else if (master) { - _LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring", - nm_device_get_iface (master)); - } else { - _LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s", - ifindex, - nm_platform_link_get_name (NM_PLATFORM_GET, ifindex)); + g_return_if_fail (plink); + + if (plink->master <= 0) + return; + + if (priv->master) { + if ( plink->master > 0 + && plink->master == nm_device_get_ifindex (priv->master)) { + /* call add-slave again. We expect @self already to be added to + * the master, but this also triggers a recheck-assume. */ + nm_device_master_add_slave (priv->master, self, FALSE); + return; + } + + nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + } + if (plink->master > 0) { + NMDevice *master; + + master = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->master); + if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) + nm_device_master_add_slave (master, self, FALSE); + else if (master) { + _LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring", + nm_device_get_iface (master)); + } else { + _LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s", + plink->master, + nm_platform_link_get_name (NM_PLATFORM_GET, plink->master)); + } } } @@ -1369,12 +1446,11 @@ device_link_changed (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMUtilsIPv6IfaceId token_iid; gboolean ip_ifname_changed = FALSE; - gboolean platform_unmanaged = FALSE; const char *udi; NMPlatformLink info; const NMPlatformLink *pllink; int ifindex; - gboolean emit_link_initialized = FALSE; + gboolean just_initialized = FALSE; gboolean was_up; priv->device_link_changed_id = 0; @@ -1407,6 +1483,12 @@ device_link_changed (NMDevice *self) g_object_notify (G_OBJECT (self), NM_DEVICE_MTU); } + if (info.driver && g_strcmp0 (priv->driver, info.driver) != 0) { + g_free (priv->driver); + priv->driver = g_strdup (info.driver); + g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER); + } + if (info.name[0] && strcmp (priv->iface, info.name) != 0) { _LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'", priv->ifindex, priv->iface, info.name); @@ -1429,15 +1511,6 @@ device_link_changed (NMDevice *self) nm_device_emit_recheck_auto_activate (self); } - /* Update slave status for external changes */ - if (priv->enslaved && info.master != nm_device_get_ifindex (priv->master)) - nm_device_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_NONE); - if (info.master && !priv->enslaved) { - device_set_master (self, info.master); - if (priv->master) - nm_device_enslave_slave (priv->master, self, NULL); - } - if (priv->rdisc && nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &token_iid)) { _LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); if (nm_rdisc_set_iid (priv->rdisc, token_iid)) @@ -1452,33 +1525,36 @@ device_link_changed (NMDevice *self) update_dynamic_ip_setup (self); if (priv->ifindex > 0 && !priv->platform_link_initialized && info.initialized) { + gboolean platform_unmanaged = FALSE; + priv->platform_link_initialized = TRUE; if (nm_platform_link_get_unmanaged (NM_PLATFORM_GET, priv->ifindex, &platform_unmanaged)) { - nm_device_set_unmanaged (self, - NM_UNMANAGED_DEFAULT, - platform_unmanaged, - NM_DEVICE_STATE_REASON_USER_REQUESTED); + nm_device_set_unmanaged_flags (self, + NM_UNMANAGED_DEFAULT, + platform_unmanaged, + NM_DEVICE_STATE_REASON_USER_REQUESTED); } - nm_device_set_unmanaged (self, - NM_UNMANAGED_PLATFORM_INIT, - FALSE, - NM_DEVICE_STATE_REASON_NOW_MANAGED); + nm_device_set_unmanaged_flags (self, + NM_UNMANAGED_PLATFORM_INIT, + FALSE, + NM_DEVICE_STATE_REASON_NOW_MANAGED); - emit_link_initialized = TRUE; + just_initialized = TRUE; } was_up = priv->up; priv->up = NM_FLAGS_HAS (info.flags, IFF_UP); if ( priv->platform_link_initialized - && (emit_link_initialized || priv->up != was_up)) { + && ( just_initialized + || priv->up != was_up)) { /* Manage externally-created software interfaces only when they are IFF_UP */ g_assert (priv->ifindex > 0); if (NM_DEVICE_GET_CLASS (self)->can_unmanaged_external_down (self)) { - gboolean external_down = nm_device_get_unmanaged_flag (self, NM_UNMANAGED_EXTERNAL_DOWN); + gboolean external_down = !!nm_device_get_unmanaged_flags (self, NM_UNMANAGED_EXTERNAL_DOWN); if (external_down && NM_FLAGS_HAS (info.flags, IFF_UP)) { if (nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED) { @@ -1492,32 +1568,30 @@ device_link_changed (NMDevice *self) * the device before assumption occurs, pass * NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED as the reason. */ - nm_device_set_unmanaged (self, - NM_UNMANAGED_EXTERNAL_DOWN, - FALSE, - NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + nm_device_set_unmanaged_flags (self, + NM_UNMANAGED_EXTERNAL_DOWN, + FALSE, + NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); } else { /* Don't trigger a state change; if the device is in a * state higher than UNAVAILABLE, it is already IFF_UP * or an explicit activation request was received. */ - priv->unmanaged_flags &= ~NM_UNMANAGED_EXTERNAL_DOWN; + _set_unmanaged_flags (self, NM_UNMANAGED_EXTERNAL_DOWN, FALSE); } } else if (!external_down && !NM_FLAGS_HAS (info.flags, IFF_UP) && nm_device_get_state (self) <= NM_DEVICE_STATE_DISCONNECTED) { /* If the device is already disconnected and is set !IFF_UP, * unmanage it. */ - nm_device_set_unmanaged (self, - NM_UNMANAGED_EXTERNAL_DOWN, - TRUE, - NM_DEVICE_STATE_REASON_USER_REQUESTED); + nm_device_set_unmanaged_flags (self, + NM_UNMANAGED_EXTERNAL_DOWN, + TRUE, + NM_DEVICE_STATE_REASON_USER_REQUESTED); } } } - if (emit_link_initialized) - g_signal_emit (self, signals[LINK_INITIALIZED], 0); - + device_recheck_slave_status (self, &info); return G_SOURCE_REMOVE; } @@ -1526,12 +1600,13 @@ device_ip_link_changed (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const NMPlatformLink *pllink; - int ip_ifindex; priv->device_ip_link_changed_id = 0; - ip_ifindex = nm_device_get_ip_ifindex (self); - pllink = nm_platform_link_get (NM_PLATFORM_GET, ip_ifindex); + if (!priv->ip_ifindex) + return G_SOURCE_REMOVE; + + pllink = nm_platform_link_get (NM_PLATFORM_GET, priv->ip_ifindex); if (!pllink) return G_SOURCE_REMOVE; @@ -1554,7 +1629,6 @@ link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, - NMPlatformReason reason, NMDevice *self) { NMDevicePrivate *priv; @@ -1564,12 +1638,6 @@ link_changed_cb (NMPlatform *platform, priv = NM_DEVICE_GET_PRIVATE (self); - /* We don't filter by 'reason' because we are interested in *all* link - * changes. For example a call to nm_platform_link_set_up() may result - * in an internal carrier change (i.e. we ask the kernel to set IFF_UP - * and it results in also setting IFF_LOWER_UP. - */ - if (ifindex == nm_device_get_ifindex (self)) { if (!priv->device_link_changed_id) { priv->device_link_changed_id = g_idle_add ((GSourceFunc) device_link_changed, self); @@ -1592,6 +1660,165 @@ link_changed (NMDevice *self, NMPlatformLink *info) nm_device_set_carrier (self, info->connected); } +static gboolean +link_type_compatible (NMDevice *self, + NMLinkType link_type, + gboolean *out_compatible, + GError **error) +{ + NMDeviceClass *klass; + NMLinkType device_type; + guint i = 0; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + klass = NM_DEVICE_GET_CLASS (self); + + if (!klass->link_types) { + NM_SET_OUT (out_compatible, FALSE); + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + "Device does not support platform links"); + return FALSE; + } + + device_type = self->priv->link_type; + if (device_type > NM_LINK_TYPE_UNKNOWN && device_type != link_type) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + "Needed link type 0x%x does not match the platform link type 0x%X", + device_type, link_type); + return FALSE; + } + + for (i = 0; klass->link_types[i] > NM_LINK_TYPE_UNKNOWN; i++) { + if (klass->link_types[i] == link_type) + return TRUE; + if (klass->link_types[i] == NM_LINK_TYPE_ANY) + return TRUE; + } + + NM_SET_OUT (out_compatible, FALSE); + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + "Device does not support platform link type 0x%X", + link_type); + return FALSE; +} + +/** + * nm_device_realize_start(): + * @self: the #NMDevice + * @plink: an existing platform link or %NULL + * @out_compatible: %TRUE on return if @self is compatible with @plink + * @error: location to store error, or %NULL + * + * Initializes and sets up the device using existing backing resources. Before + * the device is ready for use nm_device_realize_finish() must be called. + * @out_compatible will only be set if @plink is not %NULL, and + * + * Important: if nm_device_realize_start() returns %TRUE, the caller MUST + * also call nm_device_realize_finish() to balance g_object_freeze_notify(). + * + * Returns: %TRUE on success, %FALSE on error + */ +gboolean +nm_device_realize_start (NMDevice *self, + const NMPlatformLink *plink, + gboolean *out_compatible, + GError **error) +{ + NM_SET_OUT (out_compatible, TRUE); + + if (plink) { + if (g_strcmp0 (nm_device_get_iface (self), plink->name) != 0) { + NM_SET_OUT (out_compatible, FALSE); + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, + "Device interface name does not match platform link"); + return FALSE; + } + + if (!link_type_compatible (self, plink->type, out_compatible, error)) + return FALSE; + } + + realize_start_setup (self, plink); + + return TRUE; +} + +/** + * nm_device_create_and_realize(): + * @self: the #NMDevice + * @connection: the #NMConnection being activated + * @parent: the parent #NMDevice if any + * @error: location to store error, or %NULL + * + * Creates any backing resources needed to realize the device to proceed + * with activating @connection. + * + * Returns: %TRUE on success, %FALSE on error + */ +gboolean +nm_device_create_and_realize (NMDevice *self, + NMConnection *connection, + NMDevice *parent, + GError **error) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMPlatformLink plink_copy; + const NMPlatformLink *plink = NULL; + + /* Must be set before device is realized */ + priv->is_nm_owned = !nm_platform_link_get_by_ifname (NM_PLATFORM_GET, priv->iface); + + _LOGD (LOGD_DEVICE, "create (is %snm-owned)", priv->is_nm_owned ? "" : "not "); + + /* Create any resources the device needs */ + if (NM_DEVICE_GET_CLASS (self)->create_and_realize) { + if (!NM_DEVICE_GET_CLASS (self)->create_and_realize (self, connection, parent, &plink, error)) + return FALSE; + plink_copy = *plink; + plink = &plink_copy; + } + + realize_start_setup (self, plink); + nm_device_realize_finish (self, plink); + + g_return_val_if_fail (nm_device_check_connection_compatible (self, connection), TRUE); + return TRUE; +} + +static void +update_device_from_platform_link (NMDevice *self, const NMPlatformLink *plink) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const char *udi; + + g_return_if_fail (plink != NULL); + + udi = nm_platform_link_get_udi (NM_PLATFORM_GET, plink->ifindex); + if (udi && !g_strcmp0 (udi, priv->udi)) { + g_free (priv->udi); + priv->udi = g_strdup (udi); + g_object_notify (G_OBJECT (self), NM_DEVICE_UDI); + } + + if (!g_strcmp0 (plink->name, priv->iface)) { + g_free (priv->iface); + priv->iface = g_strdup (plink->name); + g_object_notify (G_OBJECT (self), NM_DEVICE_IFACE); + } + + priv->ifindex = plink->ifindex; + g_object_notify (G_OBJECT (self), NM_DEVICE_IFINDEX); + + priv->up = NM_FLAGS_HAS (plink->flags, IFF_UP); + if (plink->driver && g_strcmp0 (plink->driver, priv->driver) != 0) { + g_free (priv->driver); + priv->driver = g_strdup (plink->driver); + g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER); + } + priv->platform_link_initialized = plink->initialized; +} + static void config_changed_update_ignore_carrier (NMConfig *config, NMConfigData *config_data, @@ -1615,6 +1842,317 @@ check_carrier (NMDevice *self) nm_device_set_carrier (self, nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); } +static void +realize_start_notify (NMDevice *self, const NMPlatformLink *plink) +{ + /* Stub implementation for realize_start_notify(). It does nothing, + * but allows derived classes to uniformly invoke the parent + * implementation. */ +} + +/** + * realize_start_setup(): + * @self: the #NMDevice + * @plink: the #NMPlatformLink if backed by a kernel netdevice + * + * Update the device from backing resource properties (like hardware + * addresses, carrier states, driver/firmware info, etc). This function + * should only change properties for this device, and should not perform + * any tasks that affect other interfaces (like master/slave or parent/child + * stuff). + */ +static void +realize_start_setup (NMDevice *self, const NMPlatformLink *plink) +{ + NMDevicePrivate *priv; + NMDeviceClass *klass; + static guint32 id = 0; + + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); + + /* The device should not be realized */ + g_return_if_fail (!priv->real); + g_return_if_fail (priv->ip_ifindex <= 0); + g_return_if_fail (priv->ip_iface == NULL); + + _LOGD (LOGD_DEVICE, "start setup of %s, kernel ifindex %d", G_OBJECT_TYPE_NAME (self), plink ? plink->ifindex : 0); + + klass = NM_DEVICE_GET_CLASS (self); + + /* Balanced by a thaw in nm_device_realize_finish() */ + g_object_freeze_notify (G_OBJECT (self)); + + if (plink) { + g_return_if_fail (link_type_compatible (self, plink->type, NULL, NULL)); + update_device_from_platform_link (self, plink); + } + + if (priv->ifindex > 0) { + priv->physical_port_id = nm_platform_link_get_physical_port_id (NM_PLATFORM_GET, priv->ifindex); + g_object_notify (G_OBJECT (self), NM_DEVICE_PHYSICAL_PORT_ID); + + priv->dev_id = nm_platform_link_get_dev_id (NM_PLATFORM_GET, priv->ifindex); + + if (nm_platform_link_is_software (NM_PLATFORM_GET, priv->ifindex)) + priv->capabilities |= NM_DEVICE_CAP_IS_SOFTWARE; + + priv->mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET, priv->ifindex); + g_object_notify (G_OBJECT (self), NM_DEVICE_MTU); + + nm_platform_link_get_driver_info (NM_PLATFORM_GET, + priv->ifindex, + NULL, + &priv->driver_version, + &priv->firmware_version); + if (priv->driver_version) + g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER_VERSION); + if (priv->firmware_version) + g_object_notify (G_OBJECT (self), NM_DEVICE_FIRMWARE_VERSION); + + if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET)) + priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, priv->ifindex); + } + + if (klass->get_generic_capabilities) + priv->capabilities |= klass->get_generic_capabilities (self); + + if (!priv->udi) { + /* Use a placeholder UDI until we get a real one */ + priv->udi = g_strdup_printf ("/virtual/device/placeholder/%d", id++); + g_object_notify (G_OBJECT (self), NM_DEVICE_UDI); + } + + /* trigger initial ip config change to initialize ip-config */ + priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); + priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); + + nm_device_update_hw_address (self); + nm_device_update_initial_hw_address (self); + + /* Note: initial hardware address must be read before calling get_ignore_carrier() */ + if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { + NMConfig *config = nm_config_get (); + + priv->ignore_carrier = nm_config_data_get_ignore_carrier (nm_config_get_data (config), self); + g_signal_connect (G_OBJECT (config), + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK (config_changed_update_ignore_carrier), + self); + + check_carrier (self); + _LOGD (LOGD_HW, + "carrier is %s%s", + priv->carrier ? "ON" : "OFF", + priv->ignore_carrier ? " (but ignored)" : ""); + } else { + /* Fake online link when carrier detection is not available. */ + priv->carrier = TRUE; + } + + g_object_notify (G_OBJECT (self), NM_DEVICE_CAPABILITIES); + + klass->realize_start_notify (self, plink); +} + +/** + * nm_device_realize_finish(): + * @self: the #NMDevice + * @plink: the #NMPlatformLink if backed by a kernel netdevice + * + * Update the device's master/slave or parent/child relationships from + * backing resource properties. After this function finishes, the device + * is ready for network connectivity. + */ +void +nm_device_realize_finish (NMDevice *self, const NMPlatformLink *plink) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + g_return_if_fail (!plink || link_type_compatible (self, plink->type, NULL, NULL)); + + priv = NM_DEVICE_GET_PRIVATE (self); + + g_return_if_fail (!priv->real); + + if (plink) { + update_device_from_platform_link (self, plink); + device_recheck_slave_status (self, plink); + } + + priv->real = TRUE; + g_object_notify (G_OBJECT (self), NM_DEVICE_REAL); + + nm_device_recheck_available_connections (self); + + /* Balanced by a freeze in realize_start_setup() */ + g_object_thaw_notify (G_OBJECT (self)); +} + +static void +unrealize_notify (NMDevice *self) +{ + /* Stub implementation for unrealize_notify(). It does nothing, + * but allows derived classes to uniformly invoke the parent + * implementation. */ +} + +static gboolean +available_connection_check_delete_unrealized_on_idle (gpointer user_data) +{ + NMDevice *self = user_data; + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE); + + priv = NM_DEVICE_GET_PRIVATE (self); + + priv->check_delete_unrealized_id = 0; + + if ( g_hash_table_size (priv->available_connections) == 0 + && !nm_device_is_real (self)) + g_signal_emit (self, signals[REMOVED], 0); + + return G_SOURCE_REMOVE; +} + +static void +available_connection_check_delete_unrealized (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + /* always rescheadule the remove signal. */ + nm_clear_g_source (&priv->check_delete_unrealized_id); + + if ( g_hash_table_size (priv->available_connections) == 0 + && !nm_device_is_real (self)) + priv->check_delete_unrealized_id = g_idle_add (available_connection_check_delete_unrealized_on_idle, self); +} + +/** + * nm_device_unrealize(): + * @self: the #NMDevice + * @remove_resources: if %TRUE, remove backing resources + * @error: location to store error, or %NULL + * + * Clears any properties that depend on backing resources (kernel devices, + * etc) and removes those resources if @remove_resources is %TRUE. + * + * Returns: %TRUE on success, %FALSE on error + */ +gboolean +nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) +{ + NMDevicePrivate *priv; + int ifindex; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + if (!nm_device_is_software (self) || !nm_device_is_real (self)) { + g_set_error_literal (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_SOFTWARE, + "This device is not a software device or is not realized"); + return FALSE; + } + + priv = NM_DEVICE_GET_PRIVATE (self); + + g_return_val_if_fail (priv->iface != NULL, FALSE); + g_return_val_if_fail (priv->real, FALSE); + + g_object_freeze_notify (G_OBJECT (self)); + + ifindex = nm_device_get_ifindex (self); + + _LOGD (LOGD_DEVICE, "unrealize (ifindex %d)", ifindex > 0 ? ifindex : 0); + + if (remove_resources) { + if (ifindex > 0) + nm_platform_link_delete (NM_PLATFORM_GET, ifindex); + } + + NM_DEVICE_GET_CLASS (self)->unrealize_notify (self); + + if (priv->ifindex > 0) { + priv->ifindex = 0; + g_object_notify (G_OBJECT (self), NM_DEVICE_IFINDEX); + } + priv->ip_ifindex = 0; + if (priv->ip_iface) { + g_clear_pointer (&priv->ip_iface, g_free); + g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE); + } + if (priv->driver_version) { + g_clear_pointer (&priv->driver_version, g_free); + g_object_notify (G_OBJECT (self), NM_DEVICE_DRIVER_VERSION); + } + if (priv->firmware_version) { + g_clear_pointer (&priv->firmware_version, g_free); + g_object_notify (G_OBJECT (self), NM_DEVICE_FIRMWARE_VERSION); + } + if (priv->udi) { + g_clear_pointer (&priv->udi, g_free); + g_object_notify (G_OBJECT (self), NM_DEVICE_UDI); + } + if (priv->hw_addr) { + g_clear_pointer (&priv->hw_addr, g_free); + g_object_notify (G_OBJECT (self), NM_DEVICE_HW_ADDRESS); + } + if (priv->physical_port_id) { + g_clear_pointer (&priv->physical_port_id, g_free); + g_object_notify (G_OBJECT (self), NM_DEVICE_PHYSICAL_PORT_ID); + } + + g_clear_pointer (&priv->perm_hw_addr, g_free); + g_clear_pointer (&priv->initial_hw_addr, g_free); + + priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED; + if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities) + priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self); + g_object_notify (G_OBJECT (self), NM_DEVICE_CAPABILITIES); + + priv->real = FALSE; + g_object_notify (G_OBJECT (self), NM_DEVICE_REAL); + + nm_device_set_autoconnect (self, DEFAULT_AUTOCONNECT); + + g_object_thaw_notify (G_OBJECT (self)); + + nm_device_state_changed (self, + NM_DEVICE_STATE_UNMANAGED, + remove_resources ? + NM_DEVICE_STATE_REASON_USER_REQUESTED : NM_DEVICE_STATE_REASON_NOW_UNMANAGED); + + /* Garbage-collect unneeded unrealized devices. */ + nm_device_recheck_available_connections (self); + + return TRUE; +} + +/** + * nm_device_notify_new_device_added(): + * @self: the #NMDevice + * @device: the newly added device + * + * Called by the manager to notify the device that a new device has + * been found and added. + */ +void +nm_device_notify_new_device_added (NMDevice *self, NMDevice *device) +{ + NMDeviceClass *klass; + + g_return_if_fail (NM_IS_DEVICE (self)); + g_return_if_fail (NM_IS_DEVICE (device)); + + klass = NM_DEVICE_GET_CLASS (self); + if (klass->notify_new_device_added) + klass->notify_new_device_added (self, device); +} + /** * nm_device_notify_component_added(): * @self: the #NMDevice @@ -1630,8 +2168,14 @@ check_carrier (NMDevice *self) gboolean nm_device_notify_component_added (NMDevice *self, GObject *component) { - if (NM_DEVICE_GET_CLASS (self)->component_added) - return NM_DEVICE_GET_CLASS (self)->component_added (self, component); + NMDeviceClass *klass; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (G_IS_OBJECT (component), FALSE); + + klass = NM_DEVICE_GET_CLASS (self); + if (klass->component_added) + return klass->component_added (self, component); return FALSE; } @@ -1687,7 +2231,7 @@ slave_state_changed (NMDevice *slave, return; if (slave_new_state == NM_DEVICE_STATE_IP_CONFIG) - nm_device_enslave_slave (self, slave, nm_device_get_connection (slave)); + nm_device_master_enslave_slave (self, slave, nm_device_get_applied_connection (slave)); else if (slave_new_state > NM_DEVICE_STATE_ACTIVATED) release = TRUE; else if ( slave_new_state <= NM_DEVICE_STATE_DISCONNECTED @@ -1697,7 +2241,7 @@ slave_state_changed (NMDevice *slave, } if (release) { - nm_device_release_one_slave (self, slave, TRUE, reason); + nm_device_master_release_one_slave (self, slave, TRUE, reason); /* Bridge/bond/team interfaces are left up until manually deactivated */ if (priv->slaves == NULL && priv->state == NM_DEVICE_STATE_ACTIVATED) _LOGD (LOGD_DEVICE, "last slave removed; remaining activated"); @@ -1713,33 +2257,53 @@ slave_state_changed (NMDevice *slave, * * If @self is capable of enslaving other devices (ie it's a bridge, bond, team, * etc) then this function adds @slave to the slave list for later enslavement. - * - * Returns: %TRUE on success, %FALSE on failure */ -static gboolean +static void nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; + NMDevicePrivate *slave_priv; SlaveInfo *info; - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (slave != NULL, FALSE); - g_return_val_if_fail (NM_DEVICE_GET_CLASS (self)->enslave_slave != NULL, FALSE); + g_return_if_fail (NM_IS_DEVICE (self)); + g_return_if_fail (NM_IS_DEVICE (slave)); + g_return_if_fail (NM_DEVICE_GET_CLASS (self)->enslave_slave != NULL); + + priv = NM_DEVICE_GET_PRIVATE (self); + slave_priv = NM_DEVICE_GET_PRIVATE (slave); + + info = find_slave_info (self, slave); + + _LOGt (LOGD_CORE, "master: add one slave %p/%s%s", slave, nm_device_get_iface (slave), + info ? " (already registered)" : ""); if (configure) - g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); + g_return_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED); - if (!find_slave_info (self, slave)) { - info = g_malloc0 (sizeof (SlaveInfo)); + if (!info) { + g_return_if_fail (!slave_priv->master); + g_return_if_fail (!slave_priv->is_enslaved); + + info = g_slice_new0 (SlaveInfo); info->slave = g_object_ref (slave); info->configure = configure; - info->watch_id = g_signal_connect (slave, "state-changed", + info->watch_id = g_signal_connect (slave, + NM_DEVICE_STATE_CHANGED, G_CALLBACK (slave_state_changed), self); priv->slaves = g_slist_append (priv->slaves, info); - } - nm_device_queue_recheck_assume (self); + slave_priv->master = g_object_ref (self); - return TRUE; + /* no need to emit + * + * g_object_notify (G_OBJECT (slave), NM_DEVICE_MASTER); + * + * because slave_priv->is_enslaved is not true, thus the value + * didn't change yet. */ + } else + g_return_if_fail (slave_priv->master == self); + + nm_device_queue_recheck_assume (self); + nm_device_queue_recheck_assume (slave); } @@ -1749,7 +2313,7 @@ nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure) * * Returns: any slaves of which @self is the master. Caller owns returned list. */ -GSList * +static GSList * nm_device_master_get_slaves (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -1794,7 +2358,7 @@ nm_device_master_get_slave_by_ifindex (NMDevice *self, int ifindex) */ void nm_device_master_check_slave_physical_port (NMDevice *self, NMDevice *slave, - guint64 log_domain) + NMLogDomain log_domain) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const char *slave_physical_port_id, *existing_physical_port_id; @@ -1831,6 +2395,7 @@ nm_device_master_release_slaves (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDeviceStateReason reason; + gboolean configure = TRUE; /* Don't release the slaves if this connection doesn't belong to NM. */ if (nm_device_uses_generated_assumed_connection (self)) @@ -1840,14 +2405,29 @@ nm_device_master_release_slaves (NMDevice *self) if (priv->state == NM_DEVICE_STATE_FAILED) reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED; + if (!nm_platform_link_get (NM_PLATFORM_GET, priv->ifindex)) + configure = FALSE; + while (priv->slaves) { SlaveInfo *info = priv->slaves->data; - nm_device_release_one_slave (self, info->slave, TRUE, reason); + nm_device_master_release_one_slave (self, info->slave, configure, reason); } } /** + * nm_device_is_master: + * @self: the device + * + * Returns: %TRUE if the device can have slaves + */ +gboolean +nm_device_is_master (NMDevice *self) +{ + return NM_DEVICE_GET_PRIVATE (self)->is_master; +} + +/** * nm_device_get_master: * @self: the device * @@ -1863,10 +2443,11 @@ nm_device_get_master (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->enslaved) + if (priv->is_enslaved) { + g_return_val_if_fail (priv->master, NULL); return priv->master; - else - return NULL; + } + return NULL; } /** @@ -1881,12 +2462,12 @@ static void nm_device_slave_notify_enslave (NMDevice *self, gboolean success) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection = nm_device_get_connection (self); + NMConnection *connection = nm_device_get_applied_connection (self); gboolean activating = (priv->state == NM_DEVICE_STATE_IP_CONFIG); - g_assert (priv->master); + g_return_if_fail (priv->master); - if (!priv->enslaved) { + if (!priv->is_enslaved) { if (success) { if (activating) { _LOGI (LOGD_DEVICE, "Activation: connection '%s' enslaved, continuing activation", @@ -1894,8 +2475,9 @@ nm_device_slave_notify_enslave (NMDevice *self, gboolean success) } else _LOGI (LOGD_DEVICE, "enslaved to %s", nm_device_get_iface (priv->master)); - priv->enslaved = TRUE; + priv->is_enslaved = TRUE; g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER); + g_object_notify (G_OBJECT (priv->master), NM_DEVICE_SLAVES); } else if (activating) { _LOGW (LOGD_DEVICE, "Activation: connection '%s' could not be enslaved", nm_connection_get_id (connection)); @@ -1924,12 +2506,13 @@ static void nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection = nm_device_get_connection (self); + NMConnection *connection = nm_device_get_applied_connection (self); NMDeviceState new_state; const char *master_status; - if ( reason != NM_DEVICE_STATE_REASON_NONE - && priv->state > NM_DEVICE_STATE_DISCONNECTED + g_return_if_fail (priv->master); + + if ( priv->state > NM_DEVICE_STATE_DISCONNECTED && priv->state <= NM_DEVICE_STATE_ACTIVATED) { if (reason == NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED) { new_state = NM_DEVICE_STATE_FAILED; @@ -1947,14 +2530,13 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason) master_status); nm_device_queue_state (self, new_state, reason); - } else if (priv->master) - _LOGI (LOGD_DEVICE, "released from master %s", nm_device_get_iface (priv->master)); - else - _LOGD (LOGD_DEVICE, "released from master%s", priv->enslaved ? "" : " (was not enslaved)"); + } else + _LOGI (LOGD_DEVICE, "released from master device %s", nm_device_get_iface (priv->master)); - if (priv->enslaved) { - priv->enslaved = FALSE; + if (priv->is_enslaved) { + priv->is_enslaved = FALSE; g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER); + g_object_notify (G_OBJECT (priv->master), NM_DEVICE_SLAVES); } } @@ -1968,7 +2550,7 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason) gboolean nm_device_get_enslaved (NMDevice *self) { - return NM_DEVICE_GET_PRIVATE (self)->enslaved; + return NM_DEVICE_GET_PRIVATE (self)->is_enslaved; } /** @@ -1981,12 +2563,17 @@ nm_device_get_enslaved (NMDevice *self) void nm_device_removed (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; - if (priv->enslaved) - nm_device_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_REMOVED); -} + g_return_if_fail (NM_IS_DEVICE (self)); + priv = NM_DEVICE_GET_PRIVATE (self); + if (priv->master) { + /* this is called when something externally messes with the slave or during shut-down. + * Release the slave from master, but don't touch the device. */ + nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + } +} static gboolean is_available (NMDevice *self, NMDeviceCheckDevAvailableFlags flags) @@ -1996,7 +2583,7 @@ is_available (NMDevice *self, NMDeviceCheckDevAvailableFlags flags) if (priv->carrier || priv->ignore_carrier) return TRUE; - if (NM_FLAGS_HAS (flags, NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER)) + if (NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER)) return TRUE; return FALSE; @@ -2068,7 +2655,7 @@ nm_device_get_autoconnect (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->autoconnect; } -static void +void nm_device_set_autoconnect (NMDevice *self, gboolean autoconnect) { NMDevicePrivate *priv; @@ -2116,7 +2703,11 @@ nm_device_autoconnect_allowed (NMDevice *self) GValue instance = G_VALUE_INIT; GValue retval = G_VALUE_INIT; - if (priv->state < NM_DEVICE_STATE_DISCONNECTED || !priv->autoconnect) + if (!priv->autoconnect) + return FALSE; + + /* Unrealized devices can always autoconnect. */ + if (nm_device_is_real (self) && priv->state < NM_DEVICE_STATE_DISCONNECTED) return FALSE; /* The 'autoconnect-allowed' signal is emitted on a device to allow @@ -2130,15 +2721,13 @@ nm_device_autoconnect_allowed (NMDevice *self) g_value_set_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); + g_value_set_boolean (&retval, TRUE); /* 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); g_value_unset (&instance); + return g_value_get_boolean (&retval); } @@ -2199,7 +2788,7 @@ device_has_config (NMDevice *self) return TRUE; /* The existence of a software device is good enough. */ - if (nm_device_is_software (self)) + if (nm_device_is_software (self) && nm_device_is_real (self)) return TRUE; /* Slaves are also configured by definition */ @@ -2429,6 +3018,34 @@ nm_device_check_connection_compatible (NMDevice *self, NMConnection *connection) return NM_DEVICE_GET_CLASS (self)->check_connection_compatible (self, connection); } +gboolean +nm_device_check_slave_connection_compatible (NMDevice *self, NMConnection *slave) +{ + NMDevicePrivate *priv; + NMSettingConnection *s_con; + const char *connection_type, *slave_type; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (slave), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (!priv->is_master) + return FALSE; + + /* All masters should have connection type set */ + connection_type = NM_DEVICE_GET_CLASS (self)->connection_type; + g_return_val_if_fail (connection_type, FALSE); + + s_con = nm_connection_get_setting_connection (slave); + g_assert (s_con); + slave_type = nm_setting_connection_get_slave_type (s_con); + if (!slave_type) + return FALSE; + + return strcmp (connection_type, slave_type) == 0; +} + /** * nm_device_can_assume_connections: * @self: #NMDevice instance @@ -2441,7 +3058,8 @@ nm_device_check_connection_compatible (NMDevice *self, NMConnection *connection) static gboolean nm_device_can_assume_connections (NMDevice *self) { - return !!NM_DEVICE_GET_CLASS (self)->update_connection; + return !!NM_DEVICE_GET_CLASS (self)->update_connection + && !NM_DEVICE_GET_PRIVATE (self)->is_nm_owned; } /** @@ -2481,7 +3099,7 @@ nm_device_can_assume_active_connection (NMDevice *self) if (!nm_device_can_assume_connections (self)) return FALSE; - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); if (!connection) return FALSE; @@ -2505,9 +3123,14 @@ nm_device_can_assume_active_connection (NMDevice *self) } static gboolean -nm_device_emit_recheck_assume (gpointer self) +nm_device_emit_recheck_assume (gpointer user_data) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevice *self = user_data; + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE); + + priv = NM_DEVICE_GET_PRIVATE (self); priv->recheck_assume_id = 0; if (!nm_device_get_act_request (self)) { @@ -2522,7 +3145,8 @@ nm_device_queue_recheck_assume (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (nm_device_can_assume_connections (self) && !priv->recheck_assume_id) + if ( !priv->recheck_assume_id + && nm_device_can_assume_connections (self)) priv->recheck_assume_id = g_idle_add (nm_device_emit_recheck_assume, self); } @@ -2544,13 +3168,17 @@ recheck_available (gpointer user_data) new_state = NM_DEVICE_STATE_UNAVAILABLE; nm_device_queue_state (self, new_state, priv->recheck_available.unavailable_reason); } - _LOGD (LOGD_DEVICE, "device is %savailable, %s %s", - now_available ? "" : "not ", - new_state == NM_DEVICE_STATE_UNAVAILABLE ? "no change required for" : "will transition to", - state_to_string (new_state == NM_DEVICE_STATE_UNAVAILABLE ? state : new_state)); - priv->recheck_available.available_reason = NM_DEVICE_STATE_REASON_NONE; - priv->recheck_available.unavailable_reason = NM_DEVICE_STATE_REASON_NONE; + if (new_state > NM_DEVICE_STATE_UNKNOWN) { + _LOGD (LOGD_DEVICE, "device is %savailable, %s %s", + now_available ? "" : "not ", + new_state == NM_DEVICE_STATE_UNAVAILABLE ? "no change required for" : "will transition to", + state_to_string (new_state == NM_DEVICE_STATE_UNAVAILABLE ? state : new_state)); + + priv->recheck_available.available_reason = NM_DEVICE_STATE_REASON_NONE; + priv->recheck_available.unavailable_reason = NM_DEVICE_STATE_REASON_NONE; + } + return G_SOURCE_REMOVE; } @@ -2587,63 +3215,119 @@ dnsmasq_state_changed_cb (NMDnsMasqManager *manager, guint32 status, gpointer us } } -static void -activation_source_clear (NMDevice *self, gboolean remove_source, int family) +/*****************************************************************************/ + +static gboolean +activation_source_handle_cb4 (gpointer user_data) +{ + activation_source_handle_cb (user_data, AF_INET); + return G_SOURCE_REMOVE; +} + +static gboolean +activation_source_handle_cb6 (gpointer user_data) +{ + activation_source_handle_cb (user_data, AF_INET6); + return G_SOURCE_REMOVE; +} + +static ActivationHandleData * +activation_source_get_by_family (NMDevice *self, + int family, + GSourceFunc *out_idle_func) { 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; + NM_SET_OUT (out_idle_func, activation_source_handle_cb6); + return &priv->act_handle6; } else { - act_source_id = &priv->act_source_id; - act_source_func = &priv->act_source_func; + NM_SET_OUT (out_idle_func, activation_source_handle_cb4); + g_return_val_if_fail (family == AF_INET, &priv->act_handle4); + return &priv->act_handle4; } +} - 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_clear (NMDevice *self, int family) +{ + ActivationHandleData *act_data; + + act_data = activation_source_get_by_family (self, family, NULL); + + if (act_data->id) { + _LOGD (LOGD_DEVICE, "activation-stage: clear %s,%d (id %u)", + _activation_func_to_string (act_data->func), family, act_data->id); + nm_clear_g_source (&act_data->id); + act_data->func = NULL; } } static void -activation_source_schedule (NMDevice *self, GSourceFunc func, int family) +activation_source_handle_cb (NMDevice *self, int family) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - guint *act_source_id; - gpointer *act_source_func; + ActivationHandleData *act_data, a; - 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; + g_return_if_fail (NM_IS_DEVICE (self)); + + act_data = activation_source_get_by_family (self, family, NULL); + + g_return_if_fail (act_data->id); + g_return_if_fail (act_data->func); + + a = *act_data; + + act_data->func = NULL; + act_data->id = 0; + + _LOGD (LOGD_DEVICE, "activation-stage: invoke %s,%d (id %u)", + _activation_func_to_string (a.func), family, a.id); + + a.func (self); + + _LOGD (LOGD_DEVICE, "activation-stage: complete %s,%d (id %u)", + _activation_func_to_string (a.func), family, a.id); +} + +static void +activation_source_schedule (NMDevice *self, ActivationHandleFunc func, int family) +{ + ActivationHandleData *act_data; + GSourceFunc source_func; + guint new_id = 0; + + act_data = activation_source_get_by_family (self, family, &source_func); + + if (act_data->id && act_data->func != func) { + /* 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. + */ + _LOGD (LOGD_DEVICE, "activation-stage: already scheduled %s,%d (id %u)", + _activation_func_to_string (func), family, act_data->id); + return; } - if (*act_source_id) { - if (*act_source_func == func) { - /* 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. - */ - _LOGD (LOGD_DEVICE, "activation stage already scheduled"); - return; - } else { - _LOGW (LOGD_DEVICE, "a different activation stage already scheduled"); - activation_source_clear (self, TRUE, family); - } + new_id = g_idle_add (source_func, self); + + if (act_data->id) { + _LOGW (LOGD_DEVICE, "activation-stage: schedule %s,%d which replaces %s,%d (id %u -> %u)", + _activation_func_to_string (func), family, + _activation_func_to_string (act_data->func), family, + act_data->id, new_id); + nm_clear_g_source (&act_data->id); + } else { + _LOGD (LOGD_DEVICE, "activation-stage: schedule %s,%d (id %u)", + _activation_func_to_string (func), family, new_id); } - *act_source_id = g_idle_add (func, self); - *act_source_func = func; + act_data->func = func; + act_data->id = new_id; } +/*****************************************************************************/ + static gboolean get_ip_config_may_fail (NMDevice *self, int family) { @@ -2652,7 +3336,7 @@ get_ip_config_may_fail (NMDevice *self, int family) g_return_val_if_fail (self != NULL, TRUE); - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); g_assert (connection); /* Fail the connection if the failed IP method is required to complete */ @@ -2675,26 +3359,31 @@ master_ready (NMDevice *self, NMActiveConnection *active) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActiveConnection *master; + NMActiveConnection *master_connection; + NMDevice *master; g_return_if_fail (priv->state == NM_DEVICE_STATE_PREPARE); g_return_if_fail (!priv->master_ready_handled); /* Notify a master device that it has a new slave */ g_return_if_fail (nm_active_connection_get_master_ready (active)); - master = nm_active_connection_get_master (active); + master_connection = nm_active_connection_get_master (active); priv->master_ready_handled = TRUE; nm_clear_g_signal_handler (active, &priv->master_ready_id); - priv->master = g_object_ref (nm_active_connection_get_device (master)); - nm_device_master_add_slave (priv->master, - self, - nm_active_connection_get_assumed (active) ? FALSE : TRUE); + master = nm_active_connection_get_device (master_connection); _LOGD (LOGD_DEVICE, "master connection ready; master device %s", - nm_device_get_iface (priv->master)); + nm_device_get_iface (master)); + + if (priv->master && priv->master != master) + nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + /* If the master didn't change, add-slave only rechecks whether to assume a connection. */ + nm_device_master_add_slave (master, + self, + nm_active_connection_get_assumed (active) ? FALSE : TRUE); } static void @@ -2706,6 +3395,45 @@ master_ready_cb (NMActiveConnection *active, nm_device_activate_schedule_stage2_device_config (self); } +static void +lldp_neighbors_changed (NMLldpListener *lldp_listener, GParamSpec *pspec, + gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + + g_object_notify (G_OBJECT (self), NM_DEVICE_LLDP_NEIGHBORS); +} + +static gboolean +lldp_rx_enabled (NMDevice *self) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingConnectionLldp lldp = NM_SETTING_CONNECTION_LLDP_DEFAULT; + + connection = nm_device_get_applied_connection (self); + g_return_val_if_fail (connection, FALSE); + + s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (s_con, FALSE); + + lldp = nm_setting_connection_get_lldp (s_con); + if (lldp == NM_SETTING_CONNECTION_LLDP_DEFAULT) { + gs_free char *value = NULL; + + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "connection.lldp", + self); + lldp = _nm_utils_ascii_str_to_int64 (value, 10, + NM_SETTING_CONNECTION_LLDP_DEFAULT, + NM_SETTING_CONNECTION_LLDP_ENABLE_RX, + NM_SETTING_CONNECTION_LLDP_DEFAULT); + if (lldp == NM_SETTING_CONNECTION_LLDP_DEFAULT) + lldp = NM_SETTING_CONNECTION_LLDP_DISABLE; + } + return lldp == NM_SETTING_CONNECTION_LLDP_ENABLE_RX; +} + static NMActStageReturn act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason) { @@ -2713,48 +3441,39 @@ act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason) } /* - * nm_device_activate_stage1_device_prepare + * activate_stage1_device_prepare * * Prepare for device activation * */ -static gboolean -nm_device_activate_stage1_device_prepare (gpointer user_data) +static void +activate_stage1_device_prepare (NMDevice *self) { - NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request); - /* 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; /* Notify the new ActiveConnection along with the state change */ g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION); - _LOGD (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) started..."); nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); /* Assumed connections were already set up outside NetworkManager */ if (!nm_active_connection_get_assumed (active)) { ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason); if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { - goto out; + return; } else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - goto out; + return; } g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); } nm_device_activate_schedule_stage2_device_config (self); - -out: - _LOGD (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) complete."); - return FALSE; } @@ -2774,9 +3493,7 @@ nm_device_activate_schedule_stage1_device_prepare (NMDevice *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); - - _LOGD (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) scheduled..."); + activation_source_schedule (self, activate_stage1_device_prepare, AF_INET); } static NMActStageReturn @@ -2787,16 +3504,15 @@ act_stage2_config (NMDevice *self, NMDeviceStateReason *reason) } /* - * nm_device_activate_stage2_device_config + * 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) +static void +activate_stage2_device_config (NMDevice *self) { - NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActStageReturn ret; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; @@ -2804,10 +3520,6 @@ nm_device_activate_stage2_device_config (gpointer user_data) NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request); GSList *iter; - /* Clear the activation source ID now that this stage has run */ - activation_source_clear (self, FALSE, 0); - - _LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) starting..."); nm_device_state_changed (self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE); /* Assumed connections were already set up outside NetworkManager */ @@ -2817,15 +3529,15 @@ nm_device_activate_stage2_device_config (gpointer user_data) 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; + return; } ret = NM_DEVICE_GET_CLASS (self)->act_stage2_config (self, &reason); if (ret == NM_ACT_STAGE_RETURN_POSTPONE) - goto out; + return; else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - goto out; + return; } g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); } @@ -2836,19 +3548,39 @@ nm_device_activate_stage2_device_config (gpointer user_data) NMDeviceState slave_state = nm_device_get_state (info->slave); if (slave_state == NM_DEVICE_STATE_IP_CONFIG) - nm_device_enslave_slave (self, info->slave, nm_device_get_connection (info->slave)); + nm_device_master_enslave_slave (self, info->slave, nm_device_get_applied_connection (info->slave)); else if ( nm_device_uses_generated_assumed_connection (self) && slave_state <= NM_DEVICE_STATE_DISCONNECTED) nm_device_queue_recheck_assume (info->slave); } - _LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) successful."); + if (lldp_rx_enabled (self)) { + gs_free_error GError *error = NULL; + gconstpointer addr; + size_t addr_length; - nm_device_activate_schedule_stage3_ip_config_start (self); + if (priv->lldp_listener) + nm_lldp_listener_stop (priv->lldp_listener); + else { + priv->lldp_listener = nm_lldp_listener_new (); + g_signal_connect (priv->lldp_listener, + "notify::" NM_LLDP_LISTENER_NEIGHBORS, + G_CALLBACK (lldp_neighbors_changed), + self); + } -out: - _LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) complete."); - return FALSE; + addr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &addr_length); + + if (nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), + nm_device_get_iface (self), addr, addr_length, &error)) + _LOGD (LOGD_DEVICE, "LLDP listener %p started", priv->lldp_listener); + else { + _LOGD (LOGD_DEVICE, "LLDP listener %p could not be started: %s", + priv->lldp_listener, error->message); + } + } + + nm_device_activate_schedule_stage3_ip_config_start (self); } @@ -2893,56 +3625,74 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self) } } - activation_source_schedule (self, nm_device_activate_stage2_device_config, 0); - - _LOGD (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) scheduled..."); + activation_source_schedule (self, activate_stage2_device_config, AF_INET); } -/*********************************************/ -/* avahi-autoipd stuff */ - +/* + * nm_device_check_ip_failed + * + * Progress the device to appropriate state if both IPv4 and IPv6 failed + */ static void -aipd_timeout_remove (NMDevice *self) +nm_device_check_ip_failed (NMDevice *self, gboolean may_fail) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDeviceState state; - if (priv->aipd_timeout) { - g_source_remove (priv->aipd_timeout); - priv->aipd_timeout = 0; + if ( priv->ip4_state != IP_FAIL + || priv->ip6_state != IP_FAIL) + return; + + if (nm_device_uses_assumed_connection (self)) { + /* We have assumed configuration, but couldn't + * redo it. No problem, move to check state. */ + priv->ip4_state = priv->ip6_state = IP_DONE; + state = NM_DEVICE_STATE_IP_CHECK; + } else if ( may_fail + && get_ip_config_may_fail (self, AF_INET) + && get_ip_config_may_fail (self, AF_INET6)) { + /* Couldn't start either IPv6 and IPv4 autoconfiguration, + * but both are allowed to fail. */ + state = NM_DEVICE_STATE_SECONDARIES; + } else { + /* Autoconfiguration attempted without success. */ + state = NM_DEVICE_STATE_FAILED; } + + nm_device_state_changed (self, + state, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); } +/*********************************************/ +/* IPv4LL stuff */ + static void -aipd_cleanup (NMDevice *self) +ipv4ll_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->ipv4ll) { + sd_ipv4ll_set_callback (priv->ipv4ll, NULL, NULL); + sd_ipv4ll_stop (priv->ipv4ll); + priv->ipv4ll = sd_ipv4ll_unref (priv->ipv4ll); } - if (priv->aipd_pid > 0) { - nm_utils_kill_child_sync (priv->aipd_pid, SIGKILL, LOGD_AUTOIP4, "avahi-autoipd", NULL, 0, 0); - priv->aipd_pid = -1; - } - - aipd_timeout_remove (self); + nm_clear_g_source (&priv->ipv4ll_timeout); } static NMIP4Config * -aipd_get_ip4_config (NMDevice *self, guint32 lla) +ipv4ll_get_ip4_config (NMDevice *self, guint32 lla) { NMIP4Config *config = NULL; NMPlatformIP4Address address; NMPlatformIP4Route route; - config = nm_ip4_config_new (); + config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); g_assert (config); memset (&address, 0, sizeof (address)); - address.address = lla; - address.plen = 16; + nm_platform_ip4_address_set_addr (&address, lla, 16); address.source = NM_IP_CONFIG_SOURCE_IP4LL; nm_ip4_config_add_address (config, &address); @@ -2960,22 +3710,21 @@ aipd_get_ip4_config (NMDevice *self, guint32 lla) #define IPV4LL_NETWORK (htonl (0xA9FE0000L)) #define IPV4LL_NETMASK (htonl (0xFFFF0000L)) -void -nm_device_handle_autoip4_event (NMDevice *self, - const char *event, - const char *address) +static void +nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) { + NMDevice *self = data; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection = NULL; const char *method; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - - g_return_if_fail (event != NULL); + struct in_addr address; + NMIP4Config *config; + int r; if (priv->act_request == NULL) return; - connection = nm_act_request_get_connection (priv->act_request); + connection = nm_act_request_get_applied_connection (priv->act_request); g_assert (connection); /* Ignore if the connection isn't an AutoIP connection */ @@ -2983,86 +3732,62 @@ nm_device_handle_autoip4_event (NMDevice *self, if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) != 0) return; - if (strcmp (event, "BIND") == 0) { - guint32 lla; - NMIP4Config *config; - - if (inet_pton (AF_INET, address, &lla) <= 0) { - _LOGE (LOGD_AUTOIP4, "invalid address %s received from avahi-autoipd.", address); - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_ERROR); + switch (event) { + case SD_IPV4LL_EVENT_BIND: + r = sd_ipv4ll_get_address (ll, &address); + if (r < 0) { + _LOGE (LOGD_AUTOIP4, "invalid IPv4 link-local address received, error %d.", r); + priv->ip4_state = IP_FAIL; + nm_device_check_ip_failed (self, FALSE); return; } - if ((lla & IPV4LL_NETMASK) != IPV4LL_NETWORK) { - _LOGE (LOGD_AUTOIP4, "invalid address %s received from avahi-autoipd (not link-local).", address); - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_ERROR); + if ((address.s_addr & IPV4LL_NETMASK) != IPV4LL_NETWORK) { + _LOGE (LOGD_AUTOIP4, "invalid address %08x received (not link-local).", address.s_addr); + priv->ip4_state = IP_FAIL; + nm_device_check_ip_failed (self, FALSE); return; } - config = aipd_get_ip4_config (self, lla); + config = ipv4ll_get_ip4_config (self, address.s_addr); if (config == NULL) { - _LOGE (LOGD_AUTOIP4, "failed to get autoip config"); - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + _LOGE (LOGD_AUTOIP4, "failed to get IPv4LL config"); + priv->ip4_state = IP_FAIL; + nm_device_check_ip_failed (self, FALSE); return; } if (priv->ip4_state == IP_CONF) { - aipd_timeout_remove (self); + nm_clear_g_source (&priv->ipv4ll_timeout); nm_device_activate_schedule_ip4_config_result (self, config); } else if (priv->ip4_state == IP_DONE) { - if (!ip4_config_merge_and_apply (self, config, TRUE, &reason)) { + if (!ip4_config_merge_and_apply (self, config, TRUE, NULL)) { _LOGE (LOGD_AUTOIP4, "failed to update IP4 config for autoip change."); - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + priv->ip4_state = IP_FAIL; + nm_device_check_ip_failed (self, FALSE); } } else g_assert_not_reached (); g_object_unref (config); - } else { - _LOGW (LOGD_AUTOIP4, "autoip address %s no longer valid because '%s'.", 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); + break; + default: + _LOGW (LOGD_AUTOIP4, "IPv4LL address no longer valid after event %d.", event); + priv->ip4_state = IP_FAIL; + nm_device_check_ip_failed (self, FALSE); } } -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; - - if (!priv->aipd_watch) - return; - priv->aipd_watch = 0; - - if (WIFEXITED (status)) - _LOGD (LOGD_AUTOIP4, "avahi-autoipd exited with error code %d", WEXITSTATUS (status)); - else if (WIFSTOPPED (status)) - _LOGW (LOGD_AUTOIP4, "avahi-autoipd stopped unexpectedly with signal %d", WSTOPSIG (status)); - else if (WIFSIGNALED (status)) - _LOGW (LOGD_AUTOIP4, "avahi-autoipd died with signal %d", WTERMSIG (status)); - else - _LOGW (LOGD_AUTOIP4, "avahi-autoipd died from an unknown cause"); - - 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) +ipv4ll_timeout_cb (gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->aipd_timeout) { - _LOGI (LOGD_AUTOIP4, "avahi-autoipd timed out."); - priv->aipd_timeout = 0; - aipd_cleanup (self); + if (priv->ipv4ll_timeout) { + _LOGI (LOGD_AUTOIP4, "IPv4LL configuration timed out."); + priv->ipv4ll_timeout = 0; + ipv4ll_cleanup (self); if (priv->ip4_state == IP_CONF) nm_device_activate_schedule_ip4_config_timeout (self); @@ -3071,66 +3796,68 @@ aipd_timeout_cb (gpointer user_data) return FALSE; } -/* default to installed helper, but can be modified for testing */ -const char *nm_device_autoipd_helper_path = LIBEXECDIR "/nm-avahi-autoipd.action"; - static NMActStageReturn -aipd_start (NMDevice *self, NMDeviceStateReason *reason) +ipv4ll_start (NMDevice *self, NMDeviceStateReason *reason) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *argv[6]; - char *cmdline; - const char *aipd_binary; - int i = 0; - GError *error = NULL; + const struct ether_addr *addr; + int ifindex, r; + size_t addr_len; - aipd_cleanup (self); + ipv4ll_cleanup (self); - /* Find avahi-autoipd */ - aipd_binary = nm_utils_find_helper ("avahi-autoipd", NULL, NULL); - if (!aipd_binary) { - _LOGW (LOGD_DEVICE | LOGD_AUTOIP4, - "Activation: Stage 3 of 5 (IP Configure Start) failed" - " to start avahi-autoipd: not found"); - *reason = NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED; - return NM_ACT_STAGE_RETURN_FAILURE; + r = sd_ipv4ll_new (&priv->ipv4ll); + if (r < 0) { + _LOGE (LOGD_AUTOIP4, "IPv4LL: new() failed with error %d", r); + goto fail; } - argv[i++] = aipd_binary; - argv[i++] = "--script"; - argv[i++] = nm_device_autoipd_helper_path; + r = sd_ipv4ll_attach_event (priv->ipv4ll, NULL, 0); + if (r < 0) { + _LOGE (LOGD_AUTOIP4, "IPv4LL: attach_event() failed with error %d", r); + goto fail; + } - if (nm_logging_enabled (LOGL_DEBUG, LOGD_AUTOIP4)) - argv[i++] = "--debug"; - argv[i++] = nm_device_get_ip_iface (self); - argv[i++] = NULL; + ifindex = nm_device_get_ip_ifindex (self); + addr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &addr_len); + if (!addr || addr_len != ETH_ALEN) { + _LOGE (LOGD_AUTOIP4, "IPv4LL: can't retrieve hardware address"); + goto fail; + } - cmdline = g_strjoinv (" ", (char **) argv); - _LOGD (LOGD_AUTOIP4, "running: %s", cmdline); - g_free (cmdline); + r = sd_ipv4ll_set_mac (priv->ipv4ll, addr); + if (r < 0) { + _LOGE (LOGD_AUTOIP4, "IPv4LL: set_mac() failed with error %d", r); + goto fail; + } - if (!g_spawn_async ("/", (char **) argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, - nm_utils_setpgid, NULL, &(priv->aipd_pid), &error)) { - _LOGW (LOGD_DEVICE | LOGD_AUTOIP4, - "Activation: Stage 3 of 5 (IP Configure Start) failed" - " to start avahi-autoipd: %s", - error && error->message ? error->message : "(unknown)"); - g_clear_error (&error); - aipd_cleanup (self); - return NM_ACT_STAGE_RETURN_FAILURE; + r = sd_ipv4ll_set_index (priv->ipv4ll, ifindex); + if (r < 0) { + _LOGE (LOGD_AUTOIP4, "IPv4LL: set_index() failed with error %d", r); + goto fail; } - _LOGD (LOGD_DEVICE | LOGD_AUTOIP4, - "Activation: Stage 3 of 5 (IP Configure Start) started" - " avahi-autoipd..."); + r = sd_ipv4ll_set_callback (priv->ipv4ll, nm_device_handle_ipv4ll_event, self); + if (r < 0) { + _LOGE (LOGD_AUTOIP4, "IPv4LL: set_callback() failed with error %d", r); + goto fail; + } + + r = sd_ipv4ll_start (priv->ipv4ll); + if (r < 0) { + _LOGE (LOGD_AUTOIP4, "IPv4LL: start() failed with error %d", r); + goto fail; + } - /* Monitor the child process so we know when it dies */ - priv->aipd_watch = g_child_watch_add (priv->aipd_pid, aipd_watch_cb, self); + _LOGI (LOGD_DEVICE | LOGD_AUTOIP4, "IPv4LL: started"); /* Start a timeout to bound the address attempt */ - priv->aipd_timeout = g_timeout_add_seconds (20, aipd_timeout_cb, self); + priv->ipv4ll_timeout = g_timeout_add_seconds (20, ipv4ll_timeout_cb, self); return NM_ACT_STAGE_RETURN_POSTPONE; +fail: + *reason = NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED; + return NM_ACT_STAGE_RETURN_FAILURE; } /*********************************************/ @@ -3185,16 +3912,17 @@ static void ensure_con_ip4_config (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + int ip_ifindex = nm_device_get_ip_ifindex (self); NMConnection *connection; if (priv->con_ip4_config) return; - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); if (!connection) return; - priv->con_ip4_config = nm_ip4_config_new (); + priv->con_ip4_config = nm_ip4_config_new (ip_ifindex); nm_ip4_config_merge_setting (priv->con_ip4_config, nm_connection_get_setting_ip4_config (connection), nm_device_get_ip4_route_metric (self)); @@ -3210,16 +3938,17 @@ static void ensure_con_ip6_config (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + int ip_ifindex = nm_device_get_ip_ifindex (self); NMConnection *connection; if (priv->con_ip6_config) return; - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); if (!connection) return; - priv->con_ip6_config = nm_ip6_config_new (); + priv->con_ip6_config = nm_ip6_config_new (ip_ifindex); nm_ip6_config_merge_setting (priv->con_ip6_config, nm_connection_get_setting_ip6_config (connection), nm_device_get_ip6_route_metric (self)); @@ -3243,10 +3972,7 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) 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; - } + nm_clear_g_signal_handler (priv->dhcp4_client, &priv->dhcp4_state_sigid); nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE); @@ -3258,11 +3984,20 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) } if (priv->dhcp4_config) { - g_clear_object (&priv->dhcp4_config); + nm_exported_object_clear_and_unexport (&priv->dhcp4_config); g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG); } } +static void +_ip4_config_merge_default (gpointer value, gpointer user_data) +{ + NMIP4Config *src = (NMIP4Config *) value; + NMIP4Config *dst = (NMIP4Config *) user_data; + + nm_ip4_config_merge (dst, src, NM_IP_CONFIG_MERGE_DEFAULT); +} + static gboolean ip4_config_merge_and_apply (NMDevice *self, NMIP4Config *config, @@ -3288,7 +4023,7 @@ ip4_config_merge_and_apply (NMDevice *self, } /* Apply ignore-auto-routes and ignore-auto-dns settings */ - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); if (connection) { NMSettingIPConfig *s_ip4 = nm_connection_get_setting_ip4_config (connection); @@ -3298,7 +4033,7 @@ ip4_config_merge_and_apply (NMDevice *self, } } - composite = nm_ip4_config_new (); + composite = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); if (commit) ensure_con_ip4_config (self); @@ -3308,8 +4043,9 @@ ip4_config_merge_and_apply (NMDevice *self, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); } - if (priv->vpn4_config) - nm_ip4_config_merge (composite, priv->vpn4_config, NM_IP_CONFIG_MERGE_DEFAULT); + + g_slist_foreach (priv->vpn4_configs, _ip4_config_merge_default, composite); + if (priv->ext_ip4_config) nm_ip4_config_merge (composite, priv->ext_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT); @@ -3327,7 +4063,6 @@ ip4_config_merge_and_apply (NMDevice *self, if (priv->con_ip4_config) nm_ip4_config_merge (composite, priv->con_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT); - /* Add the default route. * * We keep track of the default route of a device in a private field. @@ -3366,7 +4101,7 @@ ip4_config_merge_and_apply (NMDevice *self, if ( !priv->v4_commit_first_time && connection_is_never_default) { - /* If the connection is explicitly configured as never-default, we enforce the (absense of the) + /* If the connection is explicitly configured as never-default, we enforce the (absence of the) * default-route only once. That allows the user to configure a connection as never-default, * but he can add default routes externally (via a dispatcher script) and NM will not interfere. */ goto END_ADD_DEFAULT_ROUTE; @@ -3390,7 +4125,7 @@ ip4_config_merge_and_apply (NMDevice *self, goto END_ADD_DEFAULT_ROUTE; has_direct_route = ( gateway == 0 - || nm_ip4_config_get_subnet_for_host (composite, gateway) + || nm_ip4_config_destination_is_direct (composite, gateway, 32) || nm_ip4_config_get_direct_route_for_host (composite, gateway)); priv->default_route.v4_has = TRUE; @@ -3452,7 +4187,8 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config) } else { /* Notify dispatcher scripts of new DHCP4 config */ nm_dispatcher_call (DISPATCHER_ACTION_DHCP4_CHANGE, - nm_device_get_connection (self), + nm_device_get_settings_connection (self), + nm_device_get_applied_connection (self), self, NULL, NULL, @@ -3472,7 +4208,7 @@ dhcp4_restart_cb (gpointer user_data) priv = NM_DEVICE_GET_PRIVATE (self); priv->dhcp4_restart_id = 0; - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); if (dhcp4_start (self, connection, &reason) == NM_ACT_STAGE_RETURN_FAILURE) priv->dhcp4_restart_id = g_timeout_add_seconds (120, dhcp4_restart_cb, self); @@ -3517,22 +4253,6 @@ dhcp4_fail (NMDevice *self, gboolean timeout) } static void -dhcp4_update_config (NMDevice *self, NMDhcp4Config *config, GHashTable *options) -{ - GHashTableIter iter; - const char *key, *value; - - /* Update the DHCP4 config object with new DHCP options */ - nm_dhcp4_config_reset (config); - - g_hash_table_iter_init (&iter, options); - while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) - nm_dhcp4_config_add_option (config, key, value); - - g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG); -} - -static void dhcp4_state_changed (NMDhcpClient *client, NMDhcpState state, NMIP4Config *ip4_config, @@ -3558,7 +4278,8 @@ dhcp4_state_changed (NMDhcpClient *client, break; } - dhcp4_update_config (self, priv->dhcp4_config, options); + nm_dhcp4_config_set_options (priv->dhcp4_config, options); + g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG); if (priv->ip4_state == IP_CONF) nm_device_activate_schedule_ip4_config_result (self, ip4_config); @@ -3584,6 +4305,28 @@ dhcp4_state_changed (NMDhcpClient *client, } } +static int +dhcp4_get_timeout (NMDevice *self, NMSettingIP4Config *s_ip4) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gs_free char *value = NULL; + int timeout; + + timeout = nm_setting_ip4_config_get_dhcp_timeout (s_ip4); + if (timeout) + return timeout; + + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "ipv4.dhcp-timeout", + self); + timeout = _nm_utils_ascii_str_to_int64 (value, 10, + 0, G_MAXINT32, 0); + if (timeout) + return timeout; + + return priv->dhcp_timeout; +} + static NMActStageReturn dhcp4_start (NMDevice *self, NMConnection *connection, @@ -3598,8 +4341,7 @@ dhcp4_start (NMDevice *self, s_ip4 = nm_connection_get_setting_ip4_config (connection); /* Clear old exported DHCP options */ - if (priv->dhcp4_config) - g_object_unref (priv->dhcp4_config); + nm_exported_object_clear_and_unexport (&priv->dhcp4_config); priv->dhcp4_config = nm_dhcp4_config_new (); hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), &hw_addr_len); @@ -3618,8 +4360,9 @@ dhcp4_start (NMDevice *self, nm_device_get_ip4_route_metric (self), nm_setting_ip_config_get_dhcp_send_hostname (s_ip4), nm_setting_ip_config_get_dhcp_hostname (s_ip4), + nm_setting_ip4_config_get_dhcp_fqdn (NM_SETTING_IP4_CONFIG (s_ip4)), nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4)), - priv->dhcp_timeout, + dhcp4_get_timeout (self, NM_SETTING_IP4_CONFIG (s_ip4)), priv->dhcp_anycast_address, NULL); @@ -3657,7 +4400,7 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release) /* Terminate old DHCP instance and release the old lease */ dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, release); - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); g_assert (connection); /* Start DHCP again on the interface */ @@ -3687,10 +4430,11 @@ reserve_shared_ip (NMDevice *self, NMSettingIPConfig *s_ip4, NMPlatformIP4Addres if (s_ip4 && nm_setting_ip_config_get_num_addresses (s_ip4)) { /* Use the first user-supplied address */ NMIPAddress *user = nm_setting_ip_config_get_address (s_ip4, 0); + in_addr_t a; g_assert (user); - nm_ip_address_get_address_binary (user, &address->address); - address->plen = nm_ip_address_get_prefix (user); + nm_ip_address_get_address_binary (user, &a); + nm_platform_ip4_address_set_addr (address, a, nm_ip_address_get_prefix (user)); } else { /* Find an unused address in the 10.42.x.x range */ guint32 start = (guint32) ntohl (0x0a2a0001); /* 10.42.0.1 */ @@ -3703,8 +4447,7 @@ reserve_shared_ip (NMDevice *self, NMSettingIPConfig *s_ip4, NMPlatformIP4Addres return FALSE; } } - address->address = start + count; - address->plen = 24; + nm_platform_ip4_address_set_addr (address, start + count, 24); g_hash_table_insert (shared_ips, GUINT_TO_POINTER (address->address), @@ -3727,7 +4470,7 @@ shared4_new_config (NMDevice *self, NMConnection *connection, NMDeviceStateReaso return NULL; } - config = nm_ip4_config_new (); + config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); address.source = NM_IP_CONFIG_SOURCE_SHARED; nm_ip4_config_add_address (config, &address); @@ -3852,7 +4595,7 @@ act_stage3_ip4_config_start (NMDevice *self, g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); g_assert (connection); if ( connection_ip4_method_requires_carrier (connection, NULL) @@ -3884,10 +4627,10 @@ act_stage3_ip4_config_start (NMDevice *self, 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); + ret = ipv4ll_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 (); + *out_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); g_assert (*out_config); ret = NM_ACT_STAGE_RETURN_SUCCESS; } else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) { @@ -3917,14 +4660,10 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE; g_clear_object (&priv->dhcp6_ip6_config); g_clear_pointer (&priv->dhcp6_event_id, g_free); - nm_clear_g_source (&priv->dhcp6_restart_id); 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; - } + nm_clear_g_signal_handler (priv->dhcp6_client, &priv->dhcp6_state_sigid); if ( cleanup_type == CLEANUP_TYPE_DECONFIGURE || cleanup_type == CLEANUP_TYPE_REMOVED) @@ -3936,11 +4675,20 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE); if (priv->dhcp6_config) { - g_clear_object (&priv->dhcp6_config); + nm_exported_object_clear_and_unexport (&priv->dhcp6_config); g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG); } } +static void +_ip6_config_merge_default (gpointer value, gpointer user_data) +{ + NMIP6Config *src = (NMIP6Config *) value; + NMIP6Config *dst = (NMIP6Config *) user_data; + + nm_ip6_config_merge (dst, src, NM_IP_CONFIG_MERGE_DEFAULT); +} + static gboolean ip6_config_merge_and_apply (NMDevice *self, gboolean commit, @@ -3958,7 +4706,7 @@ ip6_config_merge_and_apply (NMDevice *self, gboolean ignore_auto_dns = FALSE; /* Apply ignore-auto-routes and ignore-auto-dns settings */ - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); if (connection) { NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection); @@ -3969,10 +4717,11 @@ ip6_config_merge_and_apply (NMDevice *self, } /* If no config was passed in, create a new one */ - composite = nm_ip6_config_new (); + composite = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); if (commit) ensure_con_ip6_config (self); + g_assert (composite); /* Merge all the IP configs into the composite config */ if (priv->ac_ip6_config) { @@ -3985,8 +4734,9 @@ ip6_config_merge_and_apply (NMDevice *self, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); } - if (priv->vpn6_config) - nm_ip6_config_merge (composite, priv->vpn6_config, NM_IP_CONFIG_MERGE_DEFAULT); + + g_slist_foreach (priv->vpn6_configs, _ip6_config_merge_default, composite); + if (priv->ext_ip6_config) nm_ip6_config_merge (composite, priv->ext_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT); @@ -4121,7 +4871,7 @@ static void dhcp6_lease_change (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; + NMSettingsConnection *settings_connection; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; if (priv->dhcp6_ip6_config == NULL) { @@ -4132,8 +4882,8 @@ dhcp6_lease_change (NMDevice *self) g_assert (priv->dhcp6_client); /* sanity check */ - connection = nm_device_get_connection (self); - g_assert (connection); + settings_connection = nm_device_get_settings_connection (self); + g_assert (settings_connection); /* Apply the updated config */ if (ip6_config_merge_and_apply (self, TRUE, &reason) == FALSE) { @@ -4141,7 +4891,10 @@ dhcp6_lease_change (NMDevice *self) nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); } else { /* Notify dispatcher scripts of new DHCPv6 config */ - nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE, connection, self, NULL, NULL, NULL); + nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE, + settings_connection, + nm_device_get_applied_connection (self), + self, NULL, NULL, NULL); } } @@ -4221,22 +4974,6 @@ dhcp6_timeout (NMDevice *self, NMDhcpClient *client) } static void -dhcp6_update_config (NMDevice *self, NMDhcp6Config *config, GHashTable *options) -{ - GHashTableIter iter; - const char *key, *value; - - /* Update the DHCP6 config object with new DHCP options */ - nm_dhcp6_config_reset (config); - - g_hash_table_iter_init (&iter, options); - while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) - nm_dhcp6_config_add_option (config, key, value); - - g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG); -} - -static void dhcp6_state_changed (NMDhcpClient *client, NMDhcpState state, NMIP6Config *ip6_config, @@ -4273,7 +5010,7 @@ dhcp6_state_changed (NMDhcpClient *client, if (ip6_config) { priv->dhcp6_ip6_config = g_object_ref (ip6_config); priv->dhcp6_event_id = g_strdup (event_id); - dhcp6_update_config (self, priv->dhcp6_config, options); + nm_dhcp6_config_set_options (priv->dhcp6_config, options); g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG); } } @@ -4320,6 +5057,9 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) GByteArray *tmp = NULL; const guint8 *hw_addr; size_t hw_addr_len = 0; + const struct in6_addr *ll_addr = NULL; + NMIP6Config *ip6_config; + int i; g_assert (connection); s_ip6 = nm_connection_get_setting_ip6_config (connection); @@ -4331,10 +5071,22 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) g_byte_array_append (tmp, hw_addr, hw_addr_len); } + ip6_config = priv->ext_ip6_config; + for (i = 0; ip6_config && i < nm_ip6_config_get_num_addresses (ip6_config); i++) { + const NMPlatformIP6Address *addr = nm_ip6_config_get_address (ip6_config, i); + + if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) { + ll_addr = &addr->address; + break; + } + } + g_return_val_if_fail (ll_addr, FALSE); + priv->dhcp6_client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (), nm_device_get_ip_iface (self), nm_device_get_ip_ifindex (self), tmp, + ll_addr, nm_connection_get_uuid (connection), nm_device_get_ip6_route_metric (self), nm_setting_ip_config_get_dhcp_send_hostname (s_ip6), @@ -4363,14 +5115,14 @@ dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason) NMConnection *connection; NMSettingIPConfig *s_ip6; - g_clear_object (&priv->dhcp6_config); + nm_exported_object_clear_and_unexport (&priv->dhcp6_config); priv->dhcp6_config = nm_dhcp6_config_new (); g_warn_if_fail (priv->dhcp6_ip6_config == NULL); g_clear_object (&priv->dhcp6_ip6_config); g_clear_pointer (&priv->dhcp6_event_id, g_free); - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); g_assert (connection); s_ip6 = nm_connection_get_setting_ip6_config (connection); if (!nm_setting_ip_config_get_may_fail (s_ip6) || @@ -4443,10 +5195,14 @@ linklocal6_cleanup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->linklocal6_timeout_id) { - g_source_remove (priv->linklocal6_timeout_id); - priv->linklocal6_timeout_id = 0; - } + nm_clear_g_source (&priv->linklocal6_timeout_id); +} + +static void +linklocal6_failed (NMDevice *self) +{ + linklocal6_cleanup (self); + nm_device_activate_schedule_ip6_config_timeout (self); } static gboolean @@ -4454,11 +5210,8 @@ linklocal6_timeout_cb (gpointer user_data) { NMDevice *self = user_data; - linklocal6_cleanup (self); - _LOGD (LOGD_DEVICE, "linklocal6: waiting for link-local addresses failed due to timeout"); - - nm_device_activate_schedule_ip6_config_timeout (self); + linklocal6_failed (self); return G_SOURCE_REMOVE; } @@ -4474,7 +5227,7 @@ linklocal6_complete (NMDevice *self) linklocal6_cleanup (self); - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); g_assert (connection); method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); @@ -4502,9 +5255,11 @@ check_and_add_ipv6ll_addr (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int ip_ifindex = nm_device_get_ip_ifindex (self); - NMUtilsIPv6IfaceId iid; struct in6_addr lladdr; guint i, n; + NMConnection *connection; + NMSettingIP6Config *s_ip6 = NULL; + GError *error = NULL; if (priv->nm_ipv6ll == FALSE) return; @@ -4515,27 +5270,59 @@ check_and_add_ipv6ll_addr (NMDevice *self) const NMPlatformIP6Address *addr; addr = nm_ip6_config_get_address (priv->ip6_config, i); - if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) { + if ( IN6_IS_ADDR_LINKLOCAL (&addr->address) + && !(addr->flags & IFA_F_DADFAILED)) { /* Already have an LL address, nothing to do */ return; } } } - if (!nm_device_get_ip_iface_identifier (self, &iid)) { - _LOGW (LOGD_IP6, "failed to get interface identifier; IPv6 may be broken"); - return; - } - memset (&lladdr, 0, sizeof (lladdr)); lladdr.s6_addr16[0] = htons (0xfe80); - nm_utils_ipv6_addr_set_interface_identfier (&lladdr, iid); - _LOGD (LOGD_IP6, "adding IPv6LL address %s", nm_utils_inet6_ntop (&lladdr, NULL)); + + connection = nm_device_get_applied_connection (self); + if (connection) + s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection)); + + if (s_ip6 && nm_setting_ip6_config_get_addr_gen_mode (s_ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) { + if (!nm_utils_ipv6_addr_set_stable_privacy (&lladdr, + nm_device_get_iface (self), + nm_connection_get_uuid (connection), + priv->linklocal6_dad_counter++, + &error)) { + _LOGW (LOGD_IP6, "linklocal6: failed to generate an address: %s", error->message); + g_clear_error (&error); + linklocal6_failed (self); + return; + } + _LOGD (LOGD_IP6, "linklocal6: using IPv6 stable-privacy addressing"); + } else { + NMUtilsIPv6IfaceId iid; + + if (priv->linklocal6_timeout_id) { + /* We already started and attempt to add a LL address. For the EUI-64 + * mode we can't pick a new one, we'll just fail. */ + _LOGW (LOGD_IP6, "linklocal6: DAD failed for an EUI-64 address"); + linklocal6_failed (self); + return; + } + + if (!nm_device_get_ip_iface_identifier (self, &iid)) { + _LOGW (LOGD_IP6, "linklocal6: failed to get interface identifier; IPv6 cannot continue"); + return; + } + _LOGD (LOGD_IP6, "linklocal6: using EUI-64 identifier to generate IPv6LL address"); + + nm_utils_ipv6_addr_set_interface_identfier (&lladdr, iid); + } + + _LOGD (LOGD_IP6, "linklocal6: adding IPv6LL address %s", nm_utils_inet6_ntop (&lladdr, NULL)); if (!nm_platform_ip6_address_add (NM_PLATFORM_GET, ip_ifindex, lladdr, - in6addr_any, 64, + in6addr_any, NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0)) { @@ -4556,7 +5343,7 @@ linklocal6_start (NMDevice *self) if (have_ip6_address (priv->ip6_config, TRUE)) return NM_ACT_STAGE_RETURN_FINISH; - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); g_assert (connection); method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); @@ -4576,53 +5363,6 @@ linklocal6_start (NMDevice *self) /******************************************/ -static void -print_support_extended_ifa_flags (NMSettingIP6ConfigPrivacy use_tempaddr) -{ - static gint8 warn = 0; - static gint8 s_libnl = -1, s_kernel; - - if (warn >= 2) - return; - - if (s_libnl == -1) { - s_libnl = !!nm_platform_check_support_libnl_extended_ifa_flags (); - s_kernel = !!nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET); - - if (s_libnl && s_kernel) { - nm_log_dbg (LOGD_IP6, "kernel and libnl support extended IFA_FLAGS (needed by NM for IPv6 private addresses)"); - warn = 2; - return; - } - } - - if ( use_tempaddr != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR - && use_tempaddr != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) { - if (warn == 0) { - nm_log_dbg (LOGD_IP6, "%s%s%s %s not support extended IFA_FLAGS (needed by NM for IPv6 private addresses)", - !s_kernel ? "kernel" : "", - !s_kernel && !s_libnl ? " and " : "", - !s_libnl ? "libnl" : "", - !s_kernel && !s_libnl ? "do" : "does"); - warn = 1; - } - return; - } - - if (!s_libnl && !s_kernel) { - nm_log_warn (LOGD_IP6, "libnl and the kernel do not support extended IFA_FLAGS needed by NM for " - "IPv6 private addresses. This feature is not available"); - } else if (!s_libnl) { - nm_log_warn (LOGD_IP6, "libnl does not support extended IFA_FLAGS needed by NM for " - "IPv6 private addresses. This feature is not available"); - } else if (!s_kernel) { - nm_log_warn (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for " - "IPv6 private addresses. This feature is not available"); - } - - warn = 2; -} - static void nm_device_ipv6_set_mtu (NMDevice *self, guint32 mtu); static void @@ -4680,24 +5420,20 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int i; - static int system_support = -1; + int system_support; guint ifa_flags = 0x00; - if (system_support == -1) { - /* - * Check, if both libnl and the kernel are recent enough, - * to help user space handling RA. If it's not supported, - * we have no ipv6-privacy and must add autoconf addresses - * as /128. The reason for the /128 is to prevent the kernel - * from adding a prefix route for this address. - **/ - system_support = nm_platform_check_support_libnl_extended_ifa_flags () && - nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET); - } + /* + * Check, whether kernel is recent enough to help user space handling RA. + * If it's not supported, we have no ipv6-privacy and must add autoconf + * addresses as /128. The reason for the /128 is to prevent the kernel + * from adding a prefix route for this address. + **/ + system_support = nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET); if (system_support) ifa_flags = IFA_F_NOPREFIXROUTE; - if (priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR + if ( priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR || priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) { /* without system_support, this flag will be ignored. Still set it, doesn't seem to do any harm. */ @@ -4707,7 +5443,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) g_return_if_fail (priv->act_request); if (!priv->ac_ip6_config) - priv->ac_ip6_config = nm_ip6_config_new (); + priv->ac_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); if (changed & NM_RDISC_CONFIG_GATEWAYS) { /* Use the first gateway as ordered in router discovery cache. */ @@ -4805,10 +5541,11 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) "Activation: Stage 3 of 5 (IP Configure Start) starting DHCPv6" " as requested by IPv6 router..."); if (!dhcp6_start (self, FALSE, &reason)) { - if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) + if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) { nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + return; + } } - return; } } @@ -4854,10 +5591,15 @@ addrconf6_start_with_link_ready (NMDevice *self) g_assert (priv->rdisc); if (nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &iid)) { - _LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); - } else if (!nm_device_get_ip_iface_identifier (self, &iid)) { - _LOGW (LOGD_IP6, "failed to get interface identifier; IPv6 cannot continue"); - return FALSE; + _LOGD (LOGD_IP6, "addrconf6: IPv6 tokenized identifier present"); + nm_rdisc_set_iid (priv->rdisc, iid); + } else if (nm_device_get_ip_iface_identifier (self, &iid)) { + _LOGD (LOGD_IP6, "addrconf6: using the device EUI-64 identifier"); + nm_rdisc_set_iid (priv->rdisc, iid); + } else { + /* Don't abort the addrconf at this point -- if rdisc needs the iid + * it will notice this itself. */ + _LOGI (LOGD_IP6, "addrconf6: no interface identifier; IPv6 adddress creation may fail"); } /* Apply any manual configuration before starting RA */ @@ -4878,7 +5620,6 @@ addrconf6_start_with_link_ready (NMDevice *self) G_CALLBACK (rdisc_ra_timeout), self); - nm_rdisc_set_iid (priv->rdisc, iid); nm_rdisc_start (priv->rdisc); return TRUE; } @@ -4889,9 +5630,10 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; NMActStageReturn ret; - const char *ip_iface = nm_device_get_ip_iface (self); + NMSettingIP6Config *s_ip6 = NULL; + GError *error = NULL; - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); g_assert (connection); g_warn_if_fail (priv->ac_ip6_config == NULL); @@ -4900,14 +5642,27 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) priv->ac_ip6_config = NULL; } - priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self), ip_iface); + s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection)); + g_assert (s_ip6); + + priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self), + nm_device_get_ip_iface (self), + nm_connection_get_uuid (connection), + nm_setting_ip6_config_get_addr_gen_mode (s_ip6), + &error); if (!priv->rdisc) { - _LOGE (LOGD_IP6, "failed to start router discovery (%s)", ip_iface); + _LOGE (LOGD_IP6, "addrconf6: failed to start router discovery: %s", error->message); + g_error_free (error); return FALSE; } priv->rdisc_use_tempaddr = use_tempaddr; - print_support_extended_ifa_flags (use_tempaddr); + + if ( NM_IN_SET (use_tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) + && !nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET)) { + _LOGW (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for " + "IPv6 private addresses. This feature is not available"); + } if (!nm_setting_ip_config_get_may_fail (nm_connection_get_setting_ip6_config (connection))) nm_device_add_pending_action (self, PENDING_ACTION_AUTOCONF6, TRUE); @@ -4929,15 +5684,8 @@ addrconf6_cleanup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->rdisc_changed_id) { - g_signal_handler_disconnect (priv->rdisc, priv->rdisc_changed_id); - priv->rdisc_changed_id = 0; - } - - if (priv->rdisc_timeout_id) { - g_signal_handler_disconnect (priv->rdisc, priv->rdisc_timeout_id); - priv->rdisc_timeout_id = 0; - } + nm_clear_g_signal_handler (priv->rdisc, &priv->rdisc_changed_id); + nm_clear_g_signal_handler (priv->rdisc, &priv->rdisc_timeout_id); nm_device_remove_pending_action (self, PENDING_ACTION_AUTOCONF6, FALSE); @@ -5060,7 +5808,7 @@ _ip6_privacy_get (NMDevice *self) /* 1.) First look at the per-connection setting. If it is not -1 (unknown), * use it. */ - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); if (connection) { NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection); @@ -5126,7 +5874,7 @@ act_stage3_ip6_config_start (NMDevice *self, g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); g_assert (connection); if ( connection_ip6_method_requires_carrier (connection, NULL) @@ -5208,7 +5956,7 @@ act_stage3_ip6_config_start (NMDevice *self, ret = NM_ACT_STAGE_RETURN_POSTPONE; } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) == 0) { /* New blank config */ - *out_config = nm_ip6_config_new (); + *out_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); g_assert (*out_config); ret = NM_ACT_STAGE_RETURN_SUCCESS; @@ -5318,61 +6066,20 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) } /* - * nm_device_check_ip_failed - * - * Progress the device to appropriate state if both IPv4 and IPv6 failed - */ -static void -nm_device_check_ip_failed (NMDevice *self, gboolean may_fail) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMDeviceState state; - - if ( priv->ip4_state != IP_FAIL - || priv->ip6_state != IP_FAIL) - return; - - if (nm_device_uses_assumed_connection (self)) { - /* We have assumed configuration, but couldn't - * redo it. No problem, move to check state. */ - priv->ip4_state = priv->ip6_state = IP_DONE; - state = NM_DEVICE_STATE_IP_CHECK; - } else if ( may_fail - && get_ip_config_may_fail (self, AF_INET) - && get_ip_config_may_fail (self, AF_INET6)) { - /* Couldn't start either IPv6 and IPv4 autoconfiguration, - * but both are allowed to fail. */ - state = NM_DEVICE_STATE_SECONDARIES; - } else { - /* Autoconfiguration attempted without success. */ - state = NM_DEVICE_STATE_FAILED; - } - - nm_device_state_changed (self, - state, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); -} - -/* - * nm_device_activate_stage3_ip_config_start + * activate_stage3_ip_config_start * * Begin automatic/manual IP configuration * */ -static gboolean -nm_device_activate_stage3_ip_config_start (gpointer user_data) +static void +activate_stage3_ip_config_start (NMDevice *self) { - NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActiveConnection *master; NMDevice *master_device; - /* Clear the activation source ID now that this stage has run */ - activation_source_clear (self, FALSE, 0); - priv->ip4_state = priv->ip6_state = IP_WAIT; - _LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) started..."); nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); /* Device should be up before we can do anything with it */ @@ -5393,50 +6100,75 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data) master_device = nm_active_connection_get_device (master); if (priv->ip4_state == IP_WAIT && priv->ip6_state == IP_WAIT) { _LOGI (LOGD_DEVICE, "Activation: connection '%s' waiting on master '%s'", - nm_connection_get_id (nm_device_get_connection (self)), + nm_connection_get_id (nm_device_get_applied_connection (self)), master_device ? nm_device_get_iface (master_device) : "(unknown)"); } - goto out; + return; } /* IPv4 */ if ( nm_device_activate_ip4_state_in_wait (self) && !nm_device_activate_stage3_ip4_start (self)) - goto out; + return; /* IPv6 */ if ( nm_device_activate_ip6_state_in_wait (self) && !nm_device_activate_stage3_ip6_start (self)) - goto out; + return; nm_device_check_ip_failed (self, TRUE); - -out: - _LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) complete."); - return FALSE; } +static gboolean +fw_change_zone_handle (NMDevice *self, + NMFirewallManagerCallId call_id, + GError *error) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + + g_return_val_if_fail (priv->fw_call == call_id, FALSE); + priv->fw_call = NULL; + + return !nm_utils_error_is_cancelled (error, FALSE); +} static void -fw_change_zone_cb (GError *error, gpointer user_data) +fw_change_zone_cb_stage2 (NMFirewallManager *firewall_manager, + NMFirewallManagerCallId call_id, + GError *error, + gpointer user_data) { - NMDevice *self; + NMDevice *self = user_data; NMDevicePrivate *priv; - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + if (!fw_change_zone_handle (self, call_id, error)) return; - self = NM_DEVICE (user_data); + /* FIXME: fail the device on error? */ + priv = NM_DEVICE_GET_PRIVATE (self); + priv->fw_ready = TRUE; - priv->fw_call = NULL; + nm_device_activate_schedule_stage3_ip_config_start (self); +} - if (error) { - /* FIXME: fail the device activation? */ - } +static void +fw_change_zone_cb_ip_check (NMFirewallManager *firewall_manager, + NMFirewallManagerCallId call_id, + GError *error, + gpointer user_data) +{ + NMDevice *self = user_data; - activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0); - _LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled."); + if (!fw_change_zone_handle (self, call_id, error)) + return; + + /* FIXME: fail the device on error? */ + nm_device_start_ip_check (self); } /* @@ -5457,29 +6189,31 @@ nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self) priv = NM_DEVICE_GET_PRIVATE (self); g_return_if_fail (priv->act_request); - g_return_if_fail (!priv->fw_call); - /* Add the interface to the specified firewall zone */ - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); g_assert (connection); s_con = nm_connection_get_setting_connection (connection); - zone = nm_setting_connection_get_zone (s_con); - - if (nm_device_uses_assumed_connection (self)) { - _LOGD (LOGD_DEVICE, "Activation: skip setting firewall zone '%s' for assumed device", zone ? zone : "default"); - activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0); - _LOGD (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled."); - return; + if (!priv->fw_ready) { + if (nm_device_uses_assumed_connection (self)) + priv->fw_ready = TRUE; + else { + if (!priv->fw_call) { + zone = nm_setting_connection_get_zone (s_con); + + _LOGD (LOGD_DEVICE, "Activation: setting firewall zone '%s'", zone ? zone : "default"); + priv->fw_call = nm_firewall_manager_add_or_change_zone (nm_firewall_manager_get (), + nm_device_get_ip_iface (self), + zone, + FALSE, + fw_change_zone_cb_stage2, + self); + } + return; + } } - _LOGD (LOGD_DEVICE, "Activation: setting firewall zone '%s'", zone ? zone : "default"); - priv->fw_call = nm_firewall_manager_add_or_change_zone (nm_firewall_manager_get (), - nm_device_get_ip_iface (self), - zone, - FALSE, - fw_change_zone_cb, - self); + activation_source_schedule (self, activate_stage3_ip_config_start, AF_INET); } static NMActStageReturn @@ -5499,35 +6233,25 @@ act_stage4_ip4_config_timeout (NMDevice *self, NMDeviceStateReason *reason) * Time out on retrieving the IPv4 config. * */ -static gboolean -nm_device_activate_ip4_config_timeout (gpointer user_data) +static void +activate_stage4_ip4_config_timeout (NMDevice *self) { - NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); 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); - - _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 4 of 5 (IPv4 Configure Timeout) started..."); - ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip4_config_timeout (self, &reason); if (ret == NM_ACT_STAGE_RETURN_POSTPONE) - goto out; + return; else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - goto out; + return; } - g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); + g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); priv->ip4_state = IP_FAIL; nm_device_check_ip_failed (self, FALSE); - -out: - _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 4 of 5 (IPv4 Configure Timeout) complete."); - return FALSE; } @@ -5547,9 +6271,7 @@ nm_device_activate_schedule_ip4_config_timeout (NMDevice *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); - - _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 4 of 5 (IPv4 Configure Timeout) scheduled..."); + activation_source_schedule (self, activate_stage4_ip4_config_timeout, AF_INET); } @@ -5566,40 +6288,30 @@ act_stage4_ip6_config_timeout (NMDevice *self, NMDeviceStateReason *reason) /* - * nm_device_activate_ip6_config_timeout + * activate_stage4_ip6_config_timeout * * Time out on retrieving the IPv6 config. * */ -static gboolean -nm_device_activate_ip6_config_timeout (gpointer user_data) +static void +activate_stage4_ip6_config_timeout (NMDevice *self) { - NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); 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); - - _LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 4 of 5 (IPv6 Configure Timeout) started..."); - 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) { + return; + if (ret == NM_ACT_STAGE_RETURN_FAILURE) { nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - goto out; + return; } g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); priv->ip6_state = IP_FAIL; nm_device_check_ip_failed (self, FALSE); - -out: - _LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 4 of 5 (IPv6 Configure Timeout) complete."); - return FALSE; } @@ -5619,9 +6331,7 @@ nm_device_activate_schedule_ip6_config_timeout (NMDevice *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); - - _LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 4 of 5 (IPv6 Configure Timeout) scheduled..."); + activation_source_schedule (self, activate_stage4_ip6_config_timeout, AF_INET6); } static gboolean @@ -5721,7 +6431,7 @@ start_sharing (NMDevice *self, NMIP4Config *config) return FALSE; } - priv->dnsmasq_state_id = g_signal_connect (priv->dnsmasq_manager, "state-changed", + priv->dnsmasq_state_id = g_signal_connect (priv->dnsmasq_manager, NM_DNS_MASQ_MANAGER_STATE_CHANGED, G_CALLBACK (dnsmasq_state_changed_cb), self); return TRUE; @@ -5738,7 +6448,7 @@ send_arps (NMDevice *self, const char *mode_arg) NMIPAddress *addr; GError *error = NULL; - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); if (!connection) return; s_ip4 = nm_connection_get_setting_ip4_config (connection); @@ -5776,10 +6486,14 @@ send_arps (NMDevice *self, const char *mode_arg) } static gboolean -arp_announce_round2 (gpointer self) +arp_announce_round2 (gpointer user_data) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevice *self = user_data; + NMDevicePrivate *priv; + g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE); + + priv = NM_DEVICE_GET_PRIVATE (self); priv->arp_round2_id = 0; if ( priv->state >= NM_DEVICE_STATE_IP_CONFIG @@ -5794,10 +6508,7 @@ arp_cleanup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->arp_round2_id) { - g_source_remove (priv->arp_round2_id); - priv->arp_round2_id = 0; - } + nm_clear_g_source (&priv->arp_round2_id); } static void @@ -5813,7 +6524,7 @@ arp_announce (NMDevice *self) /* We only care about manually-configured addresses; DHCP- and autoip-configured * ones should already have been seen on the network at this point. */ - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); if (!connection) return; s_ip4 = nm_connection_get_setting_ip4_config (connection); @@ -5827,10 +6538,9 @@ arp_announce (NMDevice *self) priv->arp_round2_id = g_timeout_add_seconds (2, arp_announce_round2, self); } -static gboolean -nm_device_activate_ip4_config_commit (gpointer user_data) +static void +activate_stage5_ip4_config_commit (NMDevice *self) { - NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActRequest *req; const char *method; @@ -5838,14 +6548,9 @@ nm_device_activate_ip4_config_commit (gpointer user_data) NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; int ip_ifindex; - /* Clear the activation source ID now that this stage has run */ - activation_source_clear (self, FALSE, AF_INET); - - _LOGD (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv4 Commit) started..."); - req = nm_device_get_act_request (self); g_assert (req); - connection = nm_act_request_get_connection (req); + connection = nm_act_request_get_applied_connection (req); g_assert (connection); /* Interface must be IFF_UP before IP config can be applied */ @@ -5860,7 +6565,7 @@ nm_device_activate_ip4_config_commit (gpointer user_data) if (!ip4_config_merge_and_apply (self, NULL, TRUE, &reason)) { _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Commit) failed"); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - goto out; + return; } /* Start IPv4 sharing if we need it */ @@ -5870,7 +6575,7 @@ nm_device_activate_ip4_config_commit (gpointer user_data) if (!start_sharing (self, priv->ip4_config)) { _LOGW (LOGD_SHARING, "Activation: Stage 5 of 5 (IPv4 Commit) start sharing failed."); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED); - goto out; + return; } } @@ -5882,7 +6587,8 @@ nm_device_activate_ip4_config_commit (gpointer user_data) && (nm_device_get_state (self) > NM_DEVICE_STATE_IP_CONFIG)) { /* Notify dispatcher scripts of new DHCP4 config */ nm_dispatcher_call (DISPATCHER_ACTION_DHCP4_CHANGE, - nm_device_get_connection (self), + nm_device_get_settings_connection (self), + nm_device_get_applied_connection (self), self, NULL, NULL, @@ -5898,15 +6604,10 @@ nm_device_activate_ip4_config_commit (gpointer user_data) if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG) nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); - -out: - _LOGD (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv4 Commit) complete."); - - return FALSE; } static void -nm_device_queued_ip_config_change_clear (NMDevice *self) +queued_ip4_config_change_clear (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -5915,6 +6616,13 @@ nm_device_queued_ip_config_change_clear (NMDevice *self) g_source_remove (priv->queued_ip4_config_id); priv->queued_ip4_config_id = 0; } +} + +static void +queued_ip6_config_change_clear (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + if (priv->queued_ip6_config_id) { _LOGD (LOGD_DEVICE, "clearing queued IP6 config change"); g_source_remove (priv->queued_ip6_config_id); @@ -5934,10 +6642,8 @@ nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *conf if (config) priv->dev_ip4_config = g_object_ref (config); - nm_device_queued_ip_config_change_clear (self); - activation_source_schedule (self, nm_device_activate_ip4_config_commit, AF_INET); - - _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Configure Commit) scheduled..."); + queued_ip4_config_change_clear (self); + activation_source_schedule (self, activate_stage5_ip4_config_commit, AF_INET); } gboolean @@ -5954,24 +6660,18 @@ nm_device_activate_ip4_state_in_wait (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_WAIT; } -static gboolean -nm_device_activate_ip6_config_commit (gpointer user_data) +static void +activate_stage5_ip6_config_commit (NMDevice *self) { - NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActRequest *req; NMConnection *connection; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; int ip_ifindex; - /* Clear the activation source ID now that this stage has run */ - activation_source_clear (self, FALSE, AF_INET6); - - _LOGD (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv6 Commit) started..."); - req = nm_device_get_act_request (self); g_assert (req); - connection = nm_act_request_get_connection (req); + connection = nm_act_request_get_applied_connection (req); g_assert (connection); /* Interface must be IFF_UP before IP config can be applied */ @@ -5983,19 +6683,23 @@ nm_device_activate_ip6_config_commit (gpointer user_data) } if (ip6_config_merge_and_apply (self, TRUE, &reason)) { - /* If IPv6 wasn't the first IP to complete, and DHCP was used, - * then ensure dispatcher scripts get the DHCP lease information. - */ - if ( priv->dhcp6_client - && nm_device_activate_ip6_state_in_conf (self) - && (nm_device_get_state (self) > NM_DEVICE_STATE_IP_CONFIG)) { - /* Notify dispatcher scripts of new DHCP6 config */ - nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE, - nm_device_get_connection (self), - self, - NULL, - NULL, - NULL); + if ( priv->dhcp6_mode != NM_RDISC_DHCP_LEVEL_NONE + && priv->ip6_state == IP_CONF) { + if (priv->dhcp6_ip6_config) { + /* If IPv6 wasn't the first IP to complete, and DHCP was used, + * then ensure dispatcher scripts get the DHCP lease information. + */ + nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE, + nm_device_get_settings_connection (self), + nm_device_get_applied_connection (self), + self, + NULL, + NULL, + NULL); + } else { + /* still waiting for first dhcp6 lease. */ + return; + } } /* Enter the IP_CHECK state if this is the first method to complete */ @@ -6010,10 +6714,6 @@ nm_device_activate_ip6_config_commit (gpointer user_data) _LOGW (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 5 of 5 (IPv6 Commit) failed"); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); } - - _LOGD (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv6 Commit) complete."); - - return FALSE; } void @@ -6029,9 +6729,7 @@ nm_device_activate_schedule_ip6_config_result (NMDevice *self) if (priv->ip6_state == IP_FAIL) priv->ip6_state = IP_CONF; - activation_source_schedule (self, nm_device_activate_ip6_config_commit, AF_INET6); - - _LOGD (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 5 of 5 (IPv6 Commit) scheduled..."); + activation_source_schedule (self, activate_stage5_ip6_config_commit, AF_INET6); } gboolean @@ -6073,10 +6771,7 @@ dnsmasq_cleanup (NMDevice *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_clear_g_signal_handler (priv->dnsmasq_manager, &priv->dnsmasq_state_id); nm_dnsmasq_manager_stop (priv->dnsmasq_manager); g_object_unref (priv->dnsmasq_manager); @@ -6108,14 +6803,6 @@ nm_device_get_is_nm_owned (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->is_nm_owned; } -void -nm_device_set_nm_owned (NMDevice *self) -{ - g_return_if_fail (NM_IS_DEVICE (self)); - - NM_DEVICE_GET_PRIVATE (self)->is_nm_owned = TRUE; -} - /* * delete_on_deactivate_link_delete * @@ -6129,16 +6816,21 @@ delete_on_deactivate_link_delete (gpointer user_data) DeleteOnDeactivateData *data = user_data; NMDevice *self = data->device; + _LOGD (LOGD_DEVICE, "delete_on_deactivate: cleanup and delete virtual link #%d (id=%u)", + data->ifindex, data->idle_add_id); + if (data->device) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (data->device); + gs_free_error GError *error = NULL; g_object_remove_weak_pointer (G_OBJECT (data->device), (void **) &data->device); priv->delete_on_deactivate_data = NULL; - } - _LOGD (LOGD_DEVICE, "delete_on_deactivate: cleanup and delete virtual link #%d (id=%u)", - data->ifindex, data->idle_add_id); - nm_platform_link_delete (NM_PLATFORM_GET, data->ifindex); + if (!nm_device_unrealize (data->device, TRUE, &error)) + _LOGD (LOGD_DEVICE, "delete_on_deactivate: unrealizing %d failed (%s)", data->ifindex, error->message); + } else + nm_platform_link_delete (NM_PLATFORM_GET, data->ifindex); + g_free (data); return FALSE; } @@ -6173,7 +6865,7 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex) return; if (priv->queued_act_request) return; - if (!nm_device_is_software (self)) + if (!nm_device_is_software (self) || !nm_device_is_real (self)) return; if (nm_device_get_state (self) == NM_DEVICE_STATE_UNMANAGED) return; @@ -6193,8 +6885,330 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex) } static void +_cleanup_ip4_pre (NMDevice *self, CleanupType cleanup_type) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + priv->ip4_state = IP_NONE; + queued_ip4_config_change_clear (self); + + dhcp4_cleanup (self, cleanup_type, FALSE); + arp_cleanup (self); + dnsmasq_cleanup (self); + ipv4ll_cleanup (self); +} + +static void +_cleanup_ip6_pre (NMDevice *self, CleanupType cleanup_type) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + priv->ip6_state = IP_NONE; + queued_ip6_config_change_clear (self); + + dhcp6_cleanup (self, cleanup_type, FALSE); + linklocal6_cleanup (self); + addrconf6_cleanup (self); +} + +G_GNUC_NULL_TERMINATED +static gboolean +_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, GError **error, ...) +{ + va_list ap; + const char *key; + guint found_keys = 0; + +#if NM_MORE_ASSERTS > 10 + /* Assert that the keys are unique. */ + { + gs_unref_hashtable GHashTable *check_dups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + + va_start (ap, error); + while ((key = va_arg (ap, const char *))) { + if (!g_hash_table_add (check_dups, (char *) key)) + nm_assert (FALSE); + } + va_end (ap); + nm_assert (g_hash_table_size (check_dups) > 0); + } +#endif + + if (!hash || g_hash_table_size (hash) == 0) + return TRUE; + + va_start (ap, error); + while ((key = va_arg (ap, const char *))) { + if (g_hash_table_contains (hash, key)) + found_keys++; + } + va_end (ap); + + if (found_keys != g_hash_table_size (hash)) { + GHashTableIter iter; + const char *k = NULL; + const char *first_invalid_key = NULL; + + if (!error) + return FALSE; + + g_hash_table_iter_init (&iter, hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &k, NULL)) { + va_start (ap, error); + while ((key = va_arg (ap, const char *))) { + if (!strcmp (key, k)) { + first_invalid_key = k; + break; + } + } + va_end (ap); + if (first_invalid_key) + break; + } + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + "Can't reapply changes to '%s%s%s' setting", + setting_name ? : "", + setting_name ? "." : "", + first_invalid_key ? : "<UNKNOWN>"); + g_return_val_if_fail (first_invalid_key, FALSE); + return FALSE; + } + + return TRUE; +} + +void +nm_device_reactivate_ip4_config (NMDevice *self, + NMSettingIPConfig *s_ip4_old, + NMSettingIPConfig *s_ip4_new) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->ip4_state != IP_NONE) { + g_clear_object (&priv->con_ip4_config); + priv->con_ip4_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + nm_ip4_config_merge_setting (priv->con_ip4_config, + s_ip4_new, + nm_device_get_ip4_route_metric (self)); + + if (strcmp (nm_setting_ip_config_get_method (s_ip4_new), + nm_setting_ip_config_get_method (s_ip4_old))) { + _cleanup_ip4_pre (self, CLEANUP_TYPE_DECONFIGURE); + priv->ip4_state = IP_WAIT; + if (!nm_device_activate_stage3_ip4_start (self)) + _LOGW (LOGD_IP4, "Failed to apply IPv4 configuration"); + } else + ip4_config_merge_and_apply (self, NULL, TRUE, NULL); + } +} + +void +nm_device_reactivate_ip6_config (NMDevice *self, + NMSettingIPConfig *s_ip6_old, + NMSettingIPConfig *s_ip6_new) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->ip6_state != IP_NONE) { + g_clear_object (&priv->con_ip6_config); + priv->con_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + nm_ip6_config_merge_setting (priv->con_ip6_config, + s_ip6_new, + nm_device_get_ip6_route_metric (self)); + + if (strcmp (nm_setting_ip_config_get_method (s_ip6_new), + nm_setting_ip_config_get_method (s_ip6_old))) { + _cleanup_ip6_pre (self, CLEANUP_TYPE_DECONFIGURE); + priv->ip6_state = IP_WAIT; + if (!nm_device_activate_stage3_ip6_start (self)) + _LOGW (LOGD_IP6, "Failed to apply IPv6 configuration"); + } else + ip6_config_merge_and_apply (self, TRUE, NULL); + } +} + + +/* reapply_connection: + * @connection: the new connection settings to be applied or %NULL to reapply + * the current settings connection + * @error: the error if %FALSE is returned + * + * Change configuration of an already configured device if possible. + * Updates the device's applied connection upon success. + * + * Return: %FALSE if the new configuration can not be reapplied. + */ +static gboolean +reapply_connection (NMDevice *self, + NMConnection *connection, + GError **error) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMConnection *applied = nm_device_get_applied_connection (self); + gs_unref_object NMConnection *applied_clone = NULL; + gs_unref_hashtable GHashTable *diffs = NULL; + NMConnection *con_old, *con_new; + NMSettingIPConfig *s_ip4_old, *s_ip4_new; + NMSettingIPConfig *s_ip6_old, *s_ip6_new; + + if (priv->state != NM_DEVICE_STATE_ACTIVATED) { + g_set_error_literal (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ACTIVE, + "Device is not activated"); + return FALSE; + } + + nm_connection_diff (connection, + applied, + NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP | + NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS, + &diffs); + + /************************************************************************** + * check for unsupported changes and reject to reapply + *************************************************************************/ + if (!_hash_check_invalid_keys (diffs, NULL, error, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_CONNECTION_SETTING_NAME, + NULL)) + return FALSE; + + if (!_hash_check_invalid_keys (diffs ? g_hash_table_lookup (diffs, NM_SETTING_CONNECTION_SETTING_NAME) : NULL, + NM_SETTING_CONNECTION_SETTING_NAME, + error, + NM_SETTING_CONNECTION_ZONE, + NM_SETTING_CONNECTION_METERED, + NULL)) + return FALSE; + + _LOGD (LOGD_DEVICE, "reapply"); + + /************************************************************************** + * Update applied connection + *************************************************************************/ + + if (diffs) { + con_old = applied_clone = nm_simple_connection_new_clone (applied); + con_new = applied; + nm_connection_replace_settings_from_connection (applied, connection); + } else + con_old = con_new = applied; + + s_ip4_new = nm_connection_get_setting_ip4_config (con_new); + s_ip4_old = nm_connection_get_setting_ip4_config (con_old); + s_ip6_new = nm_connection_get_setting_ip6_config (con_new); + s_ip6_old = nm_connection_get_setting_ip6_config (con_old); + + /************************************************************************** + * Reapply changes + *************************************************************************/ + + nm_device_update_firewall_zone (self); + nm_device_update_metered (self); + + nm_device_reactivate_ip4_config (self, s_ip4_old, s_ip4_new); + nm_device_reactivate_ip6_config (self, s_ip6_old, s_ip6_new); + + return TRUE; +} + +static void +reapply_cb (NMDevice *self, + GDBusMethodInvocation *context, + NMAuthSubject *subject, + GError *error, + gpointer user_data) +{ + gs_unref_object NMConnection *connection = NM_CONNECTION (user_data); + GError *local = NULL; + + if (error) { + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, subject, error->message); + g_dbus_method_invocation_return_gerror (context, error); + return; + } + + if (!reapply_connection (self, + connection ? : (NMConnection *) nm_device_get_settings_connection (self), + &local)) { + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, subject, local->message); + g_dbus_method_invocation_take_error (context, local); + local = NULL; + } else { + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, TRUE, subject, NULL); + g_dbus_method_invocation_return_value (context, NULL); + } +} + +static void +impl_device_reapply (NMDevice *self, + GDBusMethodInvocation *context, + GVariant *settings, + guint flags) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMSettingsConnection *settings_connection; + NMConnection *connection = NULL; + GError *error = NULL; + + /* No flags supported as of now. */ + if (flags != 0) { + error = g_error_new_literal (NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ACTIVE, + "Invalid flags specified"); + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, context, error->message); + g_dbus_method_invocation_take_error (context, error); + return; + } + + if (priv->state != NM_DEVICE_STATE_ACTIVATED) { + error = g_error_new_literal (NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ACTIVE, + "Device is not activated"); + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, context, error->message); + g_dbus_method_invocation_take_error (context, error); + return; + } + + settings_connection = nm_device_get_settings_connection (self); + g_return_if_fail (settings_connection); + + if (settings && g_variant_n_children (settings)) { + /* New settings specified inline. */ + connection = nm_simple_connection_new_from_dbus (settings, &error); + if (!connection) { + g_prefix_error (&error, "The settings specified are invalid: "); + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, context, error->message); + g_dbus_method_invocation_take_error (context, error); + return; + } + nm_connection_clear_secrets (connection); + } + + /* Ask the manager to authenticate this request for us */ + g_signal_emit (self, signals[AUTH_REQUEST], 0, + context, + nm_device_get_applied_connection (self), + NM_AUTH_PERMISSION_NETWORK_CONTROL, + TRUE, + reapply_cb, + connection); +} + +static void disconnect_cb (NMDevice *self, - DBusGMethodInvocation *context, + GDBusMethodInvocation *context, + NMAuthSubject *subject, GError *error, gpointer user_data) { @@ -6202,7 +7216,8 @@ disconnect_cb (NMDevice *self, GError *local = NULL; if (error) { - dbus_g_method_return_error (context, error); + g_dbus_method_invocation_return_gerror (context, error); + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DISCONNECT, self, FALSE, subject, error->message); return; } @@ -6211,15 +7226,16 @@ disconnect_cb (NMDevice *self, 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); + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DISCONNECT, self, FALSE, subject, local->message); + g_dbus_method_invocation_take_error (context, local); } else { nm_device_set_autoconnect (self, FALSE); nm_device_state_changed (self, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_USER_REQUESTED); - dbus_g_method_return (context); + g_dbus_method_invocation_return_value (context, NULL); + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DISCONNECT, self, TRUE, subject, NULL); } } @@ -6233,7 +7249,7 @@ _clear_queued_act_request (NMDevicePrivate *priv) } static void -impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context) +impl_device_disconnect (NMDevice *self, GDBusMethodInvocation *context) { NMConnection *connection; GError *error = NULL; @@ -6242,12 +7258,11 @@ impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context) 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); + g_dbus_method_invocation_take_error (context, error); return; } - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); g_assert (connection); /* Ask the manager to authenticate this request for us */ @@ -6262,31 +7277,37 @@ impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context) static void delete_cb (NMDevice *self, - DBusGMethodInvocation *context, + GDBusMethodInvocation *context, + NMAuthSubject *subject, GError *error, gpointer user_data) { + GError *local = NULL; + if (error) { - dbus_g_method_return_error (context, error); + g_dbus_method_invocation_return_gerror (context, error); + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DELETE, self, FALSE, subject, error->message); return; } /* Authorized */ - nm_platform_link_delete (NM_PLATFORM_GET, nm_device_get_ifindex (self)); - dbus_g_method_return (context); + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DELETE, self, TRUE, subject, NULL); + if (nm_device_unrealize (self, TRUE, &local)) + g_dbus_method_invocation_return_value (context, NULL); + else + g_dbus_method_invocation_take_error (context, local); } static void -impl_device_delete (NMDevice *self, DBusGMethodInvocation *context) +impl_device_delete (NMDevice *self, GDBusMethodInvocation *context) { GError *error = NULL; - if (!nm_device_is_software (self)) { + if (!nm_device_is_software (self) || !nm_device_is_real (self)) { error = g_error_new_literal (NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_SOFTWARE, - "This device is not a software device"); - dbus_g_method_return_error (context, error); - g_error_free (error); + "This device is not a software device or is not realized"); + g_dbus_method_invocation_take_error (context, error); return; } @@ -6317,7 +7338,7 @@ _device_activate (NMDevice *self, NMActRequest *req) priv = NM_DEVICE_GET_PRIVATE (self); - connection = nm_act_request_get_connection (req); + connection = nm_act_request_get_applied_connection (req); g_assert (connection); _LOGI (LOGD_DEVICE, "Activation: starting connection '%s' (%s)", @@ -6378,7 +7399,7 @@ _carrier_wait_check_act_request_must_queue (NMDevice *self, NMActRequest *req) if (priv->carrier_wait_id == 0) return FALSE; - connection = nm_act_request_get_connection (req); + connection = nm_act_request_get_applied_connection (req); if (!connection_requires_carrier (connection)) return FALSE; @@ -6408,19 +7429,19 @@ _carrier_wait_check_act_request_must_queue (NMDevice *self, NMActRequest *req) } void -nm_device_steal_connection (NMDevice *self, NMConnection *connection) +nm_device_steal_connection (NMDevice *self, NMSettingsConnection *connection) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); _LOGW (LOGD_DEVICE, "disconnecting connection '%s' for new activation request.", - nm_connection_get_id (connection)); + nm_settings_connection_get_id (connection)); if ( priv->queued_act_request - && connection == nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (priv->queued_act_request))) + && connection == nm_active_connection_get_settings_connection (NM_ACTIVE_CONNECTION (priv->queued_act_request))) _clear_queued_act_request (priv); if ( priv->act_request - && connection == nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (priv->act_request)) + && connection == nm_active_connection_get_settings_connection (NM_ACTIVE_CONNECTION (priv->act_request)) && priv->state < NM_DEVICE_STATE_DEACTIVATING) nm_device_state_changed (self, NM_DEVICE_STATE_DEACTIVATING, @@ -6481,7 +7502,7 @@ nm_device_is_activating (NMDevice *self) * handler is actually run. If there's an activation handler scheduled * we're activating anyway. */ - return priv->act_source_id ? TRUE : FALSE; + return priv->act_handle4.id ? TRUE : FALSE; } /* IP Configuration stuff */ @@ -6516,13 +7537,19 @@ nm_device_set_ip4_config (NMDevice *self, gboolean has_changes = FALSE; gboolean success = TRUE; NMDeviceStateReason reason_local = NM_DEVICE_STATE_REASON_NONE; - int ip_ifindex; + int ip_ifindex, config_ifindex; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); priv = NM_DEVICE_GET_PRIVATE (self); ip_ifindex = nm_device_get_ip_ifindex (self); + if (new_config) { + config_ifindex = nm_ip4_config_get_ifindex (new_config); + if (config_ifindex > 0) + g_return_val_if_fail (ip_ifindex == config_ifindex, FALSE); + } + old_config = priv->ip4_config; /* Always commit to nm-platform to update lifetimes */ @@ -6548,25 +7575,23 @@ nm_device_set_ip4_config (NMDevice *self, nm_ip4_config_replace (old_config, new_config, &has_changes); if (has_changes) { _LOGD (LOGD_IP4, "update IP4Config instance (%s)", - nm_ip4_config_get_dbus_path (old_config)); + nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config))); } } else { has_changes = TRUE; priv->ip4_config = g_object_ref (new_config); - if (success && !nm_ip4_config_get_dbus_path (new_config)) { - /* Export over D-Bus */ - nm_ip4_config_export (new_config); - } + if (success && !nm_exported_object_is_exported (NM_EXPORTED_OBJECT (new_config))) + nm_exported_object_export (NM_EXPORTED_OBJECT (new_config)); _LOGD (LOGD_IP4, "set IP4Config instance (%s)", - nm_ip4_config_get_dbus_path (new_config)); + nm_exported_object_get_path (NM_EXPORTED_OBJECT (new_config))); } } else if (old_config) { has_changes = TRUE; priv->ip4_config = NULL; _LOGD (LOGD_IP4, "clear IP4Config instance (%s)", - nm_ip4_config_get_dbus_path (old_config)); + nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config))); /* Device config is invalid if combined config is invalid */ g_clear_object (&priv->dev_ip4_config); } @@ -6580,17 +7605,26 @@ nm_device_set_ip4_config (NMDevice *self, 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 != priv->ip4_config && old_config) - g_object_unref (old_config); + if (old_config != priv->ip4_config) + nm_exported_object_clear_and_unexport (&old_config); if (nm_device_uses_generated_assumed_connection (self)) { - NMConnection *connection = nm_device_get_connection (self); + NMConnection *connection = nm_device_get_applied_connection (self); + NMConnection *settings_connection = NM_CONNECTION (nm_device_get_settings_connection (self)); NMSetting *s_ip4; g_object_freeze_notify (G_OBJECT (connection)); + g_object_freeze_notify (G_OBJECT (settings_connection)); + + nm_connection_remove_setting (settings_connection, NM_TYPE_SETTING_IP4_CONFIG); + s_ip4 = nm_ip4_config_create_setting (priv->ip4_config); + nm_connection_add_setting (settings_connection, s_ip4); + nm_connection_remove_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); s_ip4 = nm_ip4_config_create_setting (priv->ip4_config); nm_connection_add_setting (connection, s_ip4); + + g_object_thaw_notify (G_OBJECT (settings_connection)); g_object_thaw_notify (G_OBJECT (connection)); } @@ -6603,18 +7637,49 @@ nm_device_set_ip4_config (NMDevice *self, return success; } +static gboolean +_replace_vpn_config_in_list (GSList **plist, GObject *old, GObject *new) +{ + GSList *old_link; + + /* Below, assert that we have an @old instance to replace and that + * @new is not yet tracked. But still, behave correctly in any + * case. */ + + if ( old + && (old_link = g_slist_find (*plist, old))) { + if (old != new) { + if (new) + old_link->data = g_object_ref (new); + else + *plist = g_slist_remove_link (*plist, old_link); + g_object_unref (old); + } + return TRUE; + } + + if (new) { + if (!g_slist_find (*plist, new)) + *plist = g_slist_append (*plist, g_object_ref (new)); + else + g_return_val_if_reached (TRUE); + g_return_val_if_fail (!old, TRUE); + return TRUE; + } + + /* return FALSE if both @old and @new are unset. */ + g_return_val_if_fail (!old, FALSE); + return FALSE; +} + void -nm_device_set_vpn4_config (NMDevice *self, NMIP4Config *config) +nm_device_replace_vpn4_config (NMDevice *self, NMIP4Config *old, NMIP4Config *config) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->vpn4_config == config) + if (!_replace_vpn_config_in_list (&priv->vpn4_configs, (GObject *) old, (GObject *) config)) return; - g_clear_object (&priv->vpn4_config); - if (config) - priv->vpn4_config = g_object_ref (config); - /* NULL to use existing configs */ if (!ip4_config_merge_and_apply (self, NULL, TRUE, NULL)) _LOGW (LOGD_IP4, "failed to set VPN routes for device"); @@ -6649,13 +7714,19 @@ nm_device_set_ip6_config (NMDevice *self, gboolean has_changes = FALSE; gboolean success = TRUE; NMDeviceStateReason reason_local = NM_DEVICE_STATE_REASON_NONE; - int ip_ifindex; + int ip_ifindex, config_ifindex; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); priv = NM_DEVICE_GET_PRIVATE (self); ip_ifindex = nm_device_get_ip_ifindex (self); + if (new_config) { + config_ifindex = nm_ip6_config_get_ifindex (new_config); + if (config_ifindex > 0) + g_return_val_if_fail (ip_ifindex == config_ifindex, FALSE); + } + old_config = priv->ip6_config; /* Always commit to nm-platform to update lifetimes */ @@ -6675,25 +7746,23 @@ nm_device_set_ip6_config (NMDevice *self, nm_ip6_config_replace (old_config, new_config, &has_changes); if (has_changes) { _LOGD (LOGD_IP6, "update IP6Config instance (%s)", - nm_ip6_config_get_dbus_path (old_config)); + nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config))); } } else { has_changes = TRUE; priv->ip6_config = g_object_ref (new_config); - if (success && !nm_ip6_config_get_dbus_path (new_config)) { - /* Export over D-Bus */ - nm_ip6_config_export (new_config); - } + if (success && !nm_exported_object_is_exported (NM_EXPORTED_OBJECT (new_config))) + nm_exported_object_export (NM_EXPORTED_OBJECT (new_config)); _LOGD (LOGD_IP6, "set IP6Config instance (%s)", - nm_ip6_config_get_dbus_path (new_config)); + nm_exported_object_get_path (NM_EXPORTED_OBJECT (new_config))); } } else if (old_config) { has_changes = TRUE; priv->ip6_config = NULL; _LOGD (LOGD_IP6, "clear IP6Config instance (%s)", - nm_ip6_config_get_dbus_path (old_config)); + nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config))); } nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self); @@ -6703,17 +7772,26 @@ nm_device_set_ip6_config (NMDevice *self, 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 != priv->ip6_config && old_config) - g_object_unref (old_config); + if (old_config != priv->ip6_config) + nm_exported_object_clear_and_unexport (&old_config); if (nm_device_uses_generated_assumed_connection (self)) { - NMConnection *connection = nm_device_get_connection (self); + NMConnection *connection = nm_device_get_applied_connection (self); + NMConnection *settings_connection = NM_CONNECTION (nm_device_get_settings_connection (self)); NMSetting *s_ip6; g_object_freeze_notify (G_OBJECT (connection)); + g_object_freeze_notify (G_OBJECT (settings_connection)); + + nm_connection_remove_setting (settings_connection, NM_TYPE_SETTING_IP6_CONFIG); + s_ip6 = nm_ip6_config_create_setting (priv->ip6_config); + nm_connection_add_setting (settings_connection, s_ip6); + nm_connection_remove_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); s_ip6 = nm_ip6_config_create_setting (priv->ip6_config); nm_connection_add_setting (connection, s_ip6); + + g_object_thaw_notify (G_OBJECT (settings_connection)); g_object_thaw_notify (G_OBJECT (connection)); } @@ -6727,17 +7805,13 @@ nm_device_set_ip6_config (NMDevice *self, } void -nm_device_set_vpn6_config (NMDevice *self, NMIP6Config *config) +nm_device_replace_vpn6_config (NMDevice *self, NMIP6Config *old, NMIP6Config *config) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->vpn6_config == config) + if (!_replace_vpn_config_in_list (&priv->vpn6_configs, (GObject *) old, (GObject *) config)) return; - g_clear_object (&priv->vpn6_config); - if (config) - priv->vpn6_config = g_object_ref (config); - /* NULL to use existing configs */ if (!ip6_config_merge_and_apply (self, TRUE, NULL)) _LOGW (LOGD_IP6, "failed to set VPN routes for device"); @@ -6821,7 +7895,8 @@ ip_check_pre_up (NMDevice *self) priv->dispatcher.post_state = NM_DEVICE_STATE_SECONDARIES; priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_UP, - nm_device_get_connection (self), + nm_device_get_settings_connection (self), + nm_device_get_applied_connection (self), self, dispatcher_complete_proceed_state, self, @@ -6904,7 +7979,7 @@ ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - guint log_domain = priv->gw_ping.log_domain; + NMLogDomain log_domain = priv->gw_ping.log_domain; gboolean success = FALSE; if (!priv->gw_ping.watch) @@ -6951,7 +8026,7 @@ ip_check_ping_timeout_cb (gpointer user_data) static gboolean start_ping (NMDevice *self, - guint log_domain, + NMLogDomain log_domain, const char *binary, const char *address, guint timeout) @@ -6985,7 +8060,7 @@ nm_device_start_ip_check (NMDevice *self) guint timeout = 0; const char *ping_binary = NULL; char buf[INET6_ADDRSTRLEN] = { 0 }; - guint log_domain = LOGD_IP4; + NMLogDomain log_domain = LOGD_IP4; /* Shouldn't be any active ping here, since IP_CHECK happens after the * first IP method completes. Any subsequently completing IP method doesn't @@ -6996,7 +8071,7 @@ nm_device_start_ip_check (NMDevice *self) g_assert (!priv->gw_ping.pid); g_assert (priv->ip4_state == IP_DONE || priv->ip6_state == IP_DONE); - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); g_assert (connection); s_con = nm_connection_get_setting_connection (connection); @@ -7210,12 +8285,33 @@ nm_device_get_firmware_missing (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->firmware_missing; } +void +nm_device_set_nm_plugin_missing (NMDevice *self, gboolean new_missing) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); + if (priv->nm_plugin_missing != new_missing) { + priv->nm_plugin_missing = new_missing; + g_object_notify (G_OBJECT (self), NM_DEVICE_NM_PLUGIN_MISSING); + } +} + +gboolean +nm_device_get_nm_plugin_missing (NMDevice *self) +{ + return NM_DEVICE_GET_PRIVATE (self)->nm_plugin_missing; +} + static NMIP4Config * find_ip4_lease_config (NMDevice *self, NMConnection *connection, NMIP4Config *ext_ip4_config) { const char *ip_iface = nm_device_get_ip_iface (self); + int ip_ifindex = nm_device_get_ip_ifindex (self); GSList *leases, *liter; NMIP4Config *found = NULL; @@ -7224,6 +8320,7 @@ find_ip4_lease_config (NMDevice *self, leases = nm_dhcp_manager_get_lease_ip_configs (nm_dhcp_manager_get (), ip_iface, + ip_ifindex, nm_connection_get_uuid (connection), FALSE, nm_device_get_ip4_route_metric (self)); @@ -7310,6 +8407,24 @@ capture_lease_config (NMDevice *self, } static void +_ip4_config_intersect (gpointer value, gpointer user_data) +{ + NMIP4Config *dst = (NMIP4Config *) value; + NMIP4Config *src = (NMIP4Config *) user_data; + + nm_ip4_config_intersect (dst, src); +} + +static void +_ip4_config_subtract (gpointer value, gpointer user_data) +{ + NMIP4Config *dst = (NMIP4Config *) user_data; + NMIP4Config *src = (NMIP4Config *) value; + + nm_ip4_config_subtract (dst, src); +} + +static void update_ip4_config (NMDevice *self, gboolean initial) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -7339,15 +8454,16 @@ update_ip4_config (NMDevice *self, gboolean initial) * kernel routes. */ /* This function was called upon external changes. Remove the configuration - * (adresses,routes) that is no longer present externally from the interal - * config. This way, we don't readd addresses that were manually removed + * (addresses,routes) that is no longer present externally from the internal + * config. This way, we don't re-add addresses that were manually removed * by the user. */ if (priv->con_ip4_config) nm_ip4_config_intersect (priv->con_ip4_config, priv->ext_ip4_config); if (priv->dev_ip4_config) nm_ip4_config_intersect (priv->dev_ip4_config, priv->ext_ip4_config); - if (priv->vpn4_config) - nm_ip4_config_intersect (priv->vpn4_config, priv->ext_ip4_config); + + g_slist_foreach (priv->vpn4_configs, _ip4_config_intersect, priv->ext_ip4_config); + if (priv->wwan_ip4_config) nm_ip4_config_intersect (priv->wwan_ip4_config, priv->ext_ip4_config); @@ -7358,8 +8474,9 @@ update_ip4_config (NMDevice *self, gboolean initial) nm_ip4_config_subtract (priv->ext_ip4_config, priv->con_ip4_config); if (priv->dev_ip4_config) nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config); - if (priv->vpn4_config) - nm_ip4_config_subtract (priv->ext_ip4_config, priv->vpn4_config); + + g_slist_foreach (priv->vpn4_configs, _ip4_config_subtract, priv->ext_ip4_config); + if (priv->wwan_ip4_config) nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config); @@ -7368,6 +8485,24 @@ update_ip4_config (NMDevice *self, gboolean initial) } static void +_ip6_config_intersect (gpointer value, gpointer user_data) +{ + NMIP6Config *dst = (NMIP6Config *) value; + NMIP6Config *src = (NMIP6Config *) user_data; + + nm_ip6_config_intersect (dst, src); +} + +static void +_ip6_config_subtract (gpointer value, gpointer user_data) +{ + NMIP6Config *dst = (NMIP6Config *) user_data; + NMIP6Config *src = (NMIP6Config *) value; + + nm_ip6_config_subtract (dst, src); +} + +static void update_ip6_config (NMDevice *self, gboolean initial) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -7393,8 +8528,8 @@ update_ip6_config (NMDevice *self, gboolean initial) have_ip6_address (priv->ext_ip6_config, TRUE); /* This function was called upon external changes. Remove the configuration - * (adresses,routes) that is no longer present externally from the interal - * config. This way, we don't readd addresses that were manually removed + * (addresses,routes) that is no longer present externally from the internal + * config. This way, we don't re-add addresses that were manually removed * by the user. */ if (priv->con_ip6_config) nm_ip6_config_intersect (priv->con_ip6_config, priv->ext_ip6_config); @@ -7404,8 +8539,7 @@ update_ip6_config (NMDevice *self, gboolean initial) nm_ip6_config_intersect (priv->dhcp6_ip6_config, priv->ext_ip6_config); if (priv->wwan_ip6_config) nm_ip6_config_intersect (priv->wwan_ip6_config, priv->ext_ip6_config); - if (priv->vpn6_config) - nm_ip6_config_intersect (priv->vpn6_config, priv->ext_ip6_config); + g_slist_foreach (priv->vpn6_configs, _ip6_config_intersect, priv->ext_ip6_config); /* Remove parts from ext_ip6_config to only contain the information that * was configured externally -- we already have the same configuration from @@ -7418,8 +8552,7 @@ update_ip6_config (NMDevice *self, gboolean initial) nm_ip6_config_subtract (priv->ext_ip6_config, priv->dhcp6_ip6_config); if (priv->wwan_ip6_config) nm_ip6_config_subtract (priv->ext_ip6_config, priv->wwan_ip6_config); - if (priv->vpn6_config) - nm_ip6_config_subtract (priv->ext_ip6_config, priv->vpn6_config); + g_slist_foreach (priv->vpn6_configs, _ip6_config_subtract, priv->ext_ip6_config); ip6_config_merge_and_apply (self, FALSE, NULL); } @@ -7462,6 +8595,8 @@ queued_ip6_config_change (gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + GSList *iter; + gboolean need_ipv6ll = FALSE; /* Wait for any queued state changes */ if (priv->queued_state.id) @@ -7471,13 +8606,38 @@ queued_ip6_config_change (gpointer user_data) g_object_ref (self); update_ip6_config (self, FALSE); - /* If no IPv6 link-local address exists but other addresses do then we - * must add the LL address to remain conformant with RFC 3513 chapter 2.1 - * ("Addressing Model"): "All interfaces are required to have at least - * one link-local unicast address". - */ - if (priv->ip6_config && nm_ip6_config_get_num_addresses (priv->ip6_config)) - check_and_add_ipv6ll_addr (self); + if ( nm_platform_link_get (NM_PLATFORM_GET, priv->ifindex) + && priv->state < NM_DEVICE_STATE_DEACTIVATING) { + /* Handle DAD falures */ + for (iter = priv->dad6_failed_addrs; iter; iter = g_slist_next (iter)) { + NMPlatformIP6Address *addr = iter->data; + + if (addr->source >= NM_IP_CONFIG_SOURCE_USER) + continue; + + _LOGI (LOGD_IP6, "ipv6: duplicate address check failed for the %s address", + nm_platform_ip6_address_to_string (addr, NULL, 0)); + + if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) + need_ipv6ll = TRUE; + else if (priv->rdisc) + nm_rdisc_dad_failed (priv->rdisc, &addr->address); + } + + /* If no IPv6 link-local address exists but other addresses do then we + * must add the LL address to remain conformant with RFC 3513 chapter 2.1 + * ("Addressing Model"): "All interfaces are required to have at least + * one link-local unicast address". + */ + if (priv->ip6_config && nm_ip6_config_get_num_addresses (priv->ip6_config)) + need_ipv6ll = TRUE; + + if (need_ipv6ll) + check_and_add_ipv6ll_addr (self); + } + + g_slist_free_full (priv->dad6_failed_addrs, g_free); + priv->dad6_failed_addrs = NULL; g_object_unref (self); @@ -7490,15 +8650,16 @@ device_ipx_changed (NMPlatform *platform, int ifindex, gpointer platform_object, NMPlatformSignalChangeType change_type, - NMPlatformReason reason, NMDevice *self) { NMDevicePrivate *priv; + NMPlatformIP6Address *addr; if (nm_device_get_ip_ifindex (self) != ifindex) return; priv = NM_DEVICE_GET_PRIVATE (self); + switch (obj_type) { case NMP_OBJECT_TYPE_IP4_ADDRESS: case NMP_OBJECT_TYPE_IP4_ROUTE: @@ -7508,6 +8669,16 @@ device_ipx_changed (NMPlatform *platform, } break; case NMP_OBJECT_TYPE_IP6_ADDRESS: + addr = platform_object; + + if ( priv->state > NM_DEVICE_STATE_DISCONNECTED + && priv->state < NM_DEVICE_STATE_DEACTIVATING + && ( (change_type == NM_PLATFORM_SIGNAL_CHANGED && addr->flags & IFA_F_DADFAILED) + || (change_type == NM_PLATFORM_SIGNAL_REMOVED && addr->flags & IFA_F_TENTATIVE))) { + priv->dad6_failed_addrs = g_slist_append (priv->dad6_failed_addrs, + g_memdup (addr, sizeof (NMPlatformIP6Address))); + } + /* fallthrough */ case NMP_OBJECT_TYPE_IP6_ROUTE: if (!priv->queued_ip6_config_id) { priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); @@ -7540,23 +8711,24 @@ nm_device_get_managed (NMDevice *self) * default-unmanaged flag (eg, only NM_UNMANAGED_DEFAULT is set) then * the device is managed whenever it's not in the UNMANAGED state. */ - managed = !(priv->unmanaged_flags & ~NM_UNMANAGED_DEFAULT); - if (managed && (priv->unmanaged_flags & NM_UNMANAGED_DEFAULT)) + managed = !NM_FLAGS_ANY (priv->unmanaged_flags, ~NM_UNMANAGED_DEFAULT); + if (managed && NM_FLAGS_HAS (priv->unmanaged_flags, NM_UNMANAGED_DEFAULT)) managed = (priv->state > NM_DEVICE_STATE_UNMANAGED); return managed; } /** - * nm_device_get_unmanaged_flag(): + * nm_device_get_unmanaged_flags(): * @self: the #NMDevice + * @flag: return only the selected flags * - * Returns: %TRUE if the device is unmanaged for @flag. + * Returns: the unmanage flags of the device (filtered with @flag) */ -gboolean -nm_device_get_unmanaged_flag (NMDevice *self, NMUnmanagedFlags flag) +NMUnmanagedFlags +nm_device_get_unmanaged_flags (NMDevice *self, NMUnmanagedFlags flag) { - return NM_FLAGS_ANY (NM_DEVICE_GET_PRIVATE (self)->unmanaged_flags, flag); + return NM_DEVICE_GET_PRIVATE (self)->unmanaged_flags & flag; } /** @@ -7568,14 +8740,42 @@ nm_device_get_unmanaged_flag (NMDevice *self, NMUnmanagedFlags flag) static gboolean nm_device_get_default_unmanaged (NMDevice *self) { - return nm_device_get_unmanaged_flag (self, NM_UNMANAGED_DEFAULT); + return !!nm_device_get_unmanaged_flags (self, NM_UNMANAGED_DEFAULT); +} + +static void +_set_unmanaged_flags (NMDevice *self, + NMUnmanagedFlags flags, + gboolean unmanaged) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (unmanaged) { + if (!NM_FLAGS_ALL (priv->unmanaged_flags, flags)) { + _LOGD (LOGD_DEVICE, "unmanaged: flags set to 0x%0llx (was 0x%0llx, %s 0x%0llx)", + (long long unsigned) (priv->unmanaged_flags | flags), + (long long unsigned) priv->unmanaged_flags, + "set", + (long long unsigned) flags); + priv->unmanaged_flags |= flags; + } + } else { + if (NM_FLAGS_ANY (priv->unmanaged_flags, flags)) { + _LOGD (LOGD_DEVICE, "unmanaged: flags set to 0x%0llx (was 0x%0llx, %s 0x%0llx)", + (long long unsigned) (priv->unmanaged_flags & (~flags)), + (long long unsigned) priv->unmanaged_flags, + "clear", + (long long unsigned) flags); + priv->unmanaged_flags &= ~flags; + } + } } void -nm_device_set_unmanaged (NMDevice *self, - NMUnmanagedFlags flag, - gboolean unmanaged, - NMDeviceStateReason reason) +nm_device_set_unmanaged_flags (NMDevice *self, + NMUnmanagedFlags flag, + gboolean unmanaged, + NMDeviceStateReason reason) { NMDevicePrivate *priv; gboolean was_managed, now_managed; @@ -7586,10 +8786,7 @@ nm_device_set_unmanaged (NMDevice *self, priv = NM_DEVICE_GET_PRIVATE (self); was_managed = nm_device_get_managed (self); - if (unmanaged) - priv->unmanaged_flags |= flag; - else - priv->unmanaged_flags &= ~flag; + _set_unmanaged_flags (self, flag, unmanaged); now_managed = nm_device_get_managed (self); if (was_managed != now_managed) { @@ -7605,34 +8802,41 @@ nm_device_set_unmanaged (NMDevice *self, } void -nm_device_set_unmanaged_quitting (NMDevice *self) +nm_device_set_unmanaged_flags_by_device_spec (NMDevice *self, const GSList *unmanaged_specs) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; + gboolean unmanaged; - /* It's OK to block here because we're quitting */ - if (nm_device_is_activating (self) || priv->state == NM_DEVICE_STATE_ACTIVATED) - _set_state_full (self, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_NOW_UNMANAGED, TRUE); + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->managed_touched_by_user) + return; - nm_device_set_unmanaged (self, - NM_UNMANAGED_INTERNAL, - TRUE, - NM_DEVICE_STATE_REASON_NOW_UNMANAGED); + unmanaged = nm_device_spec_match_list (self, unmanaged_specs); + nm_device_set_unmanaged_flags (self, + NM_UNMANAGED_USER, + unmanaged, + unmanaged + ? NM_DEVICE_STATE_REASON_NOW_UNMANAGED + : NM_DEVICE_STATE_REASON_NOW_MANAGED); } /** - * nm_device_set_initial_unmanaged_flag(): + * nm_device_set_unmanaged_flags_initial(): * @self: the #NMDevice * @flag: an #NMUnmanagedFlag * @unmanaged: %TRUE or %FALSE to set or clear @flag * - * Like nm_device_set_unmanaged(), but must be set before the device is + * Like nm_device_set_unmanaged_flags(), but must be set before the device is * initialized by nm_device_finish_init(), and does not trigger state changes. * Should only be used when initializing a device. */ void -nm_device_set_initial_unmanaged_flag (NMDevice *self, - NMUnmanagedFlags flag, - gboolean unmanaged) +nm_device_set_unmanaged_flags_initial (NMDevice *self, + NMUnmanagedFlags flag, + gboolean unmanaged) { NMDevicePrivate *priv; @@ -7642,13 +8846,30 @@ nm_device_set_initial_unmanaged_flag (NMDevice *self, priv = NM_DEVICE_GET_PRIVATE (self); g_return_if_fail (priv->initialized == FALSE); - if (unmanaged) - priv->unmanaged_flags |= flag; - else - priv->unmanaged_flags &= ~flag; + _set_unmanaged_flags (self, flag, unmanaged); } void +nm_device_set_unmanaged_quitting (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean need_deactivate = nm_device_is_activating (self) || + priv->state == NM_DEVICE_STATE_ACTIVATED; + + /* It's OK to block here because we're quitting */ + if (need_deactivate) + _set_state_full (self, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_NOW_UNMANAGED, TRUE); + + nm_device_set_unmanaged_flags (self, + NM_UNMANAGED_INTERNAL, + TRUE, + need_deactivate ? NM_DEVICE_STATE_REASON_REMOVED + : NM_DEVICE_STATE_REASON_NOW_UNMANAGED); +} + +/*****************************************************************************/ + +void nm_device_set_dhcp_timeout (NMDevice *self, guint32 timeout) { g_return_if_fail (NM_IS_DEVICE (self)); @@ -7670,7 +8891,84 @@ nm_device_set_dhcp_anycast_address (NMDevice *self, const char *addr) priv->dhcp_anycast_address = g_strdup (addr); } -static void +void +nm_device_reapply_settings_immediately (NMDevice *self) +{ + NMConnection *applied_connection; + NMSettingsConnection *settings_connection; + NMDeviceState state; + NMSettingConnection *s_con_settings; + NMSettingConnection *s_con_applied; + const char *zone; + NMMetered metered; + + g_return_if_fail (NM_IS_DEVICE (self)); + + state = nm_device_get_state (self); + if ( state <= NM_DEVICE_STATE_DISCONNECTED + || state > NM_DEVICE_STATE_ACTIVATED) + return; + + applied_connection = nm_device_get_applied_connection (self); + settings_connection = nm_device_get_settings_connection (self); + + if (!nm_settings_connection_has_unmodified_applied_connection (settings_connection, + applied_connection, + NM_SETTING_COMPARE_FLAG_IGNORE_REAPPLY_IMMEDIATELY)) + return; + + s_con_settings = nm_connection_get_setting_connection ((NMConnection *) settings_connection); + s_con_applied = nm_connection_get_setting_connection (applied_connection); + + if (g_strcmp0 ((zone = nm_setting_connection_get_zone (s_con_settings)), + nm_setting_connection_get_zone (s_con_applied)) != 0) { + + _LOGD (LOGD_DEVICE, "reapply setting: zone = %s%s%s", NM_PRINT_FMT_QUOTE_STRING (zone)); + + g_object_set (G_OBJECT (s_con_applied), + NM_SETTING_CONNECTION_ZONE, zone, + NULL); + + nm_device_update_firewall_zone (self); + } + + if ((metered = nm_setting_connection_get_metered (s_con_settings)) != nm_setting_connection_get_metered (s_con_applied)) { + + _LOGD (LOGD_DEVICE, "reapply setting: metered = %d", (int) metered); + + g_object_set (G_OBJECT (s_con_applied), + NM_SETTING_CONNECTION_METERED, metered, + NULL); + + nm_device_update_metered (self); + } +} + +void +nm_device_update_firewall_zone (NMDevice *self) +{ + NMConnection *applied_connection; + NMSettingConnection *s_con; + + g_return_if_fail (NM_IS_DEVICE (self)); + + applied_connection = nm_device_get_applied_connection (self); + if (!applied_connection) + return; + + s_con = nm_connection_get_setting_connection (applied_connection); + if ( nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED + && !nm_device_uses_assumed_connection (self)) { + nm_firewall_manager_add_or_change_zone (nm_firewall_manager_get (), + nm_device_get_ip_iface (self), + nm_setting_connection_get_zone (s_con), + FALSE, /* change zone */ + NULL, + NULL); + } +} + +void nm_device_update_metered (NMDevice *self) { #define NM_METERED_INVALID ((NMMetered) -1) @@ -7688,7 +8986,7 @@ nm_device_update_metered (NMDevice *self) value = NM_METERED_UNKNOWN; if (value == NM_METERED_INVALID) { - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); if (connection) { setting = nm_connection_get_setting_connection (connection); if (setting) { @@ -7723,6 +9021,34 @@ nm_device_update_metered (NMDevice *self) } } +static gboolean +_nm_device_check_connection_available (NMDevice *self, + NMConnection *connection, + NMDeviceCheckConAvailableFlags flags, + const char *specific_object) +{ + NMDeviceState state; + + state = nm_device_get_state (self); + if (state < NM_DEVICE_STATE_UNMANAGED) + return FALSE; + if ( state < NM_DEVICE_STATE_UNAVAILABLE + && nm_device_get_unmanaged_flags (self, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT)) + return FALSE; + if ( state < NM_DEVICE_STATE_DISCONNECTED + && !nm_device_is_software (self) + && ( ( !NM_FLAGS_ANY (flags, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST) + && !nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) + || ( NM_FLAGS_ANY (flags, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST) + && !nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST)))) + return FALSE; + + if (!nm_device_check_connection_compatible (self, connection)) + return FALSE; + + return NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, flags, specific_object); +} + /** * nm_device_check_connection_available(): * @self: the #NMDevice @@ -7744,25 +9070,33 @@ nm_device_check_connection_available (NMDevice *self, NMDeviceCheckConAvailableFlags flags, const char *specific_object) { - NMDeviceState state; + gboolean available; - state = nm_device_get_state (self); - if (state < NM_DEVICE_STATE_UNMANAGED) - return FALSE; - if ( state < NM_DEVICE_STATE_UNAVAILABLE - && nm_device_get_unmanaged_flag (self, NM_UNMANAGED_ALL & ~NM_UNMANAGED_DEFAULT)) - return FALSE; - if ( state < NM_DEVICE_STATE_DISCONNECTED - && ( ( !NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER) - && !nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) - || ( NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER) - && !nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER)))) - return FALSE; + available = _nm_device_check_connection_available (self, connection, flags, specific_object); - if (!nm_device_check_connection_compatible (self, connection)) - return FALSE; +#if NM_MORE_ASSERTS >= 2 + { + /* The meaning of the flags is so that *adding* a flag relaxes a condition, thus making + * the device *more* available. Assert against that requirement by testing all the flags. */ + NMDeviceCheckConAvailableFlags i, j, k; + gboolean available_all[NM_DEVICE_CHECK_CON_AVAILABLE_ALL + 1] = { FALSE }; + + for (i = 0; i <= NM_DEVICE_CHECK_CON_AVAILABLE_ALL; i++) + available_all[i] = _nm_device_check_connection_available (self, connection, i, specific_object); + + for (i = 0; i <= NM_DEVICE_CHECK_CON_AVAILABLE_ALL; i++) { + for (j = 1; j <= NM_DEVICE_CHECK_CON_AVAILABLE_ALL; j <<= 1) { + if (NM_FLAGS_HAS (i, j)) { + k = i & ~j; + nm_assert ( available_all[i] == available_all[k] + || available_all[i]); + } + } + } + } +#endif - return NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, flags, specific_object); + return available; } static void @@ -7842,6 +9176,8 @@ nm_device_recheck_available_connections (NMDevice *self) _signal_available_connections_changed (self); } + + available_connection_check_delete_unrealized (self); } /** @@ -7883,29 +9219,44 @@ nm_device_get_available_connections (NMDevice *self, const char *specific_object 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)); + NMDevice *self = user_data; + + g_return_if_fail (NM_IS_DEVICE (self)); + + if (_try_add_available_connection (self, connection)) + _signal_available_connections_changed (self); } 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)); + NMDevice *self = user_data; + + g_return_if_fail (NM_IS_DEVICE (self)); + + if (_del_available_connection (self, connection)) { + _signal_available_connections_changed (self); + available_connection_check_delete_unrealized (self); + } } static void cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) { + NMDevice *self = user_data; gboolean added, deleted; + g_return_if_fail (NM_IS_DEVICE (self)); + /* 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); + deleted = _del_available_connection (self, connection); + added = _try_add_available_connection (self, connection); /* Only signal if the connection was removed OR added, but not both */ - if (added != deleted) - _signal_available_connections_changed (NM_DEVICE (user_data)); + if (added != deleted) { + _signal_available_connections_changed (self); + available_connection_check_delete_unrealized (self); + } } gboolean @@ -7979,12 +9330,13 @@ gboolean nm_device_remove_pending_action (NMDevice *self, const char *action, gboolean assert_is_pending) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - GSList *iter; + GSList *iter, *next; guint count = 0; g_return_val_if_fail (action, FALSE); - for (iter = priv->pending_actions; iter; iter = iter->next) { + for (iter = priv->pending_actions; iter; iter = next) { + next = iter->next; if (!strcmp (action, iter->data)) { _LOGD (LOGD_DEVICE, "remove_pending_action (%d): '%s'", count + g_slist_length (iter->next), /* length excluding 'iter' */ @@ -8018,38 +9370,23 @@ nm_device_has_pending_action (NMDevice *self) /***********************************************************/ static void -_cleanup_ip_pre (NMDevice *self, CleanupType cleanup_type) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - priv->ip4_state = priv->ip6_state = IP_NONE; - nm_device_queued_ip_config_change_clear (self); - - dhcp4_cleanup (self, cleanup_type, FALSE); - arp_cleanup (self); - dhcp6_cleanup (self, cleanup_type, FALSE); - linklocal6_cleanup (self); - addrconf6_cleanup (self); - dnsmasq_cleanup (self); - aipd_cleanup (self); -} - -static void _cancel_activation (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); /* Clean up when device was deactivated during call to firewall */ if (priv->fw_call) { - nm_firewall_manager_cancel_call (nm_firewall_manager_get (), priv->fw_call); + nm_firewall_manager_cancel_call (priv->fw_call); + g_warn_if_fail (!priv->fw_call); priv->fw_call = NULL; } + priv->fw_ready = FALSE; ip_check_gw_ping_cleanup (self); /* Break the activation chain */ - activation_source_clear (self, TRUE, AF_INET); - activation_source_clear (self, TRUE, AF_INET6); + activation_source_clear (self, AF_INET); + activation_source_clear (self, AF_INET6); } static void @@ -8059,19 +9396,22 @@ _cleanup_generic_pre (NMDevice *self, CleanupType cleanup_type) _cancel_activation (self); - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); if ( cleanup_type == CLEANUP_TYPE_DECONFIGURE && connection && !nm_device_uses_assumed_connection (self)) { nm_firewall_manager_remove_from_zone (nm_firewall_manager_get (), nm_device_get_ip_iface (self), + NULL, + NULL, NULL); } /* Clear any queued transitions */ nm_device_queued_state_clear (self); - _cleanup_ip_pre (self, cleanup_type); + _cleanup_ip4_pre (self, cleanup_type); + _cleanup_ip6_pre (self, cleanup_type); } static void @@ -8098,6 +9438,8 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) priv->v4_commit_first_time = TRUE; priv->v6_commit_first_time = TRUE; + priv->linklocal6_dad_counter = 0; + /* Clean up IP configs; this does not actually deconfigure the * interface; the caller must flush routes and addresses explicitly. */ @@ -8107,15 +9449,18 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) g_clear_object (&priv->dev_ip4_config); g_clear_object (&priv->ext_ip4_config); g_clear_object (&priv->wwan_ip4_config); - g_clear_object (&priv->vpn4_config); g_clear_object (&priv->ip4_config); g_clear_object (&priv->con_ip6_config); g_clear_object (&priv->ac_ip6_config); g_clear_object (&priv->ext_ip6_config); - g_clear_object (&priv->vpn6_config); g_clear_object (&priv->wwan_ip6_config); g_clear_object (&priv->ip6_config); + g_slist_free_full (priv->vpn4_configs, g_object_unref); + priv->vpn4_configs = NULL; + g_slist_free_full (priv->vpn6_configs, g_object_unref); + priv->vpn6_configs = NULL; + clear_act_request (self); /* Clear legacy IPv4 address property */ @@ -8177,11 +9522,9 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean nm_device_master_release_slaves (self); /* slave: mark no longer enslaved */ - if (nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex) <= 0) { - g_clear_object (&priv->master); - priv->enslaved = FALSE; - g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER); - } + if ( priv->master + && nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex) <= 0) + nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); /* Take out any entries in the routing table and any IP address the device had. */ ifindex = nm_device_get_ip_ifindex (self); @@ -8190,6 +9533,9 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean nm_platform_address_flush (NM_PLATFORM_GET, ifindex); } + if (priv->lldp_listener) + nm_lldp_listener_stop (priv->lldp_listener); + nm_device_update_metered (self); _cleanup_generic_post (self, cleanup_type); } @@ -8241,13 +9587,14 @@ nm_device_spawn_iface_helper (NMDevice *self) const char *method; GPtrArray *argv; gs_free char *dhcp4_address = NULL; + char *logging_backend; if (priv->state != NM_DEVICE_STATE_ACTIVATED) return; if (!nm_device_can_assume_connections (self)) return; - connection = nm_device_get_connection (self); + connection = nm_device_get_applied_connection (self); g_assert (connection); argv = g_ptr_array_sized_new (10); @@ -8259,6 +9606,17 @@ nm_device_spawn_iface_helper (NMDevice *self) g_ptr_array_add (argv, g_strdup ("--uuid")); g_ptr_array_add (argv, g_strdup (nm_connection_get_uuid (connection))); + logging_backend = nm_config_get_is_debug (nm_config_get ()) + ? g_strdup ("debug") + : nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG, + NM_CONFIG_KEYFILE_GROUP_LOGGING, + NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND, + NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); + if (logging_backend) { + g_ptr_array_add (argv, g_strdup ("--logging-backend")); + g_ptr_array_add (argv, logging_backend); + } + dhcp4_address = find_dhcp4_address (self); method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); @@ -8278,7 +9636,7 @@ nm_device_spawn_iface_helper (NMDevice *self) g_ptr_array_add (argv, g_strdup ("--dhcp4-required")); if (priv->dhcp4_client) { - const char *hostname; + const char *hostname, *fqdn; GBytes *client_id; client_id = nm_dhcp_client_get_client_id (priv->dhcp4_client); @@ -8294,6 +9652,12 @@ nm_device_spawn_iface_helper (NMDevice *self) g_ptr_array_add (argv, g_strdup ("--dhcp4-hostname")); g_ptr_array_add (argv, g_strdup (hostname)); } + + fqdn = nm_dhcp_client_get_fqdn (priv->dhcp4_client); + if (fqdn) { + g_ptr_array_add (argv, g_strdup ("--dhcp4-fqdn")); + g_ptr_array_add (argv, g_strdup (fqdn)); + } } configured = TRUE; @@ -8325,6 +9689,9 @@ nm_device_spawn_iface_helper (NMDevice *self) g_ptr_array_add (argv, hex_iid); } + g_ptr_array_add (argv, g_strdup ("--addr-gen-mode")); + g_ptr_array_add (argv, g_strdup_printf ("%d", nm_setting_ip6_config_get_addr_gen_mode (NM_SETTING_IP6_CONFIG (s_ip6)))); + configured = TRUE; } @@ -8398,15 +9765,14 @@ deactivate_async_ready (NMDevice *self, /* If operation cancelled, just return */ if ( g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) || (priv->deactivating_cancellable && g_cancellable_is_cancelled (priv->deactivating_cancellable))) { - nm_log_warn (LOGD_DEVICE, "Deactivation (%s) cancelled", - nm_device_get_iface (self)); + _LOGW (LOGD_DEVICE, "Deactivation cancelled"); } /* In every other case, transition to the DISCONNECTED state */ else { - if (error) - nm_log_warn (LOGD_DEVICE, "Deactivation (%s) failed: %s", - nm_device_get_iface (self), - error->message); + if (error) { + _LOGW (LOGD_DEVICE, "Deactivation failed: %s", + error->message); + } nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason); } @@ -8453,17 +9819,21 @@ _set_state_full (NMDevice *self, NMDeviceStateReason reason, gboolean quitting) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; NMDeviceState old_state; NMActRequest *req; gboolean no_firmware = FALSE; - NMConnection *connection; + NMSettingsConnection *connection; + NMConnection *applied_connection; + + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); /* Track re-entry */ g_warn_if_fail (priv->in_state_changed == FALSE); - priv->in_state_changed = TRUE; - g_return_if_fail (NM_IS_DEVICE (self)); + old_state = priv->state; /* 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 @@ -8471,14 +9841,16 @@ _set_state_full (NMDevice *self, */ if ( (priv->state == state) && !(state == NM_DEVICE_STATE_UNAVAILABLE && priv->firmware_missing)) { - priv->in_state_changed = FALSE; + _LOGD (LOGD_DEVICE, "device state change: %s -> %s (reason '%s') [%d %d %d] (skip due to missing firmware)", + state_to_string (old_state), + state_to_string (state), + reason_to_string (reason), + old_state, + state, + reason); return; } - old_state = priv->state; - priv->state = state; - priv->state_reason = reason; - _LOGI (LOGD_DEVICE, "device state change: %s -> %s (reason '%s') [%d %d %d]", state_to_string (old_state), state_to_string (state), @@ -8487,6 +9859,11 @@ _set_state_full (NMDevice *self, state, reason); + priv->in_state_changed = TRUE; + + priv->state = state; + priv->state_reason = reason; + /* Clear any queued transitions */ nm_device_queued_state_clear (self); @@ -8573,7 +9950,8 @@ _set_state_full (NMDevice *self, /* Clean up any half-done IP operations if the device's layer2 * finds out it needs authentication during IP config. */ - _cleanup_ip_pre (self, CLEANUP_TYPE_DECONFIGURE); + _cleanup_ip4_pre (self, CLEANUP_TYPE_DECONFIGURE); + _cleanup_ip6_pre (self, CLEANUP_TYPE_DECONFIGURE); } break; default: @@ -8587,7 +9965,7 @@ _set_state_full (NMDevice *self, g_object_notify (G_OBJECT (self), NM_DEVICE_STATE); g_object_notify (G_OBJECT (self), NM_DEVICE_STATE_REASON); - g_signal_emit_by_name (self, "state-changed", state, old_state, reason); + g_signal_emit_by_name (self, NM_DEVICE_STATE_CHANGED, state, old_state, reason); /* Post-process the event after internal notification */ @@ -8622,13 +10000,15 @@ _set_state_full (NMDevice *self, if (quitting) { nm_dispatcher_call_sync (DISPATCHER_ACTION_PRE_DOWN, - nm_act_request_get_connection (req), + nm_act_request_get_settings_connection (req), + nm_act_request_get_applied_connection (req), self); } else { priv->dispatcher.post_state = NM_DEVICE_STATE_DISCONNECTED; priv->dispatcher.post_state_reason = reason; if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_DOWN, - nm_act_request_get_connection (req), + nm_act_request_get_settings_connection (req), + nm_act_request_get_applied_connection (req), self, deactivate_dispatcher_complete, self, @@ -8659,7 +10039,10 @@ _set_state_full (NMDevice *self, case NM_DEVICE_STATE_ACTIVATED: _LOGI (LOGD_DEVICE, "Activation: successful, device activated."); nm_device_update_metered (self); - nm_dispatcher_call (DISPATCHER_ACTION_UP, nm_act_request_get_connection (req), self, NULL, NULL, NULL); + nm_dispatcher_call (DISPATCHER_ACTION_UP, + nm_act_request_get_settings_connection (req), + nm_act_request_get_applied_connection (req), + self, NULL, NULL, NULL); break; case NM_DEVICE_STATE_FAILED: /* Usually upon failure the activation chain is interrupted in @@ -8678,10 +10061,10 @@ _set_state_full (NMDevice *self, break; } - connection = nm_device_get_connection (self); + connection = nm_device_get_settings_connection (self); _LOGW (LOGD_DEVICE | LOGD_WIFI, "Activation: failed for connection '%s'", - connection ? nm_connection_get_id (connection) : "<unknown>"); + connection ? nm_settings_connection_get_id (connection) : "<unknown>"); /* Notify any slaves of the unexpected failure */ nm_device_master_release_slaves (self); @@ -8691,11 +10074,8 @@ _set_state_full (NMDevice *self, * 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); - } + if (connection && !nm_settings_connection_get_timestamp (connection, NULL)) + nm_settings_connection_update_timestamp (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 @@ -8704,7 +10084,28 @@ _set_state_full (NMDevice *self, nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE); break; case NM_DEVICE_STATE_IP_CHECK: - nm_device_start_ip_check (self); + /* Now that IP config has completed, check if the firewall + * zone must be set again for the IP interface. + */ + applied_connection = nm_device_get_applied_connection (self); + + if ( applied_connection + && priv->ifindex != priv->ip_ifindex + && !nm_device_uses_assumed_connection (self)) { + NMSettingConnection *s_con; + const char *zone; + + s_con = nm_connection_get_setting_connection (applied_connection); + zone = nm_setting_connection_get_zone (s_con); + g_assert (!priv->fw_call); + priv->fw_call = nm_firewall_manager_add_or_change_zone (nm_firewall_manager_get (), + nm_device_get_ip_iface (self), + zone, + FALSE, + fw_change_zone_cb_ip_check, + self); + } else + nm_device_start_ip_check (self); /* IP-related properties are only valid when the device has IP configuration; * now that it does, ensure their change notifications are emitted. @@ -8724,10 +10125,17 @@ _set_state_full (NMDevice *self, if ( (old_state == NM_DEVICE_STATE_ACTIVATED || old_state == NM_DEVICE_STATE_DEACTIVATING) && (state != NM_DEVICE_STATE_DEACTIVATING)) { - if (quitting) - nm_dispatcher_call_sync (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), self); - else - nm_dispatcher_call (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), self, NULL, NULL, NULL); + if (quitting) { + nm_dispatcher_call_sync (DISPATCHER_ACTION_DOWN, + nm_act_request_get_settings_connection (req), + nm_act_request_get_applied_connection (req), + self); + } else { + nm_dispatcher_call (DISPATCHER_ACTION_DOWN, + nm_act_request_get_settings_connection (req), + nm_act_request_get_applied_connection (req), + self, NULL, NULL, NULL); + } } /* IP-related properties are only valid when the device has IP configuration. @@ -8836,7 +10244,7 @@ nm_device_queued_state_clear (NMDevice *self) if (priv->queued_state.id) { _LOGD (LOGD_DEVICE, "clearing queued state transition (id %d)", priv->queued_state.id); - g_source_remove (priv->queued_state.id); + nm_clear_g_source (&priv->queued_state.id); nm_device_remove_pending_action (self, queued_state_to_string (priv->queued_state.state), TRUE); } memset (&priv->queued_state, 0, sizeof (priv->queued_state)); @@ -8864,20 +10272,26 @@ nm_device_get_hw_address (NMDevice *self) return priv->hw_addr_len ? priv->hw_addr : NULL; } -static void +void nm_device_update_hw_address (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int ifindex = nm_device_get_ifindex (self); const guint8 *hwaddr; gsize hwaddrlen = 0; + static const guint8 zero_hwaddr[ETH_ALEN]; if (ifindex <= 0) return; hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &hwaddrlen); + if ( priv->type == NM_DEVICE_TYPE_ETHERNET + && nm_utils_hwaddr_matches (hwaddr, hwaddrlen, zero_hwaddr, sizeof (zero_hwaddr))) + hwaddrlen = 0; + if (hwaddrlen) { + priv->hw_addr_len = hwaddrlen; if (!priv->hw_addr || !nm_utils_hwaddr_matches (priv->hw_addr, -1, hwaddr, hwaddrlen)) { g_free (priv->hw_addr); priv->hw_addr = nm_utils_hwaddr_ntoa (hwaddr, hwaddrlen); @@ -8889,12 +10303,39 @@ nm_device_update_hw_address (NMDevice *self) /* Invalid or no hardware address */ if (priv->hw_addr_len != 0) { g_clear_pointer (&priv->hw_addr, g_free); + priv->hw_addr_len = 0; _LOGD (LOGD_HW | LOGD_DEVICE, "previous hardware address is no longer valid"); g_object_notify (G_OBJECT (self), NM_DEVICE_HW_ADDRESS); } } - priv->hw_addr_len = hwaddrlen; +} + +void +nm_device_update_initial_hw_address (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->hw_addr_len) { + priv->initial_hw_addr = g_strdup (priv->hw_addr); + _LOGD (LOGD_DEVICE | LOGD_HW, "read initial MAC address %s", priv->initial_hw_addr); + + if (priv->ifindex > 0) { + guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; + size_t len = 0; + + if (nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len)) { + g_warn_if_fail (len == priv->hw_addr_len); + priv->perm_hw_addr = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len); + _LOGD (LOGD_DEVICE | LOGD_HW, "read permanent MAC address %s", + priv->perm_hw_addr); + } else { + /* Fall back to current address */ + _LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address"); + priv->perm_hw_addr = g_strdup (priv->hw_addr); + } + } + } } gboolean @@ -9004,7 +10445,7 @@ spec_match_list (NMDevice *self, const GSList *specs) break; } } - if (priv->hw_addr_len) { + if (priv->hw_addr_len && priv->hw_addr) { m = nm_match_spec_hwaddr (specs, priv->hw_addr); matched = MAX (matched, m); } @@ -9021,12 +10462,34 @@ spec_match_list (NMDevice *self, const GSList *specs) /***********************************************************/ -#define DEFAULT_AUTOCONNECT TRUE +static const char * +_activation_func_to_string (ActivationHandleFunc func) +{ +#define FUNC_TO_STRING_CHECK_AND_RETURN(func, f) \ + G_STMT_START { \ + if ((func) == (f)) \ + return #f; \ + } G_STMT_END + FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage1_device_prepare); + FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage2_device_config); + FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage3_ip_config_start); + FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage4_ip4_config_timeout); + FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage4_ip6_config_timeout); + FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage5_ip4_config_commit); + FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage5_ip6_config_commit); + g_return_val_if_reached ("unknown"); +} + +/***********************************************************/ static void nm_device_init (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_DEVICE, NMDevicePrivate); + + self->priv = priv; priv->type = NM_DEVICE_TYPE_UNKNOWN; priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED; @@ -9052,133 +10515,50 @@ constructor (GType type, GObjectConstructParam *construct_params) { GObject *object; + GObjectClass *klass; NMDevice *self; NMDevicePrivate *priv; - NMPlatform *platform; - static guint32 id = 0; + const NMPlatformLink *pllink; - object = G_OBJECT_CLASS (nm_device_parent_class)->constructor (type, - n_construct_params, - construct_params); + klass = G_OBJECT_CLASS (nm_device_parent_class); + object = klass->constructor (type, n_construct_params, construct_params); if (!object) return NULL; self = NM_DEVICE (object); priv = NM_DEVICE_GET_PRIVATE (self); - _LOGD (LOGD_DEVICE, "constructor(): %s, kernel ifindex %d", G_OBJECT_TYPE_NAME (self), priv->ifindex); + if (priv->iface) { + pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, priv->iface); - if (!priv->iface) { - _LOGE (LOGD_DEVICE, "No device interface provided, ignoring"); - goto error; - } - - if (!priv->udi) { - /* Use a placeholder UDI until we get a real one */ - priv->udi = g_strdup_printf ("/virtual/device/placeholder/%d", id++); + if (pllink && link_type_compatible (self, pllink->type, NULL, NULL)) { + priv->ifindex = pllink->ifindex; + priv->up = NM_FLAGS_HAS (pllink->flags, IFF_UP); + } } - if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities) - priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self); + return object; +} - if (priv->ifindex > 0) { - priv->physical_port_id = nm_platform_link_get_physical_port_id (NM_PLATFORM_GET, priv->ifindex); - priv->dev_id = nm_platform_link_get_dev_id (NM_PLATFORM_GET, priv->ifindex); - if (nm_platform_link_is_software (NM_PLATFORM_GET, priv->ifindex)) - priv->capabilities |= NM_DEVICE_CAP_IS_SOFTWARE; - priv->mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET, priv->ifindex); +static void +constructed (GObject *object) +{ + NMDevice *self = NM_DEVICE (object); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMPlatform *platform; - nm_platform_link_get_driver_info (NM_PLATFORM_GET, - priv->ifindex, - NULL, - &priv->driver_version, - &priv->firmware_version); - } + platform = nm_platform_get (); if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities) priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self); /* Watch for external IP config changes */ - platform = nm_platform_get (); g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ipx_changed), self); g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ipx_changed), self); g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self); g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self); g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self); - /* trigger initial ip config change to initialize ip-config */ - priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); - priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); - - if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET)) { - int ip_ifindex = nm_device_get_ip_ifindex (self); - - if (ip_ifindex > 0) - priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, ip_ifindex); - } - - return object; - -error: - g_object_unref (self); - return NULL; -} - -static void -constructed (GObject *object) -{ - NMDevice *self = NM_DEVICE (object); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int master; - - nm_device_update_hw_address (self); - - if (priv->hw_addr_len) { - priv->initial_hw_addr = g_strdup (priv->hw_addr); - _LOGD (LOGD_DEVICE | LOGD_HW, "read initial MAC address %s", priv->initial_hw_addr); - - if (priv->ifindex > 0) { - guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; - size_t len = 0; - - if (nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len)) { - g_warn_if_fail (len == priv->hw_addr_len); - priv->perm_hw_addr = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len); - _LOGD (LOGD_DEVICE | LOGD_HW, "read permanent MAC address %s", - priv->perm_hw_addr); - } else { - /* Fall back to current address */ - _LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address"); - priv->perm_hw_addr = g_strdup (priv->hw_addr); - } - } - } - - /* Note: initial hardware address must be read before calling get_ignore_carrier() */ - if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { - NMConfig *config = nm_config_get (); - - priv->ignore_carrier = nm_config_data_get_ignore_carrier (nm_config_get_data (config), self); - g_signal_connect (G_OBJECT (config), - NM_CONFIG_SIGNAL_CONFIG_CHANGED, - G_CALLBACK (config_changed_update_ignore_carrier), - self); - - check_carrier (self); - _LOGD (LOGD_HW, - "carrier is %s%s", - priv->carrier ? "ON" : "OFF", - priv->ignore_carrier ? " (but ignored)" : ""); - } else { - /* Fake online link when carrier detection is not available. */ - priv->carrier = TRUE; - } - - /* Enslave ourselves */ - master = nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex); - if (master) - device_set_master (self, master); - priv->con_provider = nm_connection_provider_get (); g_assert (priv->con_provider); g_signal_connect (priv->con_provider, @@ -9206,6 +10586,8 @@ constructed (GObject *object) } G_OBJECT_CLASS (nm_device_parent_class)->constructed (object); + + _LOGD (LOGD_DEVICE, "constructed (%s)", G_OBJECT_TYPE_NAME (self)); } static void @@ -9215,7 +10597,7 @@ dispose (GObject *object) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMPlatform *platform; - _LOGD (LOGD_DEVICE, "dispose(): %s", G_OBJECT_TYPE_NAME (self)); + _LOGD (LOGD_DEVICE, "disposing"); g_signal_handlers_disconnect_by_func (nm_config_get (), config_changed_update_ignore_carrier, self); @@ -9233,15 +10615,10 @@ dispose (GObject *object) g_hash_table_remove_all (priv->ip6_saved_properties); - if (priv->recheck_assume_id) { - g_source_remove (priv->recheck_assume_id); - priv->recheck_assume_id = 0; - } + nm_clear_g_source (&priv->recheck_assume_id); + nm_clear_g_source (&priv->recheck_available.call_id); - if (priv->recheck_available.call_id) { - g_source_remove (priv->recheck_available.call_id); - priv->recheck_available.call_id = 0; - } + nm_clear_g_source (&priv->check_delete_unrealized_id); link_disconnect_action_cancel (self); @@ -9265,7 +10642,22 @@ dispose (GObject *object) nm_clear_g_source (&priv->device_link_changed_id); nm_clear_g_source (&priv->device_ip_link_changed_id); + if (priv->lldp_listener) { + g_signal_handlers_disconnect_by_func (priv->lldp_listener, + G_CALLBACK (lldp_neighbors_changed), + self); + nm_lldp_listener_stop (priv->lldp_listener); + g_clear_object (&priv->lldp_listener); + } + G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); + + if (nm_clear_g_source (&priv->queued_state.id)) { + /* FIXME: we'd expect the queud_state to be alredy cleared and this statement + * not being necessary. Add this check here to hopefully investigate crash + * rh#1270247. */ + g_return_if_reached (); + } } static void @@ -9280,9 +10672,9 @@ finalize (GObject *object) g_free (priv->perm_hw_addr); g_free (priv->initial_hw_addr); g_slist_free_full (priv->pending_actions, g_free); + g_slist_free_full (priv->dad6_failed_addrs, g_free); g_clear_pointer (&priv->physical_port_id, g_free); g_free (priv->udi); - g_free (priv->path); g_free (priv->iface); g_free (priv->ip_iface); g_free (priv->driver); @@ -9304,25 +10696,11 @@ set_property (GObject *object, guint prop_id, { NMDevice *self = NM_DEVICE (object); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMPlatformLink *platform_device; const char *hw_addr, *p; guint count; + gboolean val_bool; switch (prop_id) { - case PROP_PLATFORM_DEVICE: - platform_device = g_value_get_pointer (value); - if (platform_device) { - g_free (priv->udi); - priv->udi = g_strdup (nm_platform_link_get_udi (NM_PLATFORM_GET, platform_device->ifindex)); - g_free (priv->iface); - priv->iface = g_strdup (platform_device->name); - priv->ifindex = platform_device->ifindex; - priv->up = NM_FLAGS_HAS (platform_device->flags, IFF_UP); - g_free (priv->driver); - priv->driver = g_strdup (platform_device->driver); - priv->platform_link_initialized = platform_device->initialized; - } - break; case PROP_UDI: if (g_value_get_string (value)) { g_free (priv->udi); @@ -9330,13 +10708,9 @@ set_property (GObject *object, guint prop_id, } break; case PROP_IFACE: - if (g_value_get_string (value)) { - g_free (priv->iface); - priv->iface = g_value_dup_string (value); - priv->ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->iface); - if (priv->ifindex > 0) - priv->up = nm_platform_link_is_up (NM_PLATFORM_GET, priv->ifindex); - } + /* construct only */ + g_return_if_fail (!priv->iface); + priv->iface = g_value_dup_string (value); break; case PROP_DRIVER: if (g_value_get_string (value)) { @@ -9358,16 +10732,32 @@ set_property (GObject *object, guint prop_id, case PROP_IP4_ADDRESS: priv->ip4_address = g_value_get_uint (value); break; + case PROP_MANAGED: + val_bool = g_value_get_boolean (value); + priv->managed_touched_by_user = TRUE; + nm_device_set_unmanaged_flags (self, + NM_UNMANAGED_USER | (val_bool ? NM_UNMANAGED_DEFAULT : NM_UNMANAGED_NONE), + !val_bool, + NM_DEVICE_STATE_REASON_USER_REQUESTED); + break; case PROP_AUTOCONNECT: nm_device_set_autoconnect (self, g_value_get_boolean (value)); break; case PROP_FIRMWARE_MISSING: priv->firmware_missing = g_value_get_boolean (value); break; + case PROP_NM_PLUGIN_MISSING: + priv->nm_plugin_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_LINK_TYPE: + /* construct only */ + g_return_if_fail (priv->link_type == NM_LINK_TYPE_NONE); + priv->link_type = g_value_get_uint (value); + break; case PROP_TYPE_DESC: g_free (priv->type_desc); priv->type_desc = g_value_dup_string (value); @@ -9411,18 +10801,16 @@ set_property (GObject *object, guint prop_id, } } -#define DBUS_TYPE_STATE_REASON_STRUCT (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID)) - static void get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) + GValue *value, GParamSpec *pspec) { NMDevice *self = NM_DEVICE (object); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *ac_path = NULL; GPtrArray *array; GHashTableIter iter; NMConnection *connection; + GVariantBuilder array_builder; switch (prop_id) { case PROP_UDI: @@ -9462,44 +10850,33 @@ get_property (GObject *object, guint prop_id, g_value_set_uint (value, priv->mtu); break; case PROP_IP4_CONFIG: - if (ip_config_valid (priv->state) && priv->ip4_config) - g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config)); - else - g_value_set_boxed (value, "/"); + nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip4_config : NULL); break; case PROP_DHCP4_CONFIG: - if (ip_config_valid (priv->state) && priv->dhcp4_config) - g_value_set_boxed (value, nm_dhcp4_config_get_dbus_path (priv->dhcp4_config)); - else - g_value_set_boxed (value, "/"); + nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp4_config : NULL); break; case PROP_IP6_CONFIG: - if (ip_config_valid (priv->state) && priv->ip6_config) - g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config)); - else - g_value_set_boxed (value, "/"); + nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip6_config : NULL); break; case PROP_DHCP6_CONFIG: - if (ip_config_valid (priv->state) && priv->dhcp6_config) - g_value_set_boxed (value, nm_dhcp6_config_get_dbus_path (priv->dhcp6_config)); - else - g_value_set_boxed (value, "/"); + nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp6_config : NULL); 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_TYPE_STATE_REASON_STRUCT)); - dbus_g_type_struct_set (value, 0, priv->state, 1, priv->state_reason, G_MAXUINT); + g_value_take_variant (value, + g_variant_new ("(uu)", priv->state, priv->state_reason)); 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 : "/"); + nm_utils_g_value_set_object_path (value, priv->act_request); break; case PROP_DEVICE_TYPE: g_value_set_uint (value, priv->type); break; + case PROP_LINK_TYPE: + g_value_set_uint (value, priv->link_type); + break; case PROP_MANAGED: g_value_set_boolean (value, nm_device_get_managed (self)); break; @@ -9509,6 +10886,9 @@ get_property (GObject *object, guint prop_id, case PROP_FIRMWARE_MISSING: g_value_set_boolean (value, priv->firmware_missing); break; + case PROP_NM_PLUGIN_MISSING: + g_value_set_boolean (value, priv->nm_plugin_missing); + break; case PROP_TYPE_DESC: g_value_set_string (value, priv->type_desc); break; @@ -9520,7 +10900,8 @@ get_property (GObject *object, guint prop_id, 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); + g_ptr_array_add (array, NULL); + g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE)); break; case PROP_PHYSICAL_PORT_ID: g_value_set_string (value, priv->physical_port_id); @@ -9529,7 +10910,7 @@ get_property (GObject *object, guint prop_id, g_value_set_boolean (value, priv->is_master); break; case PROP_MASTER: - g_value_set_object (value, priv->master); + g_value_set_object (value, nm_device_get_master (self)); break; case PROP_HW_ADDRESS: g_value_set_string (value, priv->hw_addr); @@ -9540,6 +10921,37 @@ get_property (GObject *object, guint prop_id, case PROP_METERED: g_value_set_uint (value, priv->metered); break; + case PROP_LLDP_NEIGHBORS: + if (priv->lldp_listener) + g_value_set_variant (value, nm_lldp_listener_get_neighbors (priv->lldp_listener)); + else { + g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}")); + g_value_take_variant (value, g_variant_builder_end (&array_builder)); + } + break; + case PROP_REAL: + g_value_set_boolean (value, nm_device_is_real (self)); + break; + case PROP_SLAVES: { + GSList *slave_iter; + char **slave_list; + guint i; + + slave_list = g_new (char *, g_slist_length (priv->slaves) + 1); + for (slave_iter = priv->slaves, i = 0; slave_iter; slave_iter = slave_iter->next) { + SlaveInfo *info = slave_iter->data; + const char *path; + + if (!NM_DEVICE_GET_PRIVATE (info->slave)->is_enslaved) + continue; + path = nm_exported_object_get_path ((NMExportedObject *) info->slave); + if (path) + slave_list[i++] = g_strdup (path); + } + slave_list[i] = NULL; + g_value_take_boxed (value, slave_list); + break; + } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -9550,9 +10962,12 @@ static void nm_device_class_init (NMDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (NMDevicePrivate)); + exported_object_class->export_path = NM_DBUS_PATH "/Devices/%u"; + /* Virtual methods */ object_class->dispose = dispose; object_class->finalize = finalize; @@ -9578,6 +10993,8 @@ nm_device_class_init (NMDeviceClass *klass) klass->check_connection_compatible = check_connection_compatible; klass->check_connection_available = check_connection_available; klass->can_unmanaged_external_down = can_unmanaged_external_down; + klass->realize_start_notify = realize_start_notify; + klass->unrealize_notify = unrealize_notify; klass->is_up = is_up; klass->bring_up = bring_up; klass->take_down = take_down; @@ -9586,12 +11003,6 @@ nm_device_class_init (NMDeviceClass *klass) /* Properties */ g_object_class_install_property - (object_class, PROP_PLATFORM_DEVICE, - g_param_spec_pointer (NM_DEVICE_PLATFORM_DEVICE, "", "", - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (object_class, PROP_UDI, g_param_spec_string (NM_DEVICE_UDI, "", "", NULL, @@ -9663,31 +11074,31 @@ nm_device_class_init (NMDeviceClass *klass) g_object_class_install_property (object_class, PROP_IP4_CONFIG, - g_param_spec_boxed (NM_DEVICE_IP4_CONFIG, "", "", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + g_param_spec_string (NM_DEVICE_IP4_CONFIG, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_DHCP4_CONFIG, - g_param_spec_boxed (NM_DEVICE_DHCP4_CONFIG, "", "", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + g_param_spec_string (NM_DEVICE_DHCP4_CONFIG, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_IP6_CONFIG, - g_param_spec_boxed (NM_DEVICE_IP6_CONFIG, "", "", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + g_param_spec_string (NM_DEVICE_IP6_CONFIG, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_DHCP6_CONFIG, - g_param_spec_boxed (NM_DEVICE_DHCP6_CONFIG, "", "", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); + g_param_spec_string (NM_DEVICE_DHCP6_CONFIG, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_STATE, @@ -9698,17 +11109,18 @@ nm_device_class_init (NMDeviceClass *klass) g_object_class_install_property (object_class, PROP_STATE_REASON, - g_param_spec_boxed (NM_DEVICE_STATE_REASON, "", "", - DBUS_TYPE_STATE_REASON_STRUCT, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + g_param_spec_variant (NM_DEVICE_STATE_REASON, "", "", + G_VARIANT_TYPE ("(uu)"), + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_ACTIVE_CONNECTION, - g_param_spec_boxed (NM_DEVICE_ACTIVE_CONNECTION, "", "", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); + g_param_spec_string (NM_DEVICE_ACTIVE_CONNECTION, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_DEVICE_TYPE, @@ -9718,10 +11130,17 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_STATIC_STRINGS)); g_object_class_install_property + (object_class, PROP_LINK_TYPE, + g_param_spec_uint (NM_DEVICE_LINK_TYPE, "", "", + 0, G_MAXUINT32, NM_LINK_TYPE_NONE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_MANAGED, g_param_spec_boolean (NM_DEVICE_MANAGED, "", "", FALSE, - G_PARAM_READABLE | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property @@ -9739,6 +11158,13 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_STATIC_STRINGS)); g_object_class_install_property + (object_class, PROP_NM_PLUGIN_MISSING, + g_param_spec_boolean (NM_DEVICE_NM_PLUGIN_MISSING, "", "", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_class, PROP_TYPE_DESC, g_param_spec_string (NM_DEVICE_TYPE_DESC, "", "", NULL, @@ -9764,7 +11190,7 @@ nm_device_class_init (NMDeviceClass *klass) g_object_class_install_property (object_class, PROP_AVAILABLE_CONNECTIONS, g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS, "", "", - DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, + G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); @@ -9808,7 +11234,7 @@ nm_device_class_init (NMDeviceClass *klass) * * Whether the connection is metered. * - * Since: 1.0.6 + * Since: 1.2 **/ g_object_class_install_property (object_class, PROP_METERED, @@ -9817,9 +11243,31 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property + (object_class, PROP_LLDP_NEIGHBORS, + g_param_spec_variant (NM_DEVICE_LLDP_NEIGHBORS, "", "", + G_VARIANT_TYPE ("aa{sv}"), + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, PROP_REAL, + g_param_spec_boolean (NM_DEVICE_REAL, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, PROP_SLAVES, + g_param_spec_boxed (NM_DEVICE_SLAVES, "", "", + G_TYPE_STRV, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* Signals */ signals[STATE_CHANGED] = - g_signal_new ("state-changed", + g_signal_new (NM_DEVICE_STATE_CHANGED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NMDeviceClass, state_changed), @@ -9840,8 +11288,8 @@ nm_device_class_init (NMDeviceClass *klass) G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, - /* dbus-glib context, connection, permission, allow_interaction, callback, user_data */ - G_TYPE_NONE, 6, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER, G_TYPE_POINTER); + /* context, connection, permission, allow_interaction, callback, user_data */ + G_TYPE_NONE, 6, G_TYPE_DBUS_METHOD_INVOCATION, NM_TYPE_CONNECTION, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER, G_TYPE_POINTER); signals[IP4_CONFIG_CHANGED] = g_signal_new (NM_DEVICE_IP4_CONFIG_CHANGED, @@ -9878,17 +11326,10 @@ nm_device_class_init (NMDeviceClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 0); - signals[LINK_INITIALIZED] = - g_signal_new (NM_DEVICE_LINK_INITIALIZED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); - - nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), - G_TYPE_FROM_CLASS (klass), - &dbus_glib_nm_device_object_info); - - dbus_g_error_domain_register (NM_DEVICE_ERROR, NULL, NM_TYPE_DEVICE_ERROR); + nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), + NMDBUS_TYPE_DEVICE_SKELETON, + "Reapply", impl_device_reapply, + "Disconnect", impl_device_disconnect, + "Delete", impl_device_delete, + NULL); } - |