diff options
| author | Michael Biebl <biebl@debian.org> | 2015-01-22 00:29:39 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2015-01-22 00:29:39 +0100 |
| commit | 2c032d8f1c6292c1338a615e6ec40252889ba85c (patch) | |
| tree | 1f77182220b2b0264288ba4a476ab47e5bc48716 /src/devices/nm-device.c | |
| parent | 33491bc4279481db8ae47213e34a6d695a0e8830 (diff) | |
Imported Upstream version 1.0.0 upstream/1.0.0
Diffstat (limited to 'src/devices/nm-device.c')
| -rw-r--r-- | src/devices/nm-device.c | 4392 |
1 files changed, 2608 insertions, 1784 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 4788a604..a9899535 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -19,7 +19,8 @@ * Copyright (C) 2006 - 2008 Novell, Inc. */ -#include <config.h> +#include "config.h" + #include <glib.h> #include <glib/gi18n.h> #include <dbus/dbus.h> @@ -35,10 +36,9 @@ #include <sys/wait.h> #include <arpa/inet.h> #include <fcntl.h> -#include <linux/if.h> #include <netlink/route/addr.h> -#include "libgsystem.h" +#include "gsystem-local-alloc.h" #include "nm-glib-compat.h" #include "nm-device.h" #include "nm-device-private.h" @@ -49,13 +49,13 @@ #include "nm-lndp-rdisc.h" #include "nm-dhcp-manager.h" #include "nm-dbus-manager.h" -#include "nm-utils.h" #include "nm-logging.h" -#include "nm-setting-ip4-config.h" -#include "nm-setting-ip6-config.h" -#include "nm-setting-connection.h" +#include "nm-activation-request.h" +#include "nm-ip4-config.h" +#include "nm-ip6-config.h" #include "nm-dnsmasq-manager.h" #include "nm-dhcp4-config.h" +#include "nm-dhcp6-config.h" #include "nm-rfkill-manager.h" #include "nm-firewall-manager.h" #include "nm-properties-changed-signal.h" @@ -63,25 +63,23 @@ #include "nm-settings-connection.h" #include "nm-connection-provider.h" #include "nm-posix-signals.h" -#include "nm-manager-auth.h" +#include "nm-auth-utils.h" #include "nm-dbus-glib-types.h" #include "nm-dispatcher.h" -#include "nm-config-device.h" #include "nm-config.h" #include "nm-dns-manager.h" +#include "nm-core-internal.h" +#include "nm-default-route-manager.h" -#include "nm-device-bridge.h" -#include "nm-device-bond.h" -#include "nm-device-team.h" +#include "nm-device-logging.h" +_LOG_DECLARE_SELF (NMDevice); -static void impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context); +static void impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context); +static void impl_device_delete (NMDevice *self, DBusGMethodInvocation *context); #include "nm-device-glue.h" -static void nm_device_config_device_interface_init (NMConfigDeviceInterface *iface); - -G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMDevice, nm_device, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (NM_TYPE_CONFIG_DEVICE, nm_device_config_device_interface_init)) +G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, G_TYPE_OBJECT) #define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate)) @@ -176,6 +174,7 @@ typedef struct { typedef struct { gboolean in_state_changed; + gboolean initialized; NMDeviceState state; NMDeviceStateReason state_reason; @@ -199,7 +198,7 @@ typedef struct { RfKillType rfkill_type; gboolean firmware_missing; GHashTable * available_connections; - guint8 hw_addr[NM_UTILS_HWADDR_LEN_MAX]; + char * hw_addr; guint hw_addr_len; char * physical_port_id; @@ -230,22 +229,32 @@ typedef struct { guint carrier_wait_id; gboolean ignore_carrier; guint32 mtu; + gboolean up; /* IFF_UP */ /* Generic DHCP stuff */ guint32 dhcp_timeout; - GByteArray * dhcp_anycast_address; + char * dhcp_anycast_address; /* IP4 configuration info */ NMIP4Config * ip4_config; /* Combined config from VPN, settings, and device */ IpState ip4_state; NMIP4Config * dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */ NMIP4Config * ext_ip4_config; /* Stuff added outside NM */ + gboolean ext_ip4_config_had_any_addresses; + NMIP4Config * wwan_ip4_config; /* WWAN configuration */ + struct { + gboolean v4_has; + gboolean v4_is_assumed; + NMPlatformIP4Route v4; + gboolean v6_has; + gboolean v6_is_assumed; + NMPlatformIP6Route v6; + } default_route; /* DHCPv4 tracking */ - NMDHCPClient * dhcp4_client; + NMDhcpClient * dhcp4_client; gulong dhcp4_state_sigid; - gulong dhcp4_timeout_sigid; - NMDHCP4Config * dhcp4_config; + NMDhcp4Config * dhcp4_config; NMIP4Config * vpn4_config; /* routes added by a VPN which uses this device */ guint arp_round2_id; @@ -255,9 +264,8 @@ typedef struct { NMDnsMasqManager *dnsmasq_manager; gulong dnsmasq_state_id; - /* Firewall Manager */ - NMFirewallManager *fw_manager; - DBusGProxyCall *fw_call; + /* Firewall */ + NMFirewallPendingCall fw_call; /* avahi-autoipd stuff */ GPid aipd_pid; @@ -268,10 +276,14 @@ typedef struct { NMIP6Config * ip6_config; IpState ip6_state; NMIP6Config * vpn6_config; /* routes added by a VPN which uses this device */ + NMIP6Config * wwan_ip6_config; NMIP6Config * ext_ip6_config; /* Stuff added outside NM */ + gboolean ext_ip6_config_had_any_addresses; + gboolean nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */ NMRDisc * rdisc; - gulong rdisc_config_changed_sigid; + gulong rdisc_changed_id; + gulong rdisc_timeout_id; NMSettingIP6ConfigPrivacy rdisc_use_tempaddr; /* IP6 config from autoconf */ NMIP6Config * ac_ip6_config; @@ -280,11 +292,10 @@ typedef struct { GHashTable * ip6_saved_properties; - NMDHCPClient * dhcp6_client; + NMDhcpClient * dhcp6_client; NMRDiscDHCPLevel dhcp6_mode; gulong dhcp6_state_sigid; - gulong dhcp6_timeout_sigid; - NMDHCP6Config * dhcp6_config; + NMDhcp6Config * dhcp6_config; /* IP6 config from DHCP */ NMIP6Config * dhcp6_ip6_config; @@ -303,8 +314,9 @@ typedef struct { NMConnectionProvider *con_provider; } NMDevicePrivate; -static gboolean nm_device_set_ip4_config (NMDevice *dev, +static gboolean nm_device_set_ip4_config (NMDevice *self, NMIP4Config *config, + guint32 default_route_metric, gboolean commit, NMDeviceStateReason *reason); static gboolean ip4_config_merge_and_apply (NMDevice *self, @@ -312,36 +324,27 @@ static gboolean ip4_config_merge_and_apply (NMDevice *self, gboolean commit, NMDeviceStateReason *out_reason); -static gboolean nm_device_set_ip6_config (NMDevice *dev, +static gboolean nm_device_set_ip6_config (NMDevice *self, NMIP6Config *config, gboolean commit, NMDeviceStateReason *reason); -static gboolean nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure); -static void nm_device_slave_notify_enslave (NMDevice *dev, gboolean success); -static void nm_device_slave_notify_release (NMDevice *dev, NMDeviceStateReason reason); +static gboolean 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); -static void addrconf6_start_with_link_ready (NMDevice *self); +static gboolean addrconf6_start_with_link_ready (NMDevice *self); +static gboolean dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection); +static NMActStageReturn linklocal6_start (NMDevice *self); -static gboolean nm_device_get_default_unmanaged (NMDevice *device); +static gboolean nm_device_get_default_unmanaged (NMDevice *self); -static void _set_state_full (NMDevice *device, +static void _set_state_full (NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, gboolean quitting); -/***********************************************************/ - -static GQuark -nm_device_error_quark (void) -{ - static GQuark quark = 0; - if (!quark) - quark = g_quark_from_static_string ("nm-device-error"); - return quark; -} - -#define NM_DEVICE_ERROR (nm_device_error_quark ()) +static void nm_device_update_hw_address (NMDevice *self); /***********************************************************/ @@ -366,7 +369,7 @@ static const char *state_table[] = { static const char * queued_state_to_string (NMDeviceState state) { - if (state >= 0 && state < G_N_ELEMENTS (state_table)) + if ((gsize) state < G_N_ELEMENTS (state_table)) return state_table[state]; return state_table[NM_DEVICE_STATE_UNKNOWN]; } @@ -442,41 +445,41 @@ static const char *reason_table[] = { static const char * reason_to_string (NMDeviceStateReason reason) { - if (reason >= 0 && reason < G_N_ELEMENTS (reason_table)) + if ((gsize) reason < G_N_ELEMENTS (reason_table)) return reason_table[reason]; return reason_table[NM_DEVICE_STATE_REASON_UNKNOWN]; } /***********************************************************/ -static inline gboolean +gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value) { return nm_platform_sysctl_set (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property), value); } static gboolean -device_has_capability (NMDevice *device, NMDeviceCapabilities caps) +device_has_capability (NMDevice *self, NMDeviceCapabilities caps) { - return !!(NM_DEVICE_GET_PRIVATE (device)->capabilities & caps); + return !!(NM_DEVICE_GET_PRIVATE (self)->capabilities & caps); } /***********************************************************/ void -nm_device_dbus_export (NMDevice *device) +nm_device_dbus_export (NMDevice *self) { static guint32 devcount = 0; NMDevicePrivate *priv; - g_return_if_fail (NM_IS_DEVICE (device)); + g_return_if_fail (NM_IS_DEVICE (self)); - priv = NM_DEVICE_GET_PRIVATE (device); + priv = NM_DEVICE_GET_PRIVATE (self); g_return_if_fail (priv->path == NULL); priv->path = g_strdup_printf ("/org/freedesktop/NetworkManager/Devices/%d", devcount++); - nm_log_info (LOGD_DEVICE, "(%s): exported as %s", priv->iface, priv->path); - nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, device); + _LOGI (LOGD_DEVICE, "exported as %s", priv->path); + nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, self); } const char * @@ -498,7 +501,7 @@ nm_device_get_udi (NMDevice *self) const char * nm_device_get_iface (NMDevice *self) { - g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (NM_IS_DEVICE (self), 0); return NM_DEVICE_GET_PRIVATE (self)->iface; } @@ -512,9 +515,9 @@ nm_device_get_ifindex (NMDevice *self) } gboolean -nm_device_is_software (NMDevice *device) +nm_device_is_software (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); return priv->is_software; } @@ -562,11 +565,14 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface) if (priv->ip_iface) { priv->ip_ifindex = nm_platform_link_get_ifindex (priv->ip_iface); if (priv->ip_ifindex > 0) { + if (nm_platform_check_support_user_ipv6ll ()) + nm_platform_link_set_user_ipv6ll_enabled (priv->ip_ifindex, TRUE); + if (!nm_platform_link_is_up (priv->ip_ifindex)) nm_platform_link_set_up (priv->ip_ifindex); } else { /* Device IP interface must always be a kernel network interface */ - nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", iface); + _LOGW (LOGD_HW, "failed to look up interface index"); } } @@ -579,6 +585,43 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface) g_free (old_ip_iface); } +static gboolean +get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid) +{ + NMLinkType link_type; + const guint8 *hwaddr = NULL; + size_t hwaddr_len = 0; + int ifindex; + gboolean success; + + /* If we get here, we *must* have a kernel netdev, which implies an ifindex */ + ifindex = nm_device_get_ip_ifindex (self); + g_assert (ifindex); + + link_type = nm_platform_link_get_type (ifindex); + g_return_val_if_fail (link_type > NM_LINK_TYPE_UNKNOWN, 0); + + hwaddr = nm_platform_link_get_address (ifindex, &hwaddr_len); + if (!hwaddr_len) + return FALSE; + + success = nm_utils_get_ipv6_interface_identifier (link_type, + hwaddr, + hwaddr_len, + out_iid); + if (!success) { + _LOGW (LOGD_HW, "failed to generate interface identifier " + "for link type %u hwaddr_len %zu", link_type, hwaddr_len); + } + return success; +} + +static gboolean +nm_device_get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *iid) +{ + return NM_DEVICE_GET_CLASS (self)->get_ip_iface_identifier (self, iid); +} + const char * nm_device_get_driver (NMDevice *self) { @@ -606,54 +649,124 @@ nm_device_get_device_type (NMDevice *self) /** * nm_device_get_priority(): - * @dev: the #NMDevice + * @self: the #NMDevice * * Returns: the device's routing priority. Lower numbers means a "better" * device, eg higher priority. */ int -nm_device_get_priority (NMDevice *dev) +nm_device_get_priority (NMDevice *self) { - g_return_val_if_fail (NM_IS_DEVICE (dev), 100); + g_return_val_if_fail (NM_IS_DEVICE (self), 1000); - /* Device 'priority' is used for two things: + /* Device 'priority' is used for the default route-metric and is based on + * the device type. The settings ipv4.route-metric and ipv6.route-metric + * can overwrite this default. + * + * Currently for both IPv4 and IPv6 we use the same default values. + * + * The route-metric is used for the metric of the routes of device. + * This also applies to the default route. Therefore it affects also + * which device is the "best". * - * a) two devices on the same IP subnet: the "better" (ie, lower number) - * device is the default outgoing device for that subnet - * b) default route: the "better" device gets the default route. This can - * always be modified by setting a connection to never-default=TRUE, in - * which case that device will never take the default route when - * it's using that connection. + * For comparison, note that iproute2 by default adds IPv4 routes with + * metric 0, and IPv6 routes with metric 1024. The latter is the IPv6 + * "user default" in the kernel (NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6). + * In kernel, the full uint32_t range is available for route + * metrics (except for IPv6, where 0 means 1024). */ - switch (nm_device_get_device_type (dev)) { + switch (nm_device_get_device_type (self)) { + /* 50 is reserved for VPN (NM_VPN_ROUTE_METRIC_DEFAULT) */ case NM_DEVICE_TYPE_ETHERNET: - return 1; + return 100; case NM_DEVICE_TYPE_INFINIBAND: - return 2; + return 150; case NM_DEVICE_TYPE_ADSL: - return 3; + return 200; case NM_DEVICE_TYPE_WIMAX: - return 4; + return 250; case NM_DEVICE_TYPE_BOND: - return 5; + return 300; case NM_DEVICE_TYPE_TEAM: - return 6; + return 350; case NM_DEVICE_TYPE_VLAN: - return 7; + return 400; case NM_DEVICE_TYPE_MODEM: - return 8; + return 450; case NM_DEVICE_TYPE_BT: - return 9; + return 550; case NM_DEVICE_TYPE_WIFI: - return 10; + return 600; case NM_DEVICE_TYPE_OLPC_MESH: - return 11; + return 650; default: - return 20; + return 950; } } +guint32 +nm_device_get_ip4_route_metric (NMDevice *self) +{ + NMConnection *connection; + gint64 route_metric = -1; + + g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32); + + connection = nm_device_get_connection (self); + + if (connection) + route_metric = nm_setting_ip_config_get_route_metric (nm_connection_get_setting_ip4_config (connection)); + + return route_metric >= 0 ? route_metric : nm_device_get_priority (self); +} + +guint32 +nm_device_get_ip6_route_metric (NMDevice *self) +{ + NMConnection *connection; + gint64 route_metric = -1; + + g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32); + + connection = nm_device_get_connection (self); + + if (connection) + route_metric = nm_setting_ip_config_get_route_metric (nm_connection_get_setting_ip6_config (connection)); + + return route_metric >= 0 ? route_metric : nm_device_get_priority (self); +} + +const NMPlatformIP4Route * +nm_device_get_ip4_default_route (NMDevice *self, gboolean *out_is_assumed) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (out_is_assumed) + *out_is_assumed = priv->default_route.v4_is_assumed; + + return priv->default_route.v4_has ? &priv->default_route.v4 : NULL; +} + +const NMPlatformIP6Route * +nm_device_get_ip6_default_route (NMDevice *self, gboolean *out_is_assumed) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (out_is_assumed) + *out_is_assumed = priv->default_route.v6_is_assumed; + + return priv->default_route.v6_has ? &priv->default_route.v6 : NULL; +} + const char * nm_device_get_type_desc (NMDevice *self) { @@ -663,9 +776,9 @@ nm_device_get_type_desc (NMDevice *self) } gboolean -nm_device_has_carrier (NMDevice *device) +nm_device_has_carrier (NMDevice *self) { - return NM_DEVICE_GET_PRIVATE (device)->carrier; + return NM_DEVICE_GET_PRIVATE (self)->carrier; } NMActRequest * @@ -693,22 +806,38 @@ nm_device_get_rfkill_type (NMDevice *self) } static const char * -nm_device_get_physical_port_id (NMDevice *device) +nm_device_get_physical_port_id (NMDevice *self) { - return NM_DEVICE_GET_PRIVATE (device)->physical_port_id; + return NM_DEVICE_GET_PRIVATE (self)->physical_port_id; } /***********************************************************/ static gboolean -nm_device_uses_generated_connection (NMDevice *self) +nm_device_uses_generated_assumed_connection (NMDevice *self) { + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; - connection = nm_device_get_connection (self); - if (!connection) - return FALSE; - return nm_settings_connection_get_nm_generated (NM_SETTINGS_CONNECTION (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); + if ( connection + && nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) + return TRUE; + } + return FALSE; +} + +gboolean +nm_device_uses_assumed_connection (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if ( priv->act_request + && nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (priv->act_request))) + return TRUE; + return FALSE; } static SlaveInfo * @@ -737,28 +866,28 @@ free_slave_info (SlaveInfo *info) /** * nm_device_enslave_slave: - * @dev: the master device + * @self: the master device * @slave: the slave device to enslave * @connection: (allow-none): the slave device's connection * - * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team, + * If @self is capable of enslaving other devices (ie it's a bridge, bond, team, * etc) then this function enslaves @slave. * * Returns: %TRUE on success, %FALSE on failure or if this device cannot enslave * other devices. */ static gboolean -nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connection) +nm_device_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connection) { SlaveInfo *info; gboolean success = FALSE; gboolean configure; - g_return_val_if_fail (dev != NULL, FALSE); + 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 (dev)->enslave_slave != NULL, FALSE); + g_return_val_if_fail (NM_DEVICE_GET_CLASS (self)->enslave_slave != NULL, FALSE); - info = find_slave_info (dev, slave); + info = find_slave_info (self, slave); if (!info) return FALSE; @@ -769,7 +898,7 @@ nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connectio if (configure) g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); - success = NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave, connection, configure); + success = NM_DEVICE_GET_CLASS (self)->enslave_slave (self, slave, connection, configure); info->enslaved = success; } @@ -778,18 +907,18 @@ nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connectio /* Ensure the device's hardware address is up-to-date; it often changes * when slaves change. */ - nm_device_update_hw_address (dev); + nm_device_update_hw_address (self); /* Restart IP configuration if we're waiting for slaves. Do this * after updating the hardware address as IP config may need the * new address. */ if (success) { - if (NM_DEVICE_GET_PRIVATE (dev)->ip4_state == IP_WAIT) - nm_device_activate_stage3_ip4_start (dev); + if (NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_WAIT) + nm_device_activate_stage3_ip4_start (self); - if (NM_DEVICE_GET_PRIVATE (dev)->ip6_state == IP_WAIT) - nm_device_activate_stage3_ip6_start (dev); + if (NM_DEVICE_GET_PRIVATE (self)->ip6_state == IP_WAIT) + nm_device_activate_stage3_ip6_start (self); } return success; @@ -797,42 +926,42 @@ nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connectio /** * nm_device_release_one_slave: - * @dev: the master device + * @self: the master device * @slave: the slave device to release - * @configure: whether @dev needs to actually release @slave + * @configure: whether @self needs to actually release @slave * @reason: the state change reason for the @slave * - * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team, + * 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 @dev and @slave to reflect its release. + * 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 *dev, NMDevice *slave, gboolean configure, NMDeviceStateReason reason) +nm_device_release_one_slave (NMDevice *self, NMDevice *slave, gboolean configure, NMDeviceStateReason reason) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); SlaveInfo *info; gboolean success = FALSE; g_return_val_if_fail (slave != NULL, FALSE); - g_return_val_if_fail (NM_DEVICE_GET_CLASS (dev)->release_slave != NULL, FALSE); + g_return_val_if_fail (NM_DEVICE_GET_CLASS (self)->release_slave != NULL, FALSE); - info = find_slave_info (dev, slave); + info = find_slave_info (self, slave); if (!info) return FALSE; priv->slaves = g_slist_remove (priv->slaves, info); if (info->enslaved) { - success = NM_DEVICE_GET_CLASS (dev)->release_slave (dev, slave, configure); + 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. */ } if (!configure) { - g_warn_if_fail (reason == NM_DEVICE_STATE_REASON_NONE); + 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 (); @@ -845,20 +974,53 @@ nm_device_release_one_slave (NMDevice *dev, NMDevice *slave, gboolean configure, /* Ensure the device's hardware address is up-to-date; it often changes * when slaves change. */ - nm_device_update_hw_address (dev); + nm_device_update_hw_address (self); return success; } +static gboolean +is_software_external (NMDevice *self) +{ + return nm_device_is_software (self) + && !nm_device_get_is_nm_owned (self); +} + +/** + * nm_device_finish_init: + * @self: the master device + * + * Whatever needs to be done post-initialization, when the device has a DBus + * object name. + */ +void +nm_device_finish_init (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + g_assert (priv->initialized == FALSE); + + /* Do not manage externally created software devices until they are IFF_UP */ + if ( is_software_external (self) + && !nm_platform_link_is_up (priv->ifindex) + && priv->ifindex > 0) + nm_device_set_initial_unmanaged_flag (self, NM_UNMANAGED_EXTERNAL_DOWN, TRUE); + + if (priv->master) + nm_device_enslave_slave (priv->master, self, NULL); + + priv->initialized = TRUE; +} + static void -carrier_changed (NMDevice *device, gboolean carrier) +carrier_changed (NMDevice *self, gboolean carrier) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (!nm_device_get_managed (device)) + if (!nm_device_get_managed (self)) return; - nm_device_recheck_available_connections (device); + nm_device_recheck_available_connections (self); /* ignore-carrier devices ignore all carrier-down events */ if (priv->ignore_carrier && !carrier) @@ -872,13 +1034,13 @@ carrier_changed (NMDevice *device, gboolean carrier) if (!carrier) return; - if (nm_device_activate_ip4_state_in_wait (device)) - nm_device_activate_stage3_ip4_start (device); - if (nm_device_activate_ip6_state_in_wait (device)) - nm_device_activate_stage3_ip6_start (device); + if (nm_device_activate_ip4_state_in_wait (self)) + nm_device_activate_stage3_ip4_start (self); + if (nm_device_activate_ip6_state_in_wait (self)) + nm_device_activate_stage3_ip6_start (self); return; - } else if (nm_device_get_enslaved (device) && !carrier) { + } else if (nm_device_get_enslaved (self) && !carrier) { /* Slaves don't deactivate when they lose carrier; for * bonds/teams in particular that would be actively * counterproductive. @@ -890,7 +1052,7 @@ carrier_changed (NMDevice *device, gboolean carrier) g_warn_if_fail (priv->state >= NM_DEVICE_STATE_UNAVAILABLE); if (priv->state == NM_DEVICE_STATE_UNAVAILABLE) { - nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, + nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_CARRIER); } else if (priv->state == NM_DEVICE_STATE_DISCONNECTED) { /* If the device is already in DISCONNECTED state without a carrier @@ -898,14 +1060,16 @@ carrier_changed (NMDevice *device, gboolean carrier) * when the carrier appears, auto connections are rechecked for * the device. */ - nm_device_emit_recheck_auto_activate (device); + nm_device_emit_recheck_auto_activate (self); } } else { + g_return_if_fail (priv->state >= NM_DEVICE_STATE_UNAVAILABLE); + if (priv->state == NM_DEVICE_STATE_UNAVAILABLE) { - if (nm_device_queued_state_peek (device) >= NM_DEVICE_STATE_DISCONNECTED) - nm_device_queued_state_clear (device); - } else if (priv->state >= NM_DEVICE_STATE_DISCONNECTED) { - nm_device_queue_state (device, NM_DEVICE_STATE_UNAVAILABLE, + if (nm_device_queued_state_peek (self) >= NM_DEVICE_STATE_DISCONNECTED) + nm_device_queued_state_clear (self); + } else { + nm_device_queue_state (self, NM_DEVICE_STATE_UNAVAILABLE, NM_DEVICE_STATE_REASON_CARRIER); } } @@ -916,18 +1080,16 @@ carrier_changed (NMDevice *device, gboolean carrier) static gboolean link_disconnect_action_cb (gpointer user_data) { - NMDevice *device = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - nm_log_dbg (LOGD_DEVICE, "(%s): link disconnected (calling deferred action) (id=%u)", - nm_device_get_iface (device), priv->carrier_defer_id); + _LOGD (LOGD_DEVICE, "link disconnected (calling deferred action) (id=%u)", priv->carrier_defer_id); priv->carrier_defer_id = 0; - nm_log_info (LOGD_DEVICE, "(%s): link disconnected (calling deferred action)", - nm_device_get_iface (device)); + _LOGI (LOGD_DEVICE, "link disconnected (calling deferred action)"); - NM_DEVICE_GET_CLASS (device)->carrier_changed (device, FALSE); + NM_DEVICE_GET_CLASS (self)->carrier_changed (self, FALSE); return FALSE; } @@ -939,67 +1101,64 @@ link_disconnect_action_cancel (NMDevice *self) if (priv->carrier_defer_id) { g_source_remove (priv->carrier_defer_id); - nm_log_dbg (LOGD_DEVICE, "(%s): link disconnected (canceling deferred action) (id=%u)", - nm_device_get_iface (self), priv->carrier_defer_id); + _LOGD (LOGD_DEVICE, "link disconnected (canceling deferred action) (id=%u)", priv->carrier_defer_id); priv->carrier_defer_id = 0; } } void -nm_device_set_carrier (NMDevice *device, gboolean carrier) +nm_device_set_carrier (NMDevice *self, gboolean carrier) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device); - NMDeviceState state = nm_device_get_state (device); - const char *iface = nm_device_get_iface (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self); + NMDeviceState state = nm_device_get_state (self); if (priv->carrier == carrier) return; priv->carrier = carrier; - g_object_notify (G_OBJECT (device), NM_DEVICE_CARRIER); + g_object_notify (G_OBJECT (self), NM_DEVICE_CARRIER); if (priv->carrier) { - nm_log_info (LOGD_DEVICE, "(%s): link connected", iface); - link_disconnect_action_cancel (device); - klass->carrier_changed (device, TRUE); + _LOGI (LOGD_DEVICE, "link connected"); + link_disconnect_action_cancel (self); + klass->carrier_changed (self, TRUE); if (priv->carrier_wait_id) { g_source_remove (priv->carrier_wait_id); priv->carrier_wait_id = 0; - nm_device_remove_pending_action (device, "carrier wait", TRUE); + nm_device_remove_pending_action (self, "carrier wait", TRUE); } } else if (state <= NM_DEVICE_STATE_DISCONNECTED) { - nm_log_info (LOGD_DEVICE, "(%s): link disconnected", iface); - klass->carrier_changed (device, FALSE); + _LOGI (LOGD_DEVICE, "link disconnected"); + klass->carrier_changed (self, FALSE); } else { - nm_log_info (LOGD_DEVICE, "(%s): link disconnected (deferring action for %d seconds)", - iface, LINK_DISCONNECT_DELAY); + _LOGI (LOGD_DEVICE, "link disconnected (deferring action for %d seconds)", LINK_DISCONNECT_DELAY); priv->carrier_defer_id = g_timeout_add_seconds (LINK_DISCONNECT_DELAY, - link_disconnect_action_cb, device); - nm_log_dbg (LOGD_DEVICE, "(%s): link disconnected (deferring action for %d seconds) (id=%u)", - iface, LINK_DISCONNECT_DELAY, priv->carrier_defer_id); + link_disconnect_action_cb, self); + _LOGD (LOGD_DEVICE, "link disconnected (deferring action for %d seconds) (id=%u)", + LINK_DISCONNECT_DELAY, priv->carrier_defer_id); } } static void -update_for_ip_ifname_change (NMDevice *device) +update_for_ip_ifname_change (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); g_hash_table_remove_all (priv->ip6_saved_properties); if (priv->dhcp4_client) { - if (!nm_device_dhcp4_renew (device, FALSE)) { - nm_device_state_changed (device, + if (!nm_device_dhcp4_renew (self, FALSE)) { + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DHCP_FAILED); return; } } if (priv->dhcp6_client) { - if (!nm_device_dhcp6_renew (device, FALSE)) { - nm_device_state_changed (device, + if (!nm_device_dhcp6_renew (self, FALSE)) { + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DHCP_FAILED); return; @@ -1014,98 +1173,152 @@ update_for_ip_ifname_change (NMDevice *device) } static void -device_link_changed (NMDevice *device, NMPlatformLink *info) +device_set_master (NMDevice *self, int ifindex) +{ + 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 (ifindex)); + } +} + +static void +device_link_changed (NMDevice *self, NMPlatformLink *info) { - NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean ip_ifname_changed = FALSE; if (info->udi && g_strcmp0 (info->udi, priv->udi)) { /* Update UDI to what udev gives us */ g_free (priv->udi); priv->udi = g_strdup (info->udi); - g_object_notify (G_OBJECT (device), NM_DEVICE_UDI); + g_object_notify (G_OBJECT (self), NM_DEVICE_UDI); } /* Update MTU if it has changed. */ if (priv->mtu != info->mtu) { priv->mtu = info->mtu; - g_object_notify (G_OBJECT (device), NM_DEVICE_MTU); + g_object_notify (G_OBJECT (self), NM_DEVICE_MTU); } if (info->name[0] && strcmp (priv->iface, info->name) != 0) { - nm_log_info (LOGD_DEVICE, "(%s): interface index %d renamed iface from '%s' to '%s'", - priv->iface, priv->ifindex, priv->iface, info->name); + _LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'", + priv->ifindex, priv->iface, info->name); g_free (priv->iface); priv->iface = g_strdup (info->name); /* If the device has no explicit ip_iface, then changing iface changes ip_iface too. */ ip_ifname_changed = !priv->ip_iface; - g_object_notify (G_OBJECT (device), NM_DEVICE_IFACE); + g_object_notify (G_OBJECT (self), NM_DEVICE_IFACE); if (ip_ifname_changed) - g_object_notify (G_OBJECT (device), NM_DEVICE_IP_IFACE); + g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE); /* Re-match available connections against the new interface name */ - nm_device_recheck_available_connections (device); + nm_device_recheck_available_connections (self); /* Let any connections that use the new interface name have a chance * to auto-activate on the device. */ - nm_device_emit_recheck_auto_activate (device); + 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) { - NMDevice *master; - - master = nm_manager_get_device_by_ifindex (nm_manager_get (), info->master); - 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, device, FALSE); - nm_device_enslave_slave (master, device, NULL); - } else if (master) { - nm_log_info (LOGD_DEVICE, "(%s): enslaved to non-master-type device %s; ignoring", - nm_device_get_iface (device), - nm_device_get_iface (master)); - } else { - nm_log_warn (LOGD_DEVICE, "(%s): enslaved to unknown device %d %s", - nm_device_get_iface (device), - info->master, - nm_platform_link_get_name (info->master)); - } - } else if (priv->enslaved && !info->master) - nm_device_release_one_slave (priv->master, device, FALSE, NM_DEVICE_STATE_REASON_NONE); + device_set_master (self, info->master); + if (priv->master) + nm_device_enslave_slave (priv->master, self, NULL); + } if (klass->link_changed) - klass->link_changed (device, info); - + klass->link_changed (self, info); /* Update DHCP, etc, if needed */ if (ip_ifname_changed) - update_for_ip_ifname_change (device); + update_for_ip_ifname_change (self); + + if (priv->up != info->up) { + priv->up = info->up; + + /* Manage externally-created software interfaces only when they are IFF_UP */ + g_assert (priv->ifindex > 0); + if (is_software_external (self)) { + gboolean external_down = nm_device_get_unmanaged_flag (self, NM_UNMANAGED_EXTERNAL_DOWN); + + if (external_down && info->up) { + if (nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED) { + /* Ensure the assume check is queued before any queued state changes + * from the transition to UNAVAILABLE. + */ + nm_device_queue_recheck_assume (self); + + /* Resetting the EXTERNAL_DOWN flag may change the device's state + * to UNAVAILABLE. To ensure that the state change doesn't touch + * 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); + } 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; + } + } else if (!external_down && !info->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); + } + } + } } static void -device_ip_link_changed (NMDevice *device, NMPlatformLink *info) +device_ip_link_changed (NMDevice *self, NMPlatformLink *info) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); if (info->name[0] && g_strcmp0 (priv->ip_iface, info->name)) { - nm_log_info (LOGD_DEVICE, "(%s): interface index %d renamed ip_iface (%d) from '%s' to '%s'", - priv->iface, priv->ifindex, nm_device_get_ip_ifindex (device), - priv->ip_iface, info->name); + _LOGI (LOGD_DEVICE, "interface index %d renamed ip_iface (%d) from '%s' to '%s'", + priv->ifindex, nm_device_get_ip_ifindex (self), + priv->ip_iface, info->name); g_free (priv->ip_iface); priv->ip_iface = g_strdup (info->name); - g_object_notify (G_OBJECT (device), NM_DEVICE_IP_IFACE); - update_for_ip_ifname_change (device); + g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE); + update_for_ip_ifname_change (self); } } static void -link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink *info, NMPlatformSignalChangeType change_type, NMPlatformReason reason, NMDevice *device) +link_changed_cb (NMPlatform *platform, + int ifindex, + NMPlatformLink *info, + NMPlatformSignalChangeType change_type, + NMPlatformReason reason, + NMDevice *self) { if (change_type != NM_PLATFORM_SIGNAL_CHANGED) return; @@ -1116,24 +1329,24 @@ link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink *info, NMPlat * and it results in also setting IFF_LOWER_UP. */ - if (ifindex == nm_device_get_ifindex (device)) - device_link_changed (device, info); - else if (ifindex == nm_device_get_ip_ifindex (device)) - device_ip_link_changed (device, info); + if (ifindex == nm_device_get_ifindex (self)) + device_link_changed (self, info); + else if (ifindex == nm_device_get_ip_ifindex (self)) + device_ip_link_changed (self, info); } static void -link_changed (NMDevice *device, NMPlatformLink *info) +link_changed (NMDevice *self, NMPlatformLink *info) { /* Update carrier from link event if applicable. */ - if ( device_has_capability (device, NM_DEVICE_CAP_CARRIER_DETECT) - && !device_has_capability (device, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) - nm_device_set_carrier (device, info->connected); + if ( device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT) + && !device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) + nm_device_set_carrier (self, info->connected); } /** * nm_device_notify_component_added(): - * @device: the #NMDevice + * @self: the #NMDevice * @component: the component being added by a plugin * * Called by the manager to notify the device that a new component has @@ -1144,16 +1357,16 @@ link_changed (NMDevice *device, NMPlatformLink *info) * claimed. */ gboolean -nm_device_notify_component_added (NMDevice *device, GObject *component) +nm_device_notify_component_added (NMDevice *self, GObject *component) { - if (NM_DEVICE_GET_CLASS (device)->component_added) - return NM_DEVICE_GET_CLASS (device)->component_added (device, component); + if (NM_DEVICE_GET_CLASS (self)->component_added) + return NM_DEVICE_GET_CLASS (self)->component_added (self, component); return FALSE; } /** * nm_device_owns_iface(): - * @device: the #NMDevice + * @self: the #NMDevice * @iface: an interface name * * Called by the manager to ask if the device or any of its components owns @@ -1162,17 +1375,25 @@ nm_device_notify_component_added (NMDevice *device, GObject *component) * because that ethernet interface is controlled by the WWAN device and cannot * be used independently of the WWAN device. * - * Returns: %TRUE if @device or it's components owns the interface name, + * Returns: %TRUE if @self or it's components owns the interface name, * %FALSE if not */ gboolean -nm_device_owns_iface (NMDevice *device, const char *iface) +nm_device_owns_iface (NMDevice *self, const char *iface) { - if (NM_DEVICE_GET_CLASS (device)->owns_iface) - return NM_DEVICE_GET_CLASS (device)->owns_iface (device, iface); + if (NM_DEVICE_GET_CLASS (self)->owns_iface) + return NM_DEVICE_GET_CLASS (self)->owns_iface (self, iface); return FALSE; } +NMConnection * +nm_device_new_default_connection (NMDevice *self) +{ + if (NM_DEVICE_GET_CLASS (self)->new_default_connection) + return NM_DEVICE_GET_CLASS (self)->new_default_connection (self); + return NULL; +} + static void slave_state_changed (NMDevice *slave, NMDeviceState slave_new_state, @@ -1183,13 +1404,12 @@ slave_state_changed (NMDevice *slave, NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean release = FALSE; - nm_log_dbg (LOGD_DEVICE, "(%s): slave %s state change %d (%s) -> %d (%s)", - nm_device_get_iface (self), - nm_device_get_iface (slave), - slave_old_state, - state_to_string (slave_old_state), - slave_new_state, - state_to_string (slave_new_state)); + _LOGD (LOGD_DEVICE, "slave %s state change %d (%s) -> %d (%s)", + nm_device_get_iface (slave), + slave_old_state, + state_to_string (slave_old_state), + slave_new_state, + state_to_string (slave_new_state)); /* Don't try to enslave slaves until the master is ready */ if (priv->state < NM_DEVICE_STATE_CONFIG) @@ -1208,46 +1428,45 @@ slave_state_changed (NMDevice *slave, if (release) { nm_device_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) { - nm_log_dbg (LOGD_DEVICE, "(%s): last slave removed; remaining activated", - nm_device_get_iface (self)); - } + if (priv->slaves == NULL && priv->state == NM_DEVICE_STATE_ACTIVATED) + _LOGD (LOGD_DEVICE, "last slave removed; remaining activated"); } } /** * nm_device_master_add_slave: - * @dev: the master device + * @self: the master device * @slave: the slave device to enslave * @configure: pass %TRUE if the slave should be configured by the master, or * %FALSE if it is already configured outside NetworkManager * - * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team, + * 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 -nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure) +nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); SlaveInfo *info; - g_return_val_if_fail (dev != NULL, FALSE); + 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 (dev)->enslave_slave != NULL, FALSE); + g_return_val_if_fail (NM_DEVICE_GET_CLASS (self)->enslave_slave != NULL, FALSE); if (configure) g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); - if (!find_slave_info (dev, slave)) { + if (!find_slave_info (self, slave)) { info = g_malloc0 (sizeof (SlaveInfo)); info->slave = g_object_ref (slave); info->configure = configure; info->watch_id = g_signal_connect (slave, "state-changed", - G_CALLBACK (slave_state_changed), dev); + G_CALLBACK (slave_state_changed), self); priv->slaves = g_slist_append (priv->slaves, info); } + nm_device_queue_recheck_assume (self); return TRUE; } @@ -1255,14 +1474,14 @@ nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure) /** * nm_device_master_get_slaves: - * @dev: the master device + * @self: the master device * - * Returns: any slaves of which @device is the master. Caller owns returned list. + * Returns: any slaves of which @self is the master. Caller owns returned list. */ GSList * -nm_device_master_get_slaves (NMDevice *dev) +nm_device_master_get_slaves (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); GSList *slaves = NULL, *iter; for (iter = priv->slaves; iter; iter = g_slist_next (iter)) @@ -1273,18 +1492,18 @@ nm_device_master_get_slaves (NMDevice *dev) /** * nm_device_master_get_slave_by_ifindex: - * @dev: the master device + * @self: the master device * @ifindex: the slave's interface index * - * Returns: the slave with the given @ifindex of which @device is the master, - * or %NULL if no device with @ifindex is a slave of @device. + * Returns: the slave with the given @ifindex of which @self is the master, + * or %NULL if no device with @ifindex is a slave of @self. */ NMDevice * -nm_device_master_get_slave_by_ifindex (NMDevice *dev, int ifindex) +nm_device_master_get_slave_by_ifindex (NMDevice *self, int ifindex) { GSList *iter; - for (iter = NM_DEVICE_GET_PRIVATE (dev)->slaves; iter; iter = g_slist_next (iter)) { + for (iter = NM_DEVICE_GET_PRIVATE (self)->slaves; iter; iter = g_slist_next (iter)) { SlaveInfo *info = iter->data; if (nm_device_get_ip_ifindex (info->slave) == ifindex) @@ -1295,18 +1514,18 @@ nm_device_master_get_slave_by_ifindex (NMDevice *dev, int ifindex) /** * nm_device_master_check_slave_physical_port: - * @dev: the master device + * @self: the master device * @slave: a slave device * @log_domain: domain to log a warning in * - * Checks if @dev already has a slave with the same #NMDevice:physical-port-id + * Checks if @self already has a slave with the same #NMDevice:physical-port-id * as @slave, and logs a warning if so. */ void -nm_device_master_check_slave_physical_port (NMDevice *dev, NMDevice *slave, +nm_device_master_check_slave_physical_port (NMDevice *self, NMDevice *slave, guint64 log_domain) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const char *slave_physical_port_id, *existing_physical_port_id; SlaveInfo *info; GSList *iter; @@ -1322,10 +1541,9 @@ nm_device_master_check_slave_physical_port (NMDevice *dev, NMDevice *slave, existing_physical_port_id = nm_device_get_physical_port_id (info->slave); if (!g_strcmp0 (slave_physical_port_id, existing_physical_port_id)) { - nm_log_warn (log_domain, "(%s): slave %s shares a physical port with existing slave %s", - nm_device_get_ip_iface (dev), - nm_device_get_ip_iface (slave), - nm_device_get_ip_iface (info->slave)); + _LOGW (log_domain, "slave %s shares a physical port with existing slave %s", + nm_device_get_ip_iface (slave), + nm_device_get_ip_iface (info->slave)); /* Since this function will get called for every slave, we only have * to warn about the first match we find; if there are other matches * later in the list, we will have already warned about them matching @@ -1344,7 +1562,7 @@ nm_device_master_release_slaves (NMDevice *self) NMDeviceStateReason reason; /* Don't release the slaves if this connection doesn't belong to NM. */ - if (nm_device_uses_generated_connection (self)) + if (nm_device_uses_generated_assumed_connection (self)) return; reason = priv->state_reason; @@ -1360,19 +1578,19 @@ nm_device_master_release_slaves (NMDevice *self) /** * nm_device_get_master: - * @dev: the device + * @self: the device * - * If @dev has been enslaved by another device, this returns that + * If @self has been enslaved by another device, this returns that * device. Otherwise it returns %NULL. (In particular, note that if - * @dev is in the process of activating as a slave, but has not yet + * @self is in the process of activating as a slave, but has not yet * been enslaved by its master, this will return %NULL.) * - * Returns: (transfer none): @dev's master, or %NULL + * Returns: (transfer none): @self's master, or %NULL */ NMDevice * -nm_device_get_master (NMDevice *dev) +nm_device_get_master (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); if (priv->enslaved) return priv->master; @@ -1382,17 +1600,17 @@ nm_device_get_master (NMDevice *dev) /** * nm_device_slave_notify_enslave: - * @dev: the slave device + * @self: the slave device * @success: whether the enslaving operation succeeded * * Notifies a slave that either it has been enslaved, or else its master tried * to enslave it and failed. */ static void -nm_device_slave_notify_enslave (NMDevice *dev, gboolean success) +nm_device_slave_notify_enslave (NMDevice *self, gboolean success) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); - NMConnection *connection = nm_device_get_connection (dev); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMConnection *connection = nm_device_get_connection (self); gboolean activating = (priv->state == NM_DEVICE_STATE_IP_CONFIG); g_assert (priv->master); @@ -1400,49 +1618,41 @@ nm_device_slave_notify_enslave (NMDevice *dev, gboolean success) if (!priv->enslaved) { if (success) { if (activating) { - nm_log_info (LOGD_DEVICE, - "Activation (%s) connection '%s' enslaved, continuing activation", - nm_device_get_iface (dev), - nm_connection_get_id (connection)); - } else { - nm_log_info (LOGD_DEVICE, - "(%s): enslaved to %s", - nm_device_get_iface (dev), - nm_device_get_iface (priv->master)); - } + _LOGI (LOGD_DEVICE, "Activation: connection '%s' enslaved, continuing activation", + nm_connection_get_id (connection)); + } else + _LOGI (LOGD_DEVICE, "enslaved to %s", nm_device_get_iface (priv->master)); priv->enslaved = TRUE; - g_object_notify (G_OBJECT (dev), NM_DEVICE_MASTER); + g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER); } else if (activating) { - nm_log_warn (LOGD_DEVICE, - "Activation (%s) connection '%s' could not be enslaved", - nm_device_get_iface (dev), - nm_connection_get_id (connection)); + _LOGW (LOGD_DEVICE, "Activation: connection '%s' could not be enslaved", + nm_connection_get_id (connection)); } } if (activating) { priv->ip4_state = IP_DONE; priv->ip6_state = IP_DONE; - nm_device_queue_state (dev, + nm_device_queue_state (self, success ? NM_DEVICE_STATE_SECONDARIES : NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NONE); } else - nm_device_queue_recheck_assume (dev); + nm_device_queue_recheck_assume (self); } /** * nm_device_slave_notify_release: - * @dev: the slave device + * @self: the slave device * @reason: the reason associated with the state change * * Notifies a slave that it has been released, and why. */ static void -nm_device_slave_notify_release (NMDevice *dev, NMDeviceStateReason reason) +nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); - NMConnection *connection = nm_device_get_connection (dev); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMConnection *connection = nm_device_get_connection (self); NMDeviceState new_state; const char *master_status; @@ -1460,43 +1670,56 @@ nm_device_slave_notify_release (NMDevice *dev, NMDeviceStateReason reason) master_status = "deactivated"; } - nm_log_dbg (LOGD_DEVICE, - "Activation (%s) connection '%s' master %s", - nm_device_get_iface (dev), - nm_connection_get_id (connection), - master_status); + _LOGD (LOGD_DEVICE, "Activation: connection '%s' master %s", + nm_connection_get_id (connection), + master_status); - nm_device_queue_state (dev, new_state, reason); - } else { - nm_log_info (LOGD_DEVICE, - "(%s): released from master %s", - nm_device_get_iface (dev), - nm_device_get_iface (priv->master)); - } + 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)"); if (priv->enslaved) { priv->enslaved = FALSE; - g_object_notify (G_OBJECT (dev), NM_DEVICE_MASTER); + g_object_notify (G_OBJECT (self), NM_DEVICE_MASTER); } } /** * nm_device_get_enslaved: - * @device: the #NMDevice + * @self: the #NMDevice * * Returns: %TRUE if the device is enslaved to a master device (eg bridge or * bond or team), %FALSE if not */ gboolean -nm_device_get_enslaved (NMDevice *device) +nm_device_get_enslaved (NMDevice *self) { - return NM_DEVICE_GET_PRIVATE (device)->enslaved; + return NM_DEVICE_GET_PRIVATE (self)->enslaved; } +/** + * nm_device_removed: + * @self: the #NMDevice + * + * Called by the manager when the device was removed. Releases the device from + * the master in case it's enslaved. + */ +void +nm_device_removed (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->enslaved) + nm_device_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_REMOVED); +} + + static gboolean -is_available (NMDevice *device) +is_available (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); return priv->carrier || priv->ignore_carrier; } @@ -1548,12 +1771,44 @@ nm_device_set_enabled (NMDevice *self, gboolean enabled) NM_DEVICE_GET_CLASS (self)->set_enabled (self, enabled); } +/** + * nm_device_get_autoconnect: + * @self: the #NMDevice + * + * Returns: %TRUE if the device allows autoconnect connections, or %FALSE if the + * device is explicitly blocking all autoconnect connections. Does not take + * into account transient conditions like companion devices that may wish to + * block the device. + */ gboolean -nm_device_get_autoconnect (NMDevice *device) +nm_device_get_autoconnect (NMDevice *self) { - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - return NM_DEVICE_GET_PRIVATE (device)->autoconnect; + return NM_DEVICE_GET_PRIVATE (self)->autoconnect; +} + +static void +nm_device_set_autoconnect (NMDevice *self, gboolean autoconnect) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + + priv = NM_DEVICE_GET_PRIVATE (self); + if (priv->autoconnect == autoconnect) + return; + + if (autoconnect) { + /* Default-unmanaged devices never autoconnect */ + if (!nm_device_get_default_unmanaged (self)) { + priv->autoconnect = TRUE; + g_object_notify (G_OBJECT (self), NM_DEVICE_AUTOCONNECT); + } + } else { + priv->autoconnect = FALSE; + g_object_notify (G_OBJECT (self), NM_DEVICE_AUTOCONNECT); + } } static gboolean @@ -1566,6 +1821,14 @@ autoconnect_allowed_accumulator (GSignalInvocationHint *ihint, return TRUE; } +/** + * nm_device_autoconnect_allowed: + * @self: the #NMDevice + * + * Returns: %TRUE if the device can be auto-connected immediately, taking + * transient conditions into account (like companion devices that may wish to + * block autoconnect for a time). + */ gboolean nm_device_autoconnect_allowed (NMDevice *self) { @@ -1573,6 +1836,16 @@ 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) + return FALSE; + + /* The 'autoconnect-allowed' signal is emitted on a device to allow + * other listeners to block autoconnect on the device if they wish. + * This is mainly used by the OLPC Mesh devices to block autoconnect + * on their companion WiFi device as they share radio resources and + * cannot be connected at the same time. + */ + g_value_init (&instance, G_TYPE_OBJECT); g_value_set_object (&instance, self); @@ -1590,7 +1863,7 @@ nm_device_autoconnect_allowed (NMDevice *self) } static gboolean -can_auto_connect (NMDevice *device, +can_auto_connect (NMDevice *self, NMConnection *connection, char **specific_object) { @@ -1600,13 +1873,44 @@ can_auto_connect (NMDevice *device, if (!nm_setting_connection_get_autoconnect (s_con)) return FALSE; - return nm_device_connection_is_available (device, connection, FALSE); + return nm_device_connection_is_available (self, connection, FALSE); +} + +/** + * nm_device_can_auto_connect: + * @self: an #NMDevice + * @connection: a #NMConnection + * @specific_object: (out) (transfer full): on output, the path of an + * object associated with the returned connection, to be passed to + * nm_manager_activate_connection(), or %NULL. + * + * Checks if @connection can be auto-activated on @self right now. + * This requires, at a minimum, that the connection be compatible with + * @self, and that it have the #NMSettingConnection:autoconnect property + * set, and that the device allow auto connections. Some devices impose + * additional requirements. (Eg, a Wi-Fi connection can only be activated + * if its SSID was seen in the last scan.) + * + * Returns: %TRUE, if the @connection can be auto-activated. + **/ +gboolean +nm_device_can_auto_connect (NMDevice *self, + NMConnection *connection, + char **specific_object) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (specific_object && !*specific_object, FALSE); + + if (nm_device_autoconnect_allowed (self)) + return NM_DEVICE_GET_CLASS (self)->can_auto_connect (self, connection, specific_object); + return FALSE; } static gboolean -device_has_config (NMDevice *device) +device_has_config (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); /* Check for IP configuration. */ if (priv->ip4_config && nm_ip4_config_get_num_addresses (priv->ip4_config)) @@ -1615,7 +1919,7 @@ device_has_config (NMDevice *device) return TRUE; /* The existence of a software device is good enough. */ - if (nm_device_is_software (device)) + if (nm_device_is_software (self)) return TRUE; /* Slaves are also configured by definition */ @@ -1625,20 +1929,64 @@ device_has_config (NMDevice *device) return FALSE; } +/** + * nm_device_master_update_slave_connection: + * @self: the master #NMDevice + * @slave: the slave #NMDevice + * @connection: the #NMConnection to update with the slave settings + * @GError: (out): error description + * + * Reads the slave configuration for @slave and updates @connection with those + * properties. This invokes a virtual function on the master device @self. + * + * Returns: %TRUE if the configuration was read and @connection updated, + * %FALSE on failure. + */ +gboolean +nm_device_master_update_slave_connection (NMDevice *self, + NMDevice *slave, + NMConnection *connection, + GError **error) +{ + NMDeviceClass *klass; + gboolean success; + + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + g_return_val_if_fail (slave, FALSE); + g_return_val_if_fail (connection, FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + g_return_val_if_fail (nm_connection_get_setting_connection (connection), FALSE); + + g_return_val_if_fail (nm_device_get_iface (self), FALSE); + + klass = NM_DEVICE_GET_CLASS (self); + if (klass->master_update_slave_connection) { + success = klass->master_update_slave_connection (self, slave, connection, error); + + g_return_val_if_fail (!error || (success && !*error) || *error, success); + return success; + } + + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + "master device '%s' cannot update a slave connection for slave device '%s' (master type not supported?)", + nm_device_get_iface (self), nm_device_get_iface (slave)); + return FALSE; +} + NMConnection * -nm_device_generate_connection (NMDevice *device) +nm_device_generate_connection (NMDevice *self, NMDevice *master) { - NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - const char *ifname = nm_device_get_iface (device); - int ifindex = nm_device_get_ifindex (device); + NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const char *ifname = nm_device_get_iface (self); NMConnection *connection; NMSetting *s_con; NMSetting *s_ip4; NMSetting *s_ip6; gs_free char *uuid = NULL; - gs_free char *name = NULL; - int master_ifindex = 0; const char *ip4_method, *ip6_method; GError *error = NULL; @@ -1647,32 +1995,18 @@ nm_device_generate_connection (NMDevice *device) return NULL; /* Return NULL if device is unconfigured. */ - if (!device_has_config (device)) { - nm_log_dbg (LOGD_DEVICE, "(%s): device has no existing configuration", ifname); + if (!device_has_config (self)) { + _LOGD (LOGD_DEVICE, "device has no existing configuration"); return NULL; } - if (ifindex) - master_ifindex = nm_platform_link_get_master (ifindex); - if (master_ifindex) { - NMDevice *master; - - master = nm_manager_get_device_by_ifindex (nm_manager_get (), master_ifindex); - if (!master || !nm_device_get_act_request (master)) { - nm_log_dbg (LOGD_DEVICE, "(%s): cannot generate connection for slave before its master (%s)", - ifname, nm_platform_link_get_name (master_ifindex)); - return NULL; - } - } - - connection = nm_connection_new (); + connection = nm_simple_connection_new (); s_con = nm_setting_connection_new (); uuid = nm_utils_uuid_generate (); - name = g_strdup_printf ("%s", ifname); g_object_set (s_con, NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_ID, name, + NM_SETTING_CONNECTION_ID, ifname, NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL), @@ -1682,36 +2016,18 @@ nm_device_generate_connection (NMDevice *device) nm_connection_add_setting (connection, s_con); /* If the device is a slave, update various slave settings */ - if (master_ifindex) { - const char *master_iface = nm_platform_link_get_name (master_ifindex); - const char *slave_type = NULL; - gboolean success = FALSE; - - switch (nm_platform_link_get_type (master_ifindex)) { - case NM_LINK_TYPE_BRIDGE: - slave_type = NM_SETTING_BRIDGE_SETTING_NAME; - success = nm_bridge_update_slave_connection (device, connection); - break; - case NM_LINK_TYPE_BOND: - slave_type = NM_SETTING_BOND_SETTING_NAME; - success = TRUE; - break; - case NM_LINK_TYPE_TEAM: - slave_type = NM_SETTING_TEAM_SETTING_NAME; - success = nm_team_update_slave_connection (device, connection); - break; - default: - g_warn_if_reached (); - break; + if (master) { + if (!nm_device_master_update_slave_connection (master, + self, + connection, + &error)) + { + _LOGE (LOGD_DEVICE, "master device '%s' failed to update slave connection: %s", + nm_device_get_iface (master), error ? error->message : "(unknown error)"); + g_error_free (error); + g_object_unref (connection); + return NULL; } - - if (!success) - nm_log_err (LOGD_DEVICE, "(%s): failed to read slave configuration", ifname); - - g_object_set (s_con, - NM_SETTING_CONNECTION_MASTER, master_iface, - NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type, - NULL); } else { /* Only regular and master devices get IP configuration; slaves do not */ s_ip4 = nm_ip4_config_create_setting (priv->ip4_config); @@ -1721,12 +2037,11 @@ nm_device_generate_connection (NMDevice *device) nm_connection_add_setting (connection, s_ip6); } - klass->update_connection (device, connection); + klass->update_connection (self, connection); /* Check the connection in case of update_connection() bug. */ if (!nm_connection_verify (connection, &error)) { - nm_log_err (LOGD_DEVICE, "(%s): Generated connection does not verify: %s", - nm_device_get_iface (device), error->message); + _LOGE (LOGD_DEVICE, "Generated connection does not verify: %s", error->message); g_clear_error (&error); g_object_unref (connection); return NULL; @@ -1739,8 +2054,9 @@ nm_device_generate_connection (NMDevice *device) ip6_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); if ( g_strcmp0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0 && g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0 - && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con))) { - nm_log_dbg (LOGD_DEVICE, "(%s): ignoring generated connection (no IP and not slave)", ifname); + && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con)) + && !priv->slaves) { + _LOGD (LOGD_DEVICE, "ignoring generated connection (no IP and not in master-slave relationship)"); g_object_unref (connection); connection = NULL; } @@ -1748,46 +2064,6 @@ nm_device_generate_connection (NMDevice *device) return connection; } -/** - * nm_device_get_best_auto_connection: - * @dev: an #NMDevice - * @connections: (element-type #NMConnection): a list of connections - * @specific_object: (out) (transfer full): on output, the path of an - * object associated with the returned connection, to be passed to - * nm_manager_activate_connection(), or %NULL. - * - * Looks through @connections to see if there is a connection that can - * be auto-activated on @dev right now. This requires, at a minimum, - * that the connection be compatible with @dev, and that it have the - * #NMSettingConnection:autoconnect property set. Some devices impose - * additional requirements. (Eg, a Wi-Fi connection can only be - * activated if its SSID was seen in the last scan.) - * - * Returns: an auto-activatable #NMConnection, or %NULL if none are - * available. - */ - -NMConnection * -nm_device_get_best_auto_connection (NMDevice *dev, - GSList *connections, - char **specific_object) -{ - GSList *iter; - - g_return_val_if_fail (NM_IS_DEVICE (dev), NULL); - g_return_val_if_fail (specific_object != NULL, NULL); - g_return_val_if_fail (*specific_object == NULL, NULL); - - for (iter = connections; iter; iter = iter->next) { - NMConnection *connection = NM_CONNECTION (iter->data); - - if (NM_DEVICE_GET_CLASS (dev)->can_auto_connect (dev, connection, specific_object)) - return connection; - } - - return NULL; -} - gboolean nm_device_complete_connection (NMDevice *self, NMConnection *connection, @@ -1801,7 +2077,7 @@ nm_device_complete_connection (NMDevice *self, g_return_val_if_fail (connection != NULL, FALSE); if (!NM_DEVICE_GET_CLASS (self)->complete_connection) { - g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CONNECTION_INVALID, + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "Device class %s had no complete_connection method", G_OBJECT_TYPE_NAME (self)); return FALSE; @@ -1819,7 +2095,7 @@ nm_device_complete_connection (NMDevice *self, } static gboolean -check_connection_compatible (NMDevice *device, NMConnection *connection) +check_connection_compatible (NMDevice *self, NMConnection *connection) { NMSettingConnection *s_con; const char *config_iface, *device_iface; @@ -1828,7 +2104,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) g_assert (s_con); config_iface = nm_setting_connection_get_interface_name (s_con); - device_iface = nm_device_get_iface (device); + device_iface = nm_device_get_iface (self); if (config_iface && strcmp (config_iface, device_iface) != 0) return FALSE; @@ -1837,43 +2113,31 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) /** * nm_device_check_connection_compatible: - * @device: an #NMDevice + * @self: an #NMDevice * @connection: an #NMConnection * - * Checks if @connection could potentially be activated on @device. - * This means only that @device has the proper capabilities, and that + * Checks if @connection could potentially be activated on @self. + * This means only that @self has the proper capabilities, and that * @connection is not locked to some other device. It does not - * necessarily mean that @connection could be activated on @device + * necessarily mean that @connection could be activated on @self * right now. (Eg, it might refer to a Wi-Fi network that is not * currently available.) * * Returns: #TRUE if @connection could potentially be activated on - * @device. + * @self. */ gboolean -nm_device_check_connection_compatible (NMDevice *device, NMConnection *connection) +nm_device_check_connection_compatible (NMDevice *self, NMConnection *connection) { - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); - return NM_DEVICE_GET_CLASS (device)->check_connection_compatible (device, connection); -} - -static gboolean -string_in_list (const char *str, const char **array, gsize array_len) -{ - gsize i; - - for (i = 0; i < array_len; i++) { - if (strcmp (str, array[i]) == 0) - return TRUE; - } - return FALSE; + return NM_DEVICE_GET_CLASS (self)->check_connection_compatible (self, connection); } /** * nm_device_can_assume_connections: - * @device: #NMDevice instance + * @self: #NMDevice instance * * This is a convenience function to determine whether connection assumption * is available for this device. @@ -1881,14 +2145,14 @@ string_in_list (const char *str, const char **array, gsize array_len) * Returns: %TRUE if the device is capable of assuming connections, %FALSE if not */ static gboolean -nm_device_can_assume_connections (NMDevice *device) +nm_device_can_assume_connections (NMDevice *self) { - return !!NM_DEVICE_GET_CLASS (device)->update_connection; + return !!NM_DEVICE_GET_CLASS (self)->update_connection; } /** * nm_device_can_assume_active_connection: - * @device: #NMDevice instance + * @self: #NMDevice instance * * This is a convenience function to determine whether the device's active * connection can be assumed if NetworkManager restarts. This method returns @@ -1900,9 +2164,9 @@ nm_device_can_assume_connections (NMDevice *device) * assumed. */ gboolean -nm_device_can_assume_active_connection (NMDevice *device) +nm_device_can_assume_active_connection (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; const char *method; const char *assumable_ip6_methods[] = { @@ -1911,17 +2175,19 @@ nm_device_can_assume_active_connection (NMDevice *device) NM_SETTING_IP6_CONFIG_METHOD_DHCP, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NULL }; const char *assumable_ip4_methods[] = { NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NULL }; - if (!nm_device_can_assume_connections (device)) + if (!nm_device_can_assume_connections (self)) return FALSE; - connection = nm_device_get_connection (device); + connection = nm_device_get_connection (self); if (!connection) return FALSE; @@ -1934,11 +2200,11 @@ nm_device_can_assume_active_connection (NMDevice *device) return FALSE; method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - if (!string_in_list (method, assumable_ip6_methods, G_N_ELEMENTS (assumable_ip6_methods))) + if (!_nm_utils_string_in_list (method, assumable_ip6_methods)) return FALSE; method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if (!string_in_list (method, assumable_ip4_methods, G_N_ELEMENTS (assumable_ip4_methods))) + if (!_nm_utils_string_in_list (method, assumable_ip4_methods)) return FALSE; return TRUE; @@ -1950,8 +2216,10 @@ nm_device_emit_recheck_assume (gpointer self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); priv->recheck_assume_id = 0; - if (!nm_device_get_act_request (self) && (priv->ip4_config || priv->ip6_config)) + if (!nm_device_get_act_request (self)) { + _LOGD (LOGD_DEVICE, "emit RECHECK_ASSUME signal"); g_signal_emit (self, signals[RECHECK_ASSUME], 0); + } return G_SOURCE_REMOVE; } @@ -2022,9 +2290,8 @@ activation_source_schedule (NMDevice *self, GSourceFunc func, int family) act_source_func = &priv->act_source_func; } - if (*act_source_id) { - nm_log_err (LOGD_DEVICE, "activation stage already scheduled"); - } + if (*act_source_id) + _LOGE (LOGD_DEVICE, "activation stage already scheduled"); /* Don't bother rescheduling the same function that's about to * run anyway. Fixes issues with crappy wireless drivers sending @@ -2038,12 +2305,11 @@ activation_source_schedule (NMDevice *self, GSourceFunc func, int family) } } -gboolean -nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6) +static gboolean +get_ip_config_may_fail (NMDevice *self, int family) { NMConnection *connection; - NMSettingIP4Config *s_ip4; - NMSettingIP6Config *s_ip6; + NMSettingIPConfig *s_ip = NULL; g_return_val_if_fail (self != NULL, TRUE); @@ -2051,17 +2317,18 @@ nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6) g_assert (connection); /* Fail the connection if the failed IP method is required to complete */ - if (ip6) { - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (!nm_setting_ip6_config_get_may_fail (s_ip6)) - return TRUE; - } else { - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (!nm_setting_ip4_config_get_may_fail (s_ip4)) - return TRUE; + switch (family) { + case AF_INET: + s_ip = nm_connection_get_setting_ip4_config (connection); + break; + case AF_INET6: + s_ip = nm_connection_get_setting_ip6_config (connection); + break; + default: + g_assert_not_reached (); } - return FALSE; + return nm_setting_ip_config_get_may_fail (s_ip); } static void @@ -2083,9 +2350,8 @@ master_ready_cb (NMActiveConnection *active, self, nm_active_connection_get_assumed (active) ? FALSE : TRUE); - nm_log_dbg (LOGD_DEVICE, "(%s): master connection ready; master device %s", - nm_device_get_iface (self), - nm_device_get_iface (priv->master)); + _LOGD (LOGD_DEVICE, "master connection ready; master device %s", + nm_device_get_iface (priv->master)); if (priv->master_ready_id) { g_signal_handler_disconnect (active, priv->master_ready_id); @@ -2112,7 +2378,6 @@ nm_device_activate_stage1_device_prepare (gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *iface; NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request); @@ -2125,8 +2390,7 @@ nm_device_activate_stage1_device_prepare (gpointer user_data) /* Notify the new ActiveConnection along with the state change */ g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION); - iface = nm_device_get_iface (self); - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface); + _LOGI (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 */ @@ -2146,8 +2410,7 @@ nm_device_activate_stage1_device_prepare (gpointer user_data) if (nm_active_connection_get_master_ready (active)) master_ready_cb (active, NULL, self); else { - nm_log_dbg (LOGD_DEVICE, "(%s): waiting for master connection to become ready", - nm_device_get_iface (self)); + _LOGD (LOGD_DEVICE, "waiting for master connection to become ready"); /* Attach a signal handler and wait for the master connection to begin activating */ g_assert (priv->master_ready_id == 0); @@ -2161,7 +2424,7 @@ nm_device_activate_stage1_device_prepare (gpointer user_data) nm_device_activate_schedule_stage2_device_config (self); out: - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) complete.", iface); + _LOGI (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) complete."); return FALSE; } @@ -2184,12 +2447,11 @@ nm_device_activate_schedule_stage1_device_prepare (NMDevice *self) activation_source_schedule (self, nm_device_activate_stage1_device_prepare, 0); - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) scheduled...", - nm_device_get_iface (self)); + _LOGI (LOGD_DEVICE, "Activation: Stage 1 of 5 (Device Prepare) scheduled..."); } static NMActStageReturn -act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) +act_stage2_config (NMDevice *self, NMDeviceStateReason *reason) { /* Nothing to do */ return NM_ACT_STAGE_RETURN_SUCCESS; @@ -2207,7 +2469,6 @@ nm_device_activate_stage2_device_config (gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *iface; NMActStageReturn ret; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; gboolean no_firmware = FALSE; @@ -2217,8 +2478,7 @@ nm_device_activate_stage2_device_config (gpointer user_data) /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, 0); - iface = nm_device_get_iface (self); - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) starting...", iface); + _LOGI (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 */ @@ -2248,17 +2508,17 @@ nm_device_activate_stage2_device_config (gpointer user_data) if (slave_state == NM_DEVICE_STATE_IP_CONFIG) nm_device_enslave_slave (self, info->slave, nm_device_get_connection (info->slave)); - else if ( nm_device_uses_generated_connection (self) + else if ( nm_device_uses_generated_assumed_connection (self) && slave_state <= NM_DEVICE_STATE_DISCONNECTED) nm_device_queue_recheck_assume (info->slave); } - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) successful.", iface); + _LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) successful."); nm_device_activate_schedule_stage3_ip_config_start (self); out: - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) complete.", iface); + _LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) complete."); return FALSE; } @@ -2281,8 +2541,7 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self) activation_source_schedule (self, nm_device_activate_stage2_device_config, 0); - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) scheduled...", - nm_device_get_iface (self)); + _LOGI (LOGD_DEVICE, "Activation: Stage 2 of 5 (Device Configure) scheduled..."); } /*********************************************/ @@ -2310,13 +2569,7 @@ aipd_cleanup (NMDevice *self) } if (priv->aipd_pid > 0) { - kill (priv->aipd_pid, SIGKILL); - - /* ensure the child is reaped */ - nm_log_dbg (LOGD_AUTOIP4, "waiting for avahi-autoipd pid %d to exit", priv->aipd_pid); - waitpid (priv->aipd_pid, NULL, 0); - nm_log_dbg (LOGD_AUTOIP4, "avahi-autoip pid %d cleaned up", priv->aipd_pid); - + nm_utils_kill_child_sync (priv->aipd_pid, SIGKILL, LOGD_AUTOIP4, "avahi-autoipd", NULL, 0, 0); priv->aipd_pid = -1; } @@ -2336,15 +2589,15 @@ aipd_get_ip4_config (NMDevice *self, guint32 lla) memset (&address, 0, sizeof (address)); address.address = lla; address.plen = 16; - address.source = NM_PLATFORM_SOURCE_IP4LL; + address.source = NM_IP_CONFIG_SOURCE_IP4LL; nm_ip4_config_add_address (config, &address); /* Add a multicast route for link-local connections: destination= 224.0.0.0, netmask=240.0.0.0 */ memset (&route, 0, sizeof (route)); route.network = htonl (0xE0000000L); route.plen = 4; - route.source = NM_PLATFORM_SOURCE_IP4LL; - route.metric = nm_device_get_priority (self); + route.source = NM_IP_CONFIG_SOURCE_IP4LL; + route.metric = nm_device_get_ip4_route_metric (self); nm_ip4_config_add_route (config, &route); return config; @@ -2360,7 +2613,7 @@ nm_device_handle_autoip4_event (NMDevice *self, { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection = NULL; - const char *iface, *method; + const char *method; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; g_return_if_fail (event != NULL); @@ -2376,29 +2629,25 @@ nm_device_handle_autoip4_event (NMDevice *self, if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) != 0) return; - iface = nm_device_get_iface (self); - if (strcmp (event, "BIND") == 0) { guint32 lla; NMIP4Config *config; if (inet_pton (AF_INET, address, &lla) <= 0) { - nm_log_err (LOGD_AUTOIP4, "(%s): invalid address %s received from avahi-autoipd.", - iface, address); + _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); return; } if ((lla & IPV4LL_NETMASK) != IPV4LL_NETWORK) { - nm_log_err (LOGD_AUTOIP4, "(%s): invalid address %s received from avahi-autoipd (not link-local).", - iface, address); + _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); return; } config = aipd_get_ip4_config (self, lla); if (config == NULL) { - nm_log_err (LOGD_AUTOIP4, "failed to get autoip config"); + _LOGE (LOGD_AUTOIP4, "failed to get autoip config"); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); return; } @@ -2408,8 +2657,7 @@ nm_device_handle_autoip4_event (NMDevice *self, 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)) { - nm_log_err (LOGD_AUTOIP4, "(%s): failed to update IP4 config for autoip change.", - nm_device_get_iface (self)); + _LOGE (LOGD_AUTOIP4, "failed to update IP4 config for autoip change."); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); } } else @@ -2417,8 +2665,7 @@ nm_device_handle_autoip4_event (NMDevice *self, g_object_unref (config); } else { - nm_log_warn (LOGD_AUTOIP4, "(%s): autoip address %s no longer valid because '%s'.", - iface, address, event); + _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); @@ -2431,26 +2678,19 @@ aipd_watch_cb (GPid pid, gint status, gpointer user_data) NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDeviceState state; - const char *iface; if (!priv->aipd_watch) return; priv->aipd_watch = 0; - iface = nm_device_get_iface (self); - - if (WIFEXITED (status)) { - nm_log_dbg (LOGD_AUTOIP4, "(%s): avahi-autoipd exited with error code %d", - iface, WEXITSTATUS (status)); - } else if (WIFSTOPPED (status)) { - nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd stopped unexpectedly with signal %d", - iface, WSTOPSIG (status)); - } else if (WIFSIGNALED (status)) { - nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd died with signal %d", - iface, WTERMSIG (status)); - } else { - nm_log_warn (LOGD_AUTOIP4, "(%s): avahi-autoipd died from an unknown cause", iface); - } + 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); @@ -2466,7 +2706,7 @@ aipd_timeout_cb (gpointer user_data) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); if (priv->aipd_timeout) { - nm_log_info (LOGD_AUTOIP4, "(%s): avahi-autoipd timed out.", nm_device_get_iface (self)); + _LOGI (LOGD_AUTOIP4, "avahi-autoipd timed out."); priv->aipd_timeout = 0; aipd_cleanup (self); @@ -2501,63 +2741,51 @@ static NMActStageReturn aipd_start (NMDevice *self, NMDeviceStateReason *reason) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *iface = nm_device_get_iface (self); - char *argv[6], *cmdline; - const char **aipd_binary = NULL; - static const char *aipd_paths[] = { - "/usr/sbin/avahi-autoipd", - "/usr/local/sbin/avahi-autoipd", - NULL - }; + const char *argv[6]; + char *cmdline; + const char *aipd_binary; int i = 0; GError *error = NULL; aipd_cleanup (self); /* Find avahi-autoipd */ - aipd_binary = aipd_paths; - while (*aipd_binary != NULL) { - if (g_file_test (*aipd_binary, G_FILE_TEST_EXISTS)) - break; - aipd_binary++; - } - - if (!*aipd_binary) { - nm_log_warn (LOGD_DEVICE | LOGD_AUTOIP4, - "Activation (%s) Stage 3 of 5 (IP Configure Start) failed" - " to start avahi-autoipd: not found", iface); + 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; } - argv[i++] = (char *) (*aipd_binary); + argv[i++] = aipd_binary; argv[i++] = "--script"; - argv[i++] = (char *) nm_device_autoipd_helper_path; + argv[i++] = nm_device_autoipd_helper_path; if (nm_logging_enabled (LOGL_DEBUG, LOGD_AUTOIP4)) argv[i++] = "--debug"; - argv[i++] = (char *) nm_device_get_ip_iface (self); + argv[i++] = nm_device_get_ip_iface (self); argv[i++] = NULL; - cmdline = g_strjoinv (" ", argv); - nm_log_dbg (LOGD_AUTOIP4, "running: %s", cmdline); + cmdline = g_strjoinv (" ", (char **) argv); + _LOGD (LOGD_AUTOIP4, "running: %s", cmdline); g_free (cmdline); - if (!g_spawn_async ("/", argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, + if (!g_spawn_async ("/", (char **) argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, &aipd_child_setup, NULL, &(priv->aipd_pid), &error)) { - nm_log_warn (LOGD_DEVICE | LOGD_AUTOIP4, - "Activation (%s) Stage 3 of 5 (IP Configure Start) failed" - " to start avahi-autoipd: %s", - iface, - error && error->message ? error->message : "(unknown)"); + _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; } - nm_log_info (LOGD_DEVICE | LOGD_AUTOIP4, - "Activation (%s) Stage 3 of 5 (IP Configure Start) started" - " avahi-autoipd...", iface); + _LOGI (LOGD_DEVICE | LOGD_AUTOIP4, + "Activation: Stage 3 of 5 (IP Configure Start) started" + " avahi-autoipd..."); /* Monitor the child process so we know when it dies */ priv->aipd_watch = g_child_watch_add (priv->aipd_pid, aipd_watch_cb, self); @@ -2569,6 +2797,52 @@ aipd_start (NMDevice *self, NMDeviceStateReason *reason) } /*********************************************/ + +static gboolean +_device_get_default_route_from_platform (NMDevice *self, int addr_family, NMPlatformIPRoute *out_route) +{ + gboolean success = FALSE; + int ifindex = nm_device_get_ip_ifindex (self); + GArray *routes; + + if (addr_family == AF_INET) + routes = nm_platform_ip4_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT); + else + routes = nm_platform_ip6_route_get_all (ifindex, NM_PLATFORM_GET_ROUTE_MODE_ONLY_DEFAULT); + + if (routes) { + guint route_metric = G_MAXUINT32, m; + const NMPlatformIPRoute *route = NULL, *r; + guint i; + + /* if there are several default routes, find the one with the best metric */ + for (i = 0; i < routes->len; i++) { + if (addr_family == AF_INET) { + r = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP4Route, i); + m = r->metric; + } else { + r = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP6Route, i); + m = nm_utils_ip6_route_metric_normalize (r->metric); + } + if (!route || m < route_metric) { + route = r; + route_metric = m; + } + } + + if (route) { + if (addr_family == AF_INET) + *((NMPlatformIP4Route *) out_route) = *((NMPlatformIP4Route *) route); + else + *((NMPlatformIP6Route *) out_route) = *((NMPlatformIP6Route *) route); + success = TRUE; + } + g_array_free (routes, TRUE); + } + return success; +} + +/*********************************************/ /* DHCPv4 stuff */ static void @@ -2583,11 +2857,6 @@ dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release) priv->dhcp4_state_sigid = 0; } - if (priv->dhcp4_timeout_sigid) { - g_signal_handler_disconnect (priv->dhcp4_client, priv->dhcp4_timeout_sigid); - priv->dhcp4_timeout_sigid = 0; - } - nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE); if (stop) @@ -2602,14 +2871,6 @@ dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release) } } -static void -dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data) -{ - nm_dhcp4_config_add_option (NM_DHCP4_CONFIG (user_data), - (const char *) key, - (const char *) value); -} - static gboolean ip4_config_merge_and_apply (NMDevice *self, NMIP4Config *config, @@ -2620,6 +2881,7 @@ ip4_config_merge_and_apply (NMDevice *self, NMConnection *connection; gboolean success; NMIP4Config *composite; + const guint32 default_route_metric = nm_device_get_ip4_route_metric (self); /* Merge all the configs into the composite config */ if (config) { @@ -2635,12 +2897,79 @@ ip4_config_merge_and_apply (NMDevice *self, if (priv->ext_ip4_config) nm_ip4_config_merge (composite, priv->ext_ip4_config); - /* Merge user overrides into the composite config */ + /* Merge WWAN config *last* to ensure modem-given settings overwrite + * any external stuff set by pppd or other scripts. + */ + if (priv->wwan_ip4_config) + nm_ip4_config_merge (composite, priv->wwan_ip4_config); + + /* Merge user overrides into the composite config. Generated+assumed + * connections come from the system not the user and merging them would + * be redundant, so don't bother. + */ connection = nm_device_get_connection (self); + priv->default_route.v4_has = FALSE; + priv->default_route.v4_is_assumed = TRUE; if (connection) { - nm_ip4_config_merge_setting (composite, - nm_connection_get_setting_ip4_config (connection), - nm_device_get_priority (self)); + gboolean assumed = nm_device_uses_assumed_connection (self); + NMPlatformIP4Route *route = &priv->default_route.v4; + + if (!nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) { + nm_ip4_config_merge_setting (composite, + nm_connection_get_setting_ip4_config (connection), + default_route_metric); + } + + /* Add the default route. + * + * We keep track of the default route of a device in a private field. + * NMDevice needs to know the default route at this point, because the gateway + * might require a direct route (see below). + * + * But also, we don't want to add the default route to priv->ip4_config, + * because the default route from the setting might not be the same that + * NMDefaultRouteManager eventually configures (because the it might + * tweak the effective metric). + */ + if ( !assumed + && nm_default_route_manager_ip4_connection_has_default_route (nm_default_route_manager_get (), connection)) { + guint32 gateway = 0; + + priv->default_route.v4_is_assumed = FALSE; + if ( (!commit && priv->ext_ip4_config_had_any_addresses) + || ( commit && nm_ip4_config_get_num_addresses (composite))) { + /* For managed interfaces, we can only configure a gateway, if either the external config indicates + * that we already have addresses, or if we are about to commit any addresses. + * Otherwise adding a default route will fail, because NMDefaultRouteManager does not add any + * addresses for the route. */ + gateway = nm_ip4_config_get_gateway (composite); + if ( gateway + || nm_device_get_device_type (self) == NM_DEVICE_TYPE_MODEM) { + memset (route, 0, sizeof (*route)); + route->source = NM_IP_CONFIG_SOURCE_USER; + route->gateway = gateway; + route->metric = default_route_metric; + route->mss = nm_ip4_config_get_mss (composite); + priv->default_route.v4_has = TRUE; + + if ( gateway + && !nm_ip4_config_get_subnet_for_host (composite, gateway) + && !nm_ip4_config_get_direct_route_for_host (composite, gateway)) { + /* add a direct route to the gateway */ + NMPlatformIP4Route r = *route; + + r.network = gateway; + r.plen = 32; + r.gateway = 0; + nm_ip4_config_add_route (composite, &r); + } + } + } + } else { + /* For interfaces that are assumed and that have no default-route by configuration, we assume + * the default connection and pick up whatever is configured. */ + priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) route); + } } /* Allow setting MTU etc */ @@ -2649,7 +2978,7 @@ ip4_config_merge_and_apply (NMDevice *self, NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, composite); } - success = nm_device_set_ip4_config (self, composite, commit, out_reason); + success = nm_device_set_ip4_config (self, composite, default_route_metric, commit, out_reason); g_object_unref (composite); return success; } @@ -2662,8 +2991,7 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config) g_return_if_fail (config != NULL); if (!ip4_config_merge_and_apply (self, config, TRUE, &reason)) { - nm_log_warn (LOGD_DHCP4, "(%s): failed to update IPv4 config for DHCP change.", - nm_device_get_ip_iface (self)); + _LOGW (LOGD_DHCP4, "failed to update IPv4 config for DHCP change."); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); } else { /* Notify dispatcher scripts of new DHCP4 config */ @@ -2677,94 +3005,93 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config) } static void -dhcp4_fail (NMDevice *device, gboolean timeout) +dhcp4_fail (NMDevice *self, gboolean timeout) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - - nm_dhcp4_config_reset (priv->dhcp4_config); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + dhcp4_cleanup (self, TRUE, FALSE); if (timeout || (priv->ip4_state == IP_CONF)) - nm_device_activate_schedule_ip4_config_timeout (device); - else if (priv->ip4_state == IP_FAIL) - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + nm_device_activate_schedule_ip4_config_timeout (self); + else if (priv->ip4_state == IP_DONE) + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + else + g_warn_if_reached (); +} + +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, +dhcp4_state_changed (NMDhcpClient *client, + NMDhcpState state, + NMIP4Config *ip4_config, + GHashTable *options, gpointer user_data) { - NMDevice *device = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - NMIP4Config *config; + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == FALSE); + g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config)); - nm_log_dbg (LOGD_DHCP4, "(%s): new DHCPv4 client state %d", - nm_device_get_iface (device), state); + _LOGD (LOGD_DHCP4, "new DHCPv4 client state %d", state); switch (state) { - case DHC_BOUND4: /* lease obtained */ - case DHC_RENEW4: /* lease renewed */ - case DHC_REBOOT: /* have valid lease, but now obtained a different one */ - case DHC_REBIND4: /* new, different lease */ - config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE); - if (!config) { - nm_log_warn (LOGD_DHCP4, "(%s): failed to get IPv4 config in response to DHCP event.", - nm_device_get_ip_iface (device)); - nm_device_state_changed (device, + case NM_DHCP_STATE_BOUND: + if (!ip4_config) { + _LOGW (LOGD_DHCP4, "failed to get IPv4 config in response to DHCP event."); + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); break; } - /* Update the DHCP4 config object with new DHCP options */ - nm_dhcp4_config_reset (priv->dhcp4_config); - nm_dhcp_client_foreach_option (priv->dhcp4_client, - dhcp4_add_option_cb, - priv->dhcp4_config); - g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP4_CONFIG); + dhcp4_update_config (self, priv->dhcp4_config, options); if (priv->ip4_state == IP_CONF) - nm_device_activate_schedule_ip4_config_result (device, config); + nm_device_activate_schedule_ip4_config_result (self, ip4_config); else if (priv->ip4_state == IP_DONE) - dhcp4_lease_change (device, config); - g_object_unref (config); - + dhcp4_lease_change (self, ip4_config); break; - case DHC_TIMEOUT: /* timed out contacting DHCP server */ - dhcp4_fail (device, TRUE); + case NM_DHCP_STATE_TIMEOUT: + dhcp4_fail (self, TRUE); break; - case DHC_END: /* dhclient exited normally */ - case DHC_FAIL: /* all attempts to contact server timed out, sleeping */ - case DHC_ABEND: /* dhclient exited abnormally */ - /* dhclient quit and can't get/renew a lease; so kill the connection */ - dhcp4_fail (device, FALSE); + case NM_DHCP_STATE_EXPIRE: + /* Ignore expiry before we even have a lease (NAK, old lease, etc) */ + if (priv->ip4_state == IP_CONF) + break; + /* Fall through */ + case NM_DHCP_STATE_DONE: + case NM_DHCP_STATE_FAIL: + dhcp4_fail (self, FALSE); break; default: break; } } -static void -dhcp4_timeout (NMDHCPClient *client, gpointer user_data) -{ - NMDevice *device = NM_DEVICE (user_data); - - g_return_if_fail (nm_device_get_act_request (device) != NULL); - g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == FALSE); - - nm_dhcp_client_stop (client, FALSE); - dhcp4_fail (device, TRUE); -} - static NMActStageReturn dhcp4_start (NMDevice *self, NMConnection *connection, NMDeviceStateReason *reason) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMSettingIP4Config *s_ip4; + NMSettingIPConfig *s_ip4; + const guint8 *hw_addr; + size_t hw_addr_len = 0; GByteArray *tmp = NULL; s_ip4 = nm_connection_get_setting_ip4_config (connection); @@ -2774,21 +3101,26 @@ dhcp4_start (NMDevice *self, g_object_unref (priv->dhcp4_config); priv->dhcp4_config = nm_dhcp4_config_new (); - if (priv->hw_addr_len) { - tmp = g_byte_array_sized_new (priv->hw_addr_len); - g_byte_array_append (tmp, priv->hw_addr, priv->hw_addr_len); + hw_addr = nm_platform_link_get_address (nm_device_get_ip_ifindex (self), &hw_addr_len); + if (hw_addr_len) { + tmp = g_byte_array_sized_new (hw_addr_len); + g_byte_array_append (tmp, hw_addr, hw_addr_len); } /* Begin DHCP on the interface */ g_warn_if_fail (priv->dhcp4_client == NULL); priv->dhcp4_client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (), nm_device_get_ip_iface (self), + nm_device_get_ip_ifindex (self), tmp, nm_connection_get_uuid (connection), - nm_device_get_priority (self), - s_ip4, + 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_client_id (NM_SETTING_IP4_CONFIG (s_ip4)), priv->dhcp_timeout, - priv->dhcp_anycast_address); + priv->dhcp_anycast_address, + NULL); if (tmp) g_byte_array_free (tmp, TRUE); @@ -2802,10 +3134,6 @@ dhcp4_start (NMDevice *self, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (dhcp4_state_changed), self); - priv->dhcp4_timeout_sigid = g_signal_connect (priv->dhcp4_client, - NM_DHCP_CLIENT_SIGNAL_TIMEOUT, - G_CALLBACK (dhcp4_timeout), - self); nm_device_add_pending_action (self, PENDING_ACTION_DHCP4, TRUE); @@ -2823,8 +3151,7 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release) g_return_val_if_fail (priv->dhcp4_client != NULL, FALSE); - nm_log_info (LOGD_DHCP4, "(%s): DHCPv4 lease renewal requested", - nm_device_get_iface (self)); + _LOGI (LOGD_DHCP4, "DHCPv4 lease renewal requested"); /* Terminate old DHCP instance and release the old lease */ dhcp4_cleanup (self, TRUE, release); @@ -2849,20 +3176,20 @@ release_shared_ip (gpointer data) } static gboolean -reserve_shared_ip (NMSettingIP4Config *s_ip4, NMPlatformIP4Address *address) +reserve_shared_ip (NMDevice *self, NMSettingIPConfig *s_ip4, NMPlatformIP4Address *address) { if (G_UNLIKELY (shared_ips == NULL)) shared_ips = g_hash_table_new (g_direct_hash, g_direct_equal); memset (address, 0, sizeof (*address)); - if (s_ip4 && nm_setting_ip4_config_get_num_addresses (s_ip4)) { + if (s_ip4 && nm_setting_ip_config_get_num_addresses (s_ip4)) { /* Use the first user-supplied address */ - NMIP4Address *user = nm_setting_ip4_config_get_address (s_ip4, 0); + NMIPAddress *user = nm_setting_ip_config_get_address (s_ip4, 0); g_assert (user); - address->address = nm_ip4_address_get_address (user); - address->plen = nm_ip4_address_get_prefix (user); + nm_ip_address_get_address_binary (user, &address->address); + address->plen = 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 */ @@ -2871,7 +3198,7 @@ reserve_shared_ip (NMSettingIP4Config *s_ip4, NMPlatformIP4Address *address) while (g_hash_table_lookup (shared_ips, GUINT_TO_POINTER (start + count))) { count += ntohl (0x100); if (count > ntohl (0xFE00)) { - nm_log_err (LOGD_SHARING, "ran out of shared IP addresses!"); + _LOGE (LOGD_SHARING, "ran out of shared IP addresses!"); return FALSE; } } @@ -2894,13 +3221,13 @@ shared4_new_config (NMDevice *self, NMConnection *connection, NMDeviceStateReaso g_return_val_if_fail (self != NULL, NULL); - if (!reserve_shared_ip (nm_connection_get_setting_ip4_config (connection), &address)) { + if (!reserve_shared_ip (self, nm_connection_get_setting_ip4_config (connection), &address)) { *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; return NULL; } config = nm_ip4_config_new (); - address.source = NM_PLATFORM_SOURCE_SHARED; + address.source = NM_IP_CONFIG_SOURCE_SHARED; nm_ip4_config_add_address (config, &address); /* Remove the address lock when the object gets disposed */ @@ -2914,7 +3241,79 @@ shared4_new_config (NMDevice *self, NMConnection *connection, NMDeviceStateReaso /*********************************************/ static gboolean -have_any_ready_slaves (NMDevice *device, const GSList *slaves) +connection_ip4_method_requires_carrier (NMConnection *connection, + gboolean *out_ip4_enabled) +{ + const char *method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + static const char *ip4_carrier_methods[] = { + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL, + NULL + }; + + if (out_ip4_enabled) + *out_ip4_enabled = !!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED); + return _nm_utils_string_in_list (method, ip4_carrier_methods); +} + +static gboolean +connection_ip6_method_requires_carrier (NMConnection *connection, + gboolean *out_ip6_enabled) +{ + const char *method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + static const char *ip6_carrier_methods[] = { + NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_DHCP, + NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL, + NULL + }; + + if (out_ip6_enabled) + *out_ip6_enabled = !!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE); + return _nm_utils_string_in_list (method, ip6_carrier_methods); +} + +static gboolean +connection_requires_carrier (NMConnection *connection) +{ + NMSettingIPConfig *s_ip4, *s_ip6; + gboolean ip4_carrier_wanted, ip6_carrier_wanted; + gboolean ip4_used = FALSE, ip6_used = FALSE; + + ip4_carrier_wanted = connection_ip4_method_requires_carrier (connection, &ip4_used); + if (ip4_carrier_wanted) { + /* If IPv4 wants a carrier and cannot fail, the whole connection + * requires a carrier regardless of the IPv6 method. + */ + s_ip4 = nm_connection_get_setting_ip4_config (connection); + if (s_ip4 && !nm_setting_ip_config_get_may_fail (s_ip4)) + return TRUE; + } + + ip6_carrier_wanted = connection_ip6_method_requires_carrier (connection, &ip6_used); + if (ip6_carrier_wanted) { + /* If IPv6 wants a carrier and cannot fail, the whole connection + * requires a carrier regardless of the IPv4 method. + */ + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if (s_ip6 && !nm_setting_ip_config_get_may_fail (s_ip6)) + return TRUE; + } + + /* If an IP version wants a carrier and and the other IP version isn't + * used, the connection requires carrier since it will just fail without one. + */ + if (ip4_carrier_wanted && !ip6_used) + return TRUE; + if (ip6_carrier_wanted && !ip4_used) + return TRUE; + + /* If both want a carrier, the whole connection wants a carrier */ + return ip4_carrier_wanted && ip6_carrier_wanted; +} + +static gboolean +have_any_ready_slaves (NMDevice *self, const GSList *slaves) { const GSList *iter; @@ -2955,16 +3354,11 @@ act_stage3_ip4_config_start (NMDevice *self, connection = nm_device_get_connection (self); g_assert (connection); - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if (priv->master) - g_assert_cmpstr (method, ==, NM_SETTING_IP4_CONFIG_METHOD_DISABLED); - - if ( strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) != 0 + if ( connection_ip4_method_requires_carrier (connection, NULL) && priv->is_master && !priv->carrier) { - nm_log_info (LOGD_IP4 | LOGD_DEVICE, - "(%s): IPv4 config waiting until carrier is on", - nm_device_get_ip_iface (self)); + _LOGI (LOGD_IP4 | LOGD_DEVICE, + "IPv4 config waiting until carrier is on"); return NM_ACT_STAGE_RETURN_WAIT; } @@ -2977,13 +3371,14 @@ act_stage3_ip4_config_start (NMDevice *self, g_slist_free (slaves); if (ready_slaves == FALSE) { - nm_log_info (LOGD_DEVICE | LOGD_IP4, - "(%s): IPv4 config waiting until slaves are ready", - nm_device_get_ip_iface (self)); + _LOGI (LOGD_DEVICE | LOGD_IP4, + "IPv4 config waiting until slaves are ready"); return NM_ACT_STAGE_RETURN_WAIT; } } + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + /* Start IPv4 addressing based on the method requested */ if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) ret = dhcp4_start (self, connection, reason); @@ -3004,10 +3399,8 @@ act_stage3_ip4_config_start (NMDevice *self, } else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) { /* Nothing to do... */ ret = NM_ACT_STAGE_RETURN_STOP; - } else { - nm_log_warn (LOGD_IP4, "(%s): unhandled IPv4 config method '%s'; will fail", - nm_device_get_ip_iface (self), method); - } + } else + _LOGW (LOGD_IP4, "unhandled IPv4 config method '%s'; will fail", method); return ret; } @@ -3029,33 +3422,20 @@ dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release) priv->dhcp6_state_sigid = 0; } - if (priv->dhcp6_timeout_sigid) { - g_signal_handler_disconnect (priv->dhcp6_client, priv->dhcp6_timeout_sigid); - priv->dhcp6_timeout_sigid = 0; - } - - nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE); - if (stop) nm_dhcp_client_stop (priv->dhcp6_client, release); g_clear_object (&priv->dhcp6_client); } + nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE); + if (priv->dhcp6_config) { g_clear_object (&priv->dhcp6_config); g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG); } } -static void -dhcp6_add_option_cb (gpointer key, gpointer value, gpointer user_data) -{ - nm_dhcp6_config_add_option (NM_DHCP6_CONFIG (user_data), - (const char *) key, - (const char *) value); -} - static gboolean ip6_config_merge_and_apply (NMDevice *self, gboolean commit, @@ -3080,109 +3460,216 @@ ip6_config_merge_and_apply (NMDevice *self, if (priv->ext_ip6_config) nm_ip6_config_merge (composite, priv->ext_ip6_config); - /* Merge user overrides into the composite config */ + /* Merge WWAN config *last* to ensure modem-given settings overwrite + * any external stuff set by pppd or other scripts. + */ + if (priv->wwan_ip6_config) + nm_ip6_config_merge (composite, priv->wwan_ip6_config); + + /* Merge user overrides into the composite config. Generated+assumed + * connections come from the system not the user and merging them would + * be redundant, so don't bother. + */ connection = nm_device_get_connection (self); + priv->default_route.v6_has = FALSE; + priv->default_route.v6_is_assumed = TRUE; if (connection) { - nm_ip6_config_merge_setting (composite, - nm_connection_get_setting_ip6_config (connection), - nm_device_get_priority (self)); + gboolean assumed = nm_device_uses_assumed_connection (self); + NMPlatformIP6Route *route = &priv->default_route.v6; + + if (!nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection))) { + nm_ip6_config_merge_setting (composite, + nm_connection_get_setting_ip6_config (connection), + nm_device_get_ip6_route_metric (self)); + } + + /* Add the default route. + * + * We keep track of the default route of a device in a private field. + * NMDevice needs to know the default route at this point, because the gateway + * might require a direct route (see below). + * + * But also, we don't want to add the default route to priv->ip4_config, + * because the default route from the setting might not be the same that + * NMDefaultRouteManager eventually configures (because the it might + * tweak the effective metric). + */ + if ( !assumed + && nm_default_route_manager_ip6_connection_has_default_route (nm_default_route_manager_get (), connection)) { + const struct in6_addr *gateway = NULL; + + priv->default_route.v6_is_assumed = FALSE; + if ( (!commit && priv->ext_ip6_config_had_any_addresses) + || ( commit && nm_ip6_config_get_num_addresses (composite))) { + /* For managed interfaces, we can only configure a gateway, if either the external config indicates + * that we already have addresses, or if we are about to commit any addresses. + * Otherwise adding a default route will fail, because NMDefaultRouteManager does not add any + * addresses for the route. */ + gateway = nm_ip6_config_get_gateway (composite); + if (gateway) { + memset (route, 0, sizeof (*route)); + route->source = NM_IP_CONFIG_SOURCE_USER; + route->gateway = *gateway; + route->metric = nm_device_get_ip6_route_metric (self); + route->mss = nm_ip6_config_get_mss (composite); + priv->default_route.v6_has = TRUE; + + if ( gateway + && !nm_ip6_config_get_subnet_for_host (composite, gateway) + && !nm_ip6_config_get_direct_route_for_host (composite, gateway)) { + /* add a direct route to the gateway */ + NMPlatformIP6Route r = *route; + + r.network = *gateway; + r.plen = 128; + r.gateway = in6addr_any; + nm_ip6_config_add_route (composite, &r); + } + } + } + } else { + /* For interfaces that are assumed and that have no default-route by configuration, we assume + * the default connection and pick up whatever is configured. */ + priv->default_route.v6_has = _device_get_default_route_from_platform (self, AF_INET6, (NMPlatformIPRoute *) route); + } } nm_ip6_config_addresses_sort (composite, priv->rdisc ? priv->rdisc_use_tempaddr : NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + /* Allow setting MTU etc */ + if (commit) { + if (NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit) + NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit (self, composite); + } + success = nm_device_set_ip6_config (self, composite, commit, out_reason); g_object_unref (composite); return success; } static void -dhcp6_lease_change (NMDevice *device) +dhcp6_lease_change (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; if (priv->dhcp6_ip6_config == NULL) { - nm_log_warn (LOGD_DHCP6, "(%s): failed to get DHCPv6 config for rebind", - nm_device_get_ip_iface (device)); - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + _LOGW (LOGD_DHCP6, "failed to get DHCPv6 config for rebind"); + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); return; } g_assert (priv->dhcp6_client); /* sanity check */ - connection = nm_device_get_connection (device); + connection = nm_device_get_connection (self); g_assert (connection); /* Apply the updated config */ - if (ip6_config_merge_and_apply (device, TRUE, &reason) == FALSE) { - nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv6 config in response to DHCP event.", - nm_device_get_ip_iface (device)); - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); + if (ip6_config_merge_and_apply (self, TRUE, &reason) == FALSE) { + _LOGW (LOGD_DHCP6, "failed to update IPv6 config in response to DHCP event."); + 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, device, NULL, NULL, NULL); + nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE, connection, self, NULL, NULL, NULL); } } static void -dhcp6_fail (NMDevice *device, gboolean timeout) +dhcp6_fail (NMDevice *self, gboolean timeout) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + dhcp6_cleanup (self, TRUE, FALSE); + + if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) { + if (timeout || (priv->ip6_state == IP_CONF)) + nm_device_activate_schedule_ip6_config_timeout (self); + else if (priv->ip6_state == IP_DONE) + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + else + g_warn_if_reached (); + } else { + /* not a hard failure; just live with the RA info */ + if (priv->ip6_state == IP_CONF) + nm_device_activate_schedule_ip6_config_result (self); + } +} - nm_dhcp6_config_reset (priv->dhcp6_config); +static void +dhcp6_timeout (NMDevice *self, NMDhcpClient *client) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (timeout || (priv->ip6_state == IP_CONF)) - nm_device_activate_schedule_ip6_config_timeout (device); - else if (priv->ip6_state == IP_FAIL) - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) + dhcp6_fail (self, TRUE); + else { + /* not a hard failure; just live with the RA info */ + dhcp6_cleanup (self, TRUE, FALSE); + if (priv->ip6_state == IP_CONF) + nm_device_activate_schedule_ip6_config_result (self); + } } static void -dhcp6_state_changed (NMDHCPClient *client, - NMDHCPState state, +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, + GHashTable *options, gpointer user_data) { - NMDevice *device = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE); + g_return_if_fail (!ip6_config || NM_IS_IP6_CONFIG (ip6_config)); - nm_log_dbg (LOGD_DHCP6, "(%s): new DHCPv6 client state %d", - nm_device_get_iface (device), state); + _LOGD (LOGD_DHCP6, "new DHCPv6 client state %d", state); switch (state) { - case DHC_BOUND6: - case DHC_RENEW6: /* lease renewed */ - case DHC_REBOOT: /* have valid lease, but now obtained a different one */ - case DHC_REBIND6: /* new, different lease */ + case NM_DHCP_STATE_BOUND: g_clear_object (&priv->dhcp6_ip6_config); - priv->dhcp6_ip6_config = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE); - - /* Update the DHCP6 config object with new DHCP options */ - nm_dhcp6_config_reset (priv->dhcp6_config); - if (priv->dhcp6_ip6_config) { - nm_dhcp_client_foreach_option (priv->dhcp6_client, - dhcp6_add_option_cb, - priv->dhcp6_config); + if (ip6_config) { + priv->dhcp6_ip6_config = g_object_ref (ip6_config); + dhcp6_update_config (self, priv->dhcp6_config, options); } - g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP6_CONFIG); if (priv->ip6_state == IP_CONF) { if (priv->dhcp6_ip6_config == NULL) { /* FIXME: Initial DHCP failed; should we fail IPv6 entirely then? */ - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DHCP_FAILED); + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DHCP_FAILED); break; } - nm_device_activate_schedule_ip6_config_result (device); + nm_device_activate_schedule_ip6_config_result (self); } else if (priv->ip6_state == IP_DONE) - dhcp6_lease_change (device); + dhcp6_lease_change (self); break; - case DHC_TIMEOUT: /* timed out contacting DHCP server */ - dhcp6_fail (device, TRUE); + case NM_DHCP_STATE_TIMEOUT: + dhcp6_timeout (self, client); break; - case DHC_END: /* dhclient exited normally */ + case NM_DHCP_STATE_EXPIRE: + /* Ignore expiry before we even have a lease (NAK, old lease, etc) */ + if (priv->ip6_state != IP_CONF) + dhcp6_fail (self, FALSE); + break; + case NM_DHCP_STATE_DONE: /* In IPv6 info-only mode, the client doesn't handle leases so it * may exit right after getting a response from the server. That's * normal. In that case we just ignore the exit. @@ -3190,83 +3677,45 @@ dhcp6_state_changed (NMDHCPClient *client, if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_OTHERCONF) break; /* Otherwise, fall through */ - case DHC_FAIL: /* all attempts to contact server timed out, sleeping */ - case DHC_ABEND: /* dhclient exited abnormally */ - /* dhclient quit and can't get/renew a lease; so kill the connection */ - dhcp6_fail (device, FALSE); + case NM_DHCP_STATE_FAIL: + dhcp6_fail (self, FALSE); break; default: break; } } -static void -dhcp6_timeout (NMDHCPClient *client, gpointer user_data) -{ - NMDevice *device = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - - g_return_if_fail (nm_device_get_act_request (device) != NULL); - g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE); - - nm_dhcp_client_stop (client, FALSE); - if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) - dhcp6_fail (device, TRUE); - else { - /* not a hard failure; just live with the RA info */ - nm_dhcp6_config_reset (priv->dhcp6_config); - if (priv->dhcp6_ip6_config) - g_object_unref (priv->dhcp6_ip6_config); - priv->dhcp6_ip6_config = NULL; - - if (priv->ip6_state == IP_CONF) - nm_device_activate_schedule_ip6_config_result (device); - } -} - -static NMActStageReturn -dhcp6_start (NMDevice *self, - NMConnection *connection, - guint32 dhcp_opt, - NMDeviceStateReason *reason) +static gboolean +dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) { - NMSettingIP6Config *s_ip6; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + NMSettingIPConfig *s_ip6; GByteArray *tmp = NULL; + const guint8 *hw_addr; + size_t hw_addr_len = 0; - if (!connection) { - connection = nm_device_get_connection (self); - g_assert (connection); - } - - /* Begin a DHCP transaction on the interface */ - - /* Clear old exported DHCP options */ - if (priv->dhcp6_config) - g_object_unref (priv->dhcp6_config); - priv->dhcp6_config = nm_dhcp6_config_new (); - - g_warn_if_fail (priv->dhcp6_ip6_config == NULL); - if (priv->dhcp6_ip6_config) { - g_object_unref (priv->dhcp6_ip6_config); - priv->dhcp6_ip6_config = NULL; - } + g_assert (connection); + s_ip6 = nm_connection_get_setting_ip6_config (connection); + g_assert (s_ip6); - if (priv->hw_addr_len) { - tmp = g_byte_array_sized_new (priv->hw_addr_len); - g_byte_array_append (tmp, priv->hw_addr, priv->hw_addr_len); + hw_addr = nm_platform_link_get_address (nm_device_get_ip_ifindex (self), &hw_addr_len); + if (hw_addr_len) { + tmp = g_byte_array_sized_new (hw_addr_len); + g_byte_array_append (tmp, hw_addr, hw_addr_len); } 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, nm_connection_get_uuid (connection), - nm_device_get_priority (self), - nm_connection_get_setting_ip6_config (connection), + nm_device_get_ip6_route_metric (self), + nm_setting_ip_config_get_dhcp_send_hostname (s_ip6), + nm_setting_ip_config_get_dhcp_hostname (s_ip6), priv->dhcp_timeout, priv->dhcp_anycast_address, - (dhcp_opt == NM_RDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE); + (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE, + nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6))); if (tmp) g_byte_array_free (tmp, TRUE); @@ -3275,65 +3724,85 @@ dhcp6_start (NMDevice *self, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (dhcp6_state_changed), self); - priv->dhcp6_timeout_sigid = g_signal_connect (priv->dhcp6_client, - NM_DHCP_CLIENT_SIGNAL_TIMEOUT, - G_CALLBACK (dhcp6_timeout), - self); + } - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (!nm_setting_ip6_config_get_may_fail (s_ip6) || - !strcmp (nm_setting_ip6_config_get_method (s_ip6), NM_SETTING_IP6_CONFIG_METHOD_DHCP)) - nm_device_add_pending_action (self, PENDING_ACTION_DHCP6, TRUE); + return !!priv->dhcp6_client; +} - /* DHCP devices will be notified by the DHCP manager when stuff happens */ - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else { +static gboolean +dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMConnection *connection; + NMSettingIPConfig *s_ip6; + + g_clear_object (&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); + + connection = nm_device_get_connection (self); + g_assert (connection); + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if (!nm_setting_ip_config_get_may_fail (s_ip6) || + !strcmp (nm_setting_ip_config_get_method (s_ip6), NM_SETTING_IP6_CONFIG_METHOD_DHCP)) + nm_device_add_pending_action (self, PENDING_ACTION_DHCP6, TRUE); + + if (wait_for_ll) { + NMActStageReturn ret; + + /* ensure link local is ready... */ + ret = linklocal6_start (self); + if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { + /* success; wait for the LL address to show up */ + return TRUE; + } + + /* success; already have the LL address; kick off DHCP */ + g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); + } + + if (!dhcp6_start_with_link_ready (self, connection)) { *reason = NM_DEVICE_STATE_REASON_DHCP_START_FAILED; - ret = NM_ACT_STAGE_RETURN_FAILURE; + return FALSE; } - return ret; + return TRUE; } gboolean nm_device_dhcp6_renew (NMDevice *self, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActStageReturn ret; - NMDeviceStateReason reason; - NMConnection *connection; g_return_val_if_fail (priv->dhcp6_client != NULL, FALSE); - nm_log_info (LOGD_DHCP6, "(%s): DHCPv6 lease renewal requested", - nm_device_get_iface (self)); + _LOGI (LOGD_DHCP6, "DHCPv6 lease renewal requested"); /* Terminate old DHCP instance and release the old lease */ dhcp6_cleanup (self, TRUE, release); - connection = nm_device_get_connection (self); - g_assert (connection); - /* Start DHCP again on the interface */ - ret = dhcp6_start (self, connection, priv->dhcp6_mode, &reason); - - return (ret != NM_ACT_STAGE_RETURN_FAILURE); + return dhcp6_start (self, FALSE, NULL); } /******************************************/ static gboolean -linklocal6_config_is_ready (const NMIP6Config *ip6_config) +have_ip6_address (const NMIP6Config *ip6_config, gboolean linklocal) { - int i; + guint i; if (!ip6_config) return FALSE; + linklocal = !!linklocal; + for (i = 0; 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) && + if ((IN6_IS_ADDR_LINKLOCAL (&addr->address) == linklocal) && !(addr->flags & IFA_F_TENTATIVE)) return TRUE; } @@ -3359,8 +3828,7 @@ linklocal6_timeout_cb (gpointer user_data) linklocal6_cleanup (self); - nm_log_dbg (LOGD_DEVICE, "[%s] linklocal6: waiting for link-local addresses failed due to timeout", - nm_device_get_iface (self)); + _LOGD (LOGD_DEVICE, "linklocal6: waiting for link-local addresses failed due to timeout"); nm_device_activate_schedule_ip6_config_timeout (self); return G_SOURCE_REMOVE; @@ -3374,7 +3842,7 @@ linklocal6_complete (NMDevice *self) const char *method; g_assert (priv->linklocal6_timeout_id); - g_assert (linklocal6_config_is_ready (priv->ip6_config)); + g_assert (have_ip6_address (priv->ip6_config, TRUE)); linklocal6_cleanup (self); @@ -3383,17 +3851,70 @@ linklocal6_complete (NMDevice *self) method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - nm_log_dbg (LOGD_DEVICE, "[%s] linklocal6: waiting for link-local addresses successful, continue with method %s", - nm_device_get_iface (self), method); + _LOGD (LOGD_DEVICE, "linklocal6: waiting for link-local addresses successful, continue with method %s", method); - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) - addrconf6_start_with_link_ready (self); - else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) + if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) { + if (!addrconf6_start_with_link_ready (self)) { + /* Time out IPv6 instead of failing the entire activation */ + nm_device_activate_schedule_ip6_config_timeout (self); + } + } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) { + if (!dhcp6_start_with_link_ready (self, connection)) { + /* Time out IPv6 instead of failing the entire activation */ + nm_device_activate_schedule_ip6_config_timeout (self); + } + } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) nm_device_activate_schedule_ip6_config_result (self); else g_return_if_fail (FALSE); } +static void +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; + + if (priv->nm_ipv6ll == FALSE) + return; + + if (priv->ip6_config) { + n = nm_ip6_config_get_num_addresses (priv->ip6_config); + for (i = 0; i < n; i++) { + const NMPlatformIP6Address *addr; + + addr = nm_ip6_config_get_address (priv->ip6_config, i); + if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) { + /* 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)); + if (!nm_platform_ip6_address_add (ip_ifindex, + lladdr, + in6addr_any, + 64, + NM_PLATFORM_LIFETIME_PERMANENT, + NM_PLATFORM_LIFETIME_PERMANENT, + 0)) { + _LOGW (LOGD_IP6, "failed to add IPv6 link-local address %s", + nm_utils_inet6_ntop (&lladdr, NULL)); + } +} + static NMActStageReturn linklocal6_start (NMDevice *self) { @@ -3403,15 +3924,16 @@ linklocal6_start (NMDevice *self) linklocal6_cleanup (self); - if (linklocal6_config_is_ready (priv->ip6_config)) + if (have_ip6_address (priv->ip6_config, TRUE)) return NM_ACT_STAGE_RETURN_SUCCESS; connection = nm_device_get_connection (self); g_assert (connection); method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - nm_log_dbg (LOGD_DEVICE, "[%s] linklocal6: starting IPv6 with method '%s', but the device has no link-local addresses configured. Wait.", - nm_device_get_iface (self), method); + _LOGD (LOGD_DEVICE, "linklocal6: starting IPv6 with method '%s', but the device has no link-local addresses configured. Wait.", method); + + check_and_add_ipv6ll_addr (self); priv->linklocal6_timeout_id = g_timeout_add_seconds (5, linklocal6_timeout_cb, self); @@ -3468,12 +3990,10 @@ print_support_extended_ifa_flags (NMSettingIP6ConfigPrivacy use_tempaddr) } static void -rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device) +rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - NMConnection *connection; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int i; - NMDeviceStateReason reason; static int system_support = -1; guint ifa_flags = 0x00; @@ -3499,8 +4019,6 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device } g_return_if_fail (priv->act_request); - connection = nm_device_get_connection (device); - g_assert (connection); if (!priv->ac_ip6_config) priv->ac_ip6_config = nm_ip6_config_new (); @@ -3536,7 +4054,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device address.preferred = discovered_address->preferred; if (address.preferred > address.lifetime) address.preferred = address.lifetime; - address.source = NM_PLATFORM_SOURCE_RDISC; + address.source = NM_IP_CONFIG_SOURCE_RDISC; address.flags = ifa_flags; nm_ip6_config_add_address (priv->ac_ip6_config, &address); @@ -3560,8 +4078,8 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device route.network = discovered_route->network; route.plen = discovered_route->plen; route.gateway = discovered_route->gateway; - route.source = NM_PLATFORM_SOURCE_RDISC; - route.metric = nm_device_get_priority (device); + route.source = NM_IP_CONFIG_SOURCE_RDISC; + route.metric = nm_device_get_ip6_route_metric (self); nm_ip6_config_add_route (priv->ac_ip6_config, &route); } @@ -3591,39 +4109,99 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *device } if (changed & NM_RDISC_CONFIG_DHCP_LEVEL) { - dhcp6_cleanup (device, TRUE, TRUE); + dhcp6_cleanup (self, TRUE, TRUE); priv->dhcp6_mode = rdisc->dhcp_level; - - switch (priv->dhcp6_mode) { - case NM_RDISC_DHCP_LEVEL_NONE: - break; - default: - nm_log_info (LOGD_DEVICE | LOGD_DHCP6, - "Activation (%s) Stage 3 of 5 (IP Configure Start) starting DHCPv6" - " as requested by IPv6 router...", - priv->iface); - switch (dhcp6_start (device, connection, priv->dhcp6_mode, &reason)) { - case NM_ACT_STAGE_RETURN_SUCCESS: - g_warn_if_reached (); - break; - case NM_ACT_STAGE_RETURN_POSTPONE: - return; - default: - nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); - return; + if (priv->dhcp6_mode != NM_RDISC_DHCP_LEVEL_NONE) { + NMDeviceStateReason reason; + + _LOGI (LOGD_DEVICE | LOGD_DHCP6, + "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) + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); } + return; } } - if (changed & NM_RDISC_CONFIG_HOP_LIMIT) { + /* hop_limit == 0 is a special value "unspecified", so do not touch + * in this case */ + if (changed & NM_RDISC_CONFIG_HOP_LIMIT && rdisc->hop_limit > 0) { char val[16]; g_snprintf (val, sizeof (val), "%d", rdisc->hop_limit); - nm_device_ipv6_sysctl_set (device, "hop_limit", val); + nm_device_ipv6_sysctl_set (self, "hop_limit", val); } - nm_device_activate_schedule_ip6_config_result (device); + if (changed & NM_RDISC_CONFIG_MTU) { + char val[16]; + + g_snprintf (val, sizeof (val), "%d", rdisc->mtu); + nm_device_ipv6_sysctl_set (self, "mtu", val); + } + + nm_device_activate_schedule_ip6_config_result (self); +} + +static void +rdisc_ra_timeout (NMRDisc *rdisc, NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + /* We don't want to stop listening for router advertisements completely, + * but instead let device activation continue activating. If an RA + * shows up later, we'll use it as long as the device is not disconnected. + */ + + _LOGD (LOGD_IP6, "timed out waiting for IPv6 router advertisement"); + if (priv->ip6_state == IP_CONF) { + /* If RA is our only source of addressing information and we don't + * ever receive one, then time out IPv6. But if there is other + * IPv6 configuration, like manual IPv6 addresses or external IPv6 + * config, consider that sufficient for IPv6 success. + */ + if (have_ip6_address (priv->ip6_config, FALSE)) + nm_device_activate_schedule_ip6_config_result (self); + else + nm_device_activate_schedule_ip6_config_timeout (self); + } +} + +static gboolean +addrconf6_start_with_link_ready (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMUtilsIPv6IfaceId iid; + + g_assert (priv->rdisc); + + if (!nm_device_get_ip_iface_identifier (self, &iid)) { + _LOGW (LOGD_IP6, "failed to get interface identifier; IPv6 cannot continue"); + return FALSE; + } + nm_rdisc_set_iid (priv->rdisc, iid); + + /* Apply any manual configuration before starting RA */ + if (!ip6_config_merge_and_apply (self, TRUE, NULL)) + _LOGW (LOGD_IP6, "failed to apply manual IPv6 configuration"); + + nm_device_ipv6_sysctl_set (self, "accept_ra", "1"); + nm_device_ipv6_sysctl_set (self, "accept_ra_defrtr", "0"); + nm_device_ipv6_sysctl_set (self, "accept_ra_pinfo", "0"); + nm_device_ipv6_sysctl_set (self, "accept_ra_rtr_pref", "0"); + + priv->rdisc_changed_id = g_signal_connect (priv->rdisc, + NM_RDISC_CONFIG_CHANGED, + G_CALLBACK (rdisc_config_changed), + self); + priv->rdisc_timeout_id = g_signal_connect (priv->rdisc, + NM_RDISC_RA_TIMEOUT, + G_CALLBACK (rdisc_ra_timeout), + self); + nm_rdisc_start (priv->rdisc); + return TRUE; } static gboolean @@ -3645,46 +4223,26 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self), ip_iface); if (!priv->rdisc) { - nm_log_err (LOGD_IP6, "(%s): failed to start router discovery.", ip_iface); + _LOGE (LOGD_IP6, "failed to start router discovery (%s)", ip_iface); return FALSE; } priv->rdisc_use_tempaddr = use_tempaddr; print_support_extended_ifa_flags (use_tempaddr); - if (!nm_setting_ip6_config_get_may_fail (nm_connection_get_setting_ip6_config (connection))) + 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); /* ensure link local is ready... */ ret = linklocal6_start (self); - if (ret == NM_ACT_STAGE_RETURN_SUCCESS) - addrconf6_start_with_link_ready (self); - else - g_return_val_if_fail (ret == NM_ACT_STAGE_RETURN_POSTPONE, TRUE); - - return TRUE; -} - -static void -addrconf6_start_with_link_ready (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - g_assert (priv->rdisc); - - /* FIXME: what if interface has no lladdr, like PPP? */ - if (priv->hw_addr_len) - nm_rdisc_set_lladdr (priv->rdisc, (const char *) priv->hw_addr, priv->hw_addr_len); - - nm_device_ipv6_sysctl_set (self, "accept_ra", "1"); - nm_device_ipv6_sysctl_set (self, "accept_ra_defrtr", "0"); - nm_device_ipv6_sysctl_set (self, "accept_ra_pinfo", "0"); - nm_device_ipv6_sysctl_set (self, "accept_ra_rtr_pref", "0"); - - priv->rdisc_config_changed_sigid = g_signal_connect (priv->rdisc, NM_RDISC_CONFIG_CHANGED, - G_CALLBACK (rdisc_config_changed), self); + if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { + /* success; wait for the LL address to show up */ + return TRUE; + } - nm_rdisc_start (priv->rdisc); + /* success; already have the LL address; kick off router discovery */ + g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); + return addrconf6_start_with_link_ready (self); } static void @@ -3692,10 +4250,14 @@ addrconf6_cleanup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->rdisc_config_changed_sigid) { - g_signal_handler_disconnect (priv->rdisc, - priv->rdisc_config_changed_sigid); - priv->rdisc_config_changed_sigid = 0; + 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_device_remove_pending_action (self, PENDING_ACTION_AUTOCONF6, FALSE); @@ -3744,8 +4306,53 @@ restore_ip6_properties (NMDevice *self) gpointer key, value; g_hash_table_iter_init (&iter, priv->ip6_saved_properties); - while (g_hash_table_iter_next (&iter, &key, &value)) + while (g_hash_table_iter_next (&iter, &key, &value)) { + /* Don't touch "disable_ipv6" if we're doing userland IPv6LL */ + if (priv->nm_ipv6ll && strcmp (key, "disable_ipv6") == 0) + continue; nm_device_ipv6_sysctl_set (self, key, value); + } +} + +static inline void +set_disable_ipv6 (NMDevice *self, const char *value) +{ + /* We only touch disable_ipv6 when NM is not managing the IPv6LL address */ + if (NM_DEVICE_GET_PRIVATE (self)->nm_ipv6ll == FALSE) + nm_device_ipv6_sysctl_set (self, "disable_ipv6", value); +} + +static inline void +set_nm_ipv6ll (NMDevice *self, gboolean enable) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + int ifindex = nm_device_get_ip_ifindex (self); + const char *iface = nm_device_get_ip_iface (self); + char *value; + + if (!nm_platform_check_support_user_ipv6ll ()) + return; + + priv->nm_ipv6ll = enable; + if (ifindex > 0) { + const char *detail = enable ? "enable" : "disable"; + + _LOGD (LOGD_IP6, "will %s userland IPv6LL", detail); + if ( !nm_platform_link_set_user_ipv6ll_enabled (ifindex, enable) + && nm_platform_get_error () != NM_PLATFORM_ERROR_NOT_FOUND) + _LOGW (LOGD_IP6, "failed to %s userspace IPv6LL address handling", detail); + + if (enable) { + /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */ + value = nm_platform_sysctl_get (nm_utils_ip6_property_path (iface, "disable_ipv6")); + if (g_strcmp0 (value, "0") == 0) { + nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); + nm_device_ipv6_sysctl_set (self, "disable_ipv6", "0"); + } + g_free (value); + } + + } } static NMSettingIP6ConfigPrivacy @@ -3838,15 +4445,11 @@ act_stage3_ip6_config_start (NMDevice *self, connection = nm_device_get_connection (self); g_assert (connection); - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - if (priv->master) - g_assert_cmpstr (method, ==, NM_SETTING_IP6_CONFIG_METHOD_IGNORE); - - if ( strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) != 0 + if ( connection_ip4_method_requires_carrier (connection, NULL) && priv->is_master && !priv->carrier) { - nm_log_info (LOGD_IP6 | LOGD_DEVICE, - "(%s): IPv6 config waiting until carrier is on", ip_iface); + _LOGI (LOGD_IP6 | LOGD_DEVICE, + "IPv6 config waiting until carrier is on"); return NM_ACT_STAGE_RETURN_WAIT; } @@ -3859,23 +4462,41 @@ act_stage3_ip6_config_start (NMDevice *self, g_slist_free (slaves); if (ready_slaves == FALSE) { - nm_log_info (LOGD_DEVICE | LOGD_IP6, - "(%s): IPv6 config waiting until slaves are ready", - ip_iface); + _LOGI (LOGD_DEVICE | LOGD_IP6, + "IPv6 config waiting until slaves are ready"); return NM_ACT_STAGE_RETURN_WAIT; } } priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE; + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) { - if (!priv->master) + if (!priv->master) { + gboolean old_nm_ipv6ll = priv->nm_ipv6ll; + + /* When activating an IPv6 'ignore' connection we need to revert back + * to kernel IPv6LL, but the kernel won't actually assign an address + * to the interface until disable_ipv6 is bounced. + */ + set_nm_ipv6ll (self, FALSE); + if (old_nm_ipv6ll == TRUE) + nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); restore_ip6_properties (self); + } return NM_ACT_STAGE_RETURN_STOP; } + /* Any method past this point requires an IPv6LL address. Use NM-controlled + * IPv6LL if this is not an assumed connection, since assumed connections + * will already have IPv6 set up. + */ + if (!nm_device_uses_assumed_connection (self)) + set_nm_ipv6ll (self, TRUE); + /* Re-enable IPv6 on the interface */ - nm_device_ipv6_sysctl_set (self, "disable_ipv6", "0"); + set_disable_ipv6 (self, "0"); /* Enable/disable IPv6 Privacy Extensions. * If a global value is configured by sysadmin (e.g. /etc/sysctl.conf), @@ -3883,10 +4504,10 @@ act_stage3_ip6_config_start (NMDevice *self, */ ip6_privacy = ip6_use_tempaddr (); if (ip6_privacy == NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) { - NMSettingIP6Config *s_ip6 = nm_connection_get_setting_ip6_config (connection); + NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection); if (s_ip6) - ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (s_ip6); + ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)); } ip6_privacy = use_tempaddr_clamp (ip6_privacy); @@ -3905,17 +4526,19 @@ act_stage3_ip6_config_start (NMDevice *self, } } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) { priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_MANAGED; - ret = dhcp6_start (self, connection, priv->dhcp6_mode, reason); + if (!dhcp6_start (self, TRUE, reason)) { + /* IPv6 might be disabled; allow IPv4 to proceed */ + ret = NM_ACT_STAGE_RETURN_STOP; + } else + 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 (); g_assert (*out_config); ret = NM_ACT_STAGE_RETURN_SUCCESS; - } else { - nm_log_warn (LOGD_IP6, "(%s): unhandled IPv6 config method '%s'; will fail", - nm_device_get_ip_iface (self), method); - } + } else + _LOGW (LOGD_IP6, "unhandled IPv6 config method '%s'; will fail", method); /* Other methods (shared) aren't implemented yet */ @@ -4015,6 +4638,42 @@ 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 * * Begin automatic/manual IP configuration @@ -4025,7 +4684,6 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *iface; NMActiveConnection *master; NMDevice *master_device; @@ -4034,15 +4692,12 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data) priv->ip4_state = priv->ip6_state = IP_WAIT; - iface = nm_device_get_iface (self); - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) started...", iface); + _LOGI (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 */ - if (!nm_platform_link_is_up (nm_device_get_ip_ifindex (self))) { - nm_log_warn (LOGD_DEVICE, "(%s): interface %s not up for IP configuration", - iface, nm_device_get_ip_iface (self)); - } + if (!nm_platform_link_is_up (nm_device_get_ip_ifindex (self))) + _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); /* If the device is a slave, then we don't do any IP configuration but we * use the IP config stage to indicate to the master we're ready for @@ -4057,10 +4712,9 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data) if (master) { master_device = nm_active_connection_get_device (master); if (priv->ip4_state == IP_WAIT && priv->ip6_state == IP_WAIT) { - nm_log_info (LOGD_DEVICE, "Activation (%s) connection '%s' waiting on master '%s'", - nm_device_get_iface (self), - nm_connection_get_id (nm_device_get_connection (self)), - master_device ? nm_device_get_iface (master_device) : "(unknown)"); + _LOGI (LOGD_DEVICE, "Activation: connection '%s' waiting on master '%s'", + nm_connection_get_id (nm_device_get_connection (self)), + master_device ? nm_device_get_iface (master_device) : "(unknown)"); } goto out; } @@ -4073,13 +4727,10 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data) if (!nm_device_activate_stage3_ip6_start (self)) goto out; - if (priv->ip4_state == IP_FAIL && priv->ip6_state == IP_FAIL) { - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - } + nm_device_check_ip_failed (self, TRUE); out: - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) complete.", iface); + _LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) complete."); return FALSE; } @@ -4087,8 +4738,14 @@ out: static void fw_change_zone_cb (GError *error, gpointer user_data) { - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevice *self; + NMDevicePrivate *priv; + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_DEVICE (user_data); + priv = NM_DEVICE_GET_PRIVATE (self); priv->fw_call = NULL; @@ -4097,9 +4754,7 @@ fw_change_zone_cb (GError *error, gpointer user_data) } activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0); - - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) scheduled.", - nm_device_get_iface (self)); + _LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled."); } /* @@ -4120,15 +4775,24 @@ 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); g_assert (connection); s_con = nm_connection_get_setting_connection (connection); zone = nm_setting_connection_get_zone (s_con); - nm_log_dbg (LOGD_DEVICE, "Activation (%s) setting firewall zone '%s'", - nm_device_get_iface (self), zone ? zone : "default"); - priv->fw_call = nm_firewall_manager_add_or_change_zone (priv->fw_manager, + + 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); + _LOGI (LOGD_DEVICE, "Activation: Stage 3 of 5 (IP Configure Start) scheduled."); + 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, @@ -4139,7 +4803,7 @@ nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self) static NMActStageReturn act_stage4_ip4_config_timeout (NMDevice *self, NMDeviceStateReason *reason) { - if (nm_device_ip_config_should_fail (self, FALSE)) { + if (!get_ip_config_may_fail (self, AF_INET)) { *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; return NM_ACT_STAGE_RETURN_FAILURE; } @@ -4158,17 +4822,14 @@ nm_device_activate_ip4_config_timeout (gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *iface; NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, AF_INET); - iface = nm_device_get_iface (self); - nm_log_info (LOGD_DEVICE | LOGD_IP4, - "Activation (%s) Stage 4 of 5 (IPv4 Configure Timeout) started...", - iface); + _LOGI (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) @@ -4181,16 +4842,11 @@ nm_device_activate_ip4_config_timeout (gpointer user_data) priv->ip4_state = IP_FAIL; - /* If IPv4 failed and IPv6 failed, the activation fails */ - if (priv->ip6_state == IP_FAIL) - nm_device_state_changed (self, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + nm_device_check_ip_failed (self, FALSE); out: - nm_log_info (LOGD_DEVICE | LOGD_IP4, - "Activation (%s) Stage 4 of 5 (IPv4 Configure Timeout) complete.", - iface); + _LOGI (LOGD_DEVICE | LOGD_IP4, + "Activation: Stage 4 of 5 (IPv4 Configure Timeout) complete."); return FALSE; } @@ -4213,16 +4869,15 @@ nm_device_activate_schedule_ip4_config_timeout (NMDevice *self) activation_source_schedule (self, nm_device_activate_ip4_config_timeout, AF_INET); - nm_log_info (LOGD_DEVICE | LOGD_IP4, - "Activation (%s) Stage 4 of 5 (IPv4 Configure Timeout) scheduled...", - nm_device_get_iface (self)); + _LOGI (LOGD_DEVICE | LOGD_IP4, + "Activation: Stage 4 of 5 (IPv4 Configure Timeout) scheduled..."); } static NMActStageReturn act_stage4_ip6_config_timeout (NMDevice *self, NMDeviceStateReason *reason) { - if (nm_device_ip_config_should_fail (self, TRUE)) { + if (!get_ip_config_may_fail (self, AF_INET6)) { *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; return NM_ACT_STAGE_RETURN_FAILURE; } @@ -4242,17 +4897,14 @@ nm_device_activate_ip6_config_timeout (gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *iface; NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, AF_INET6); - iface = nm_device_get_iface (self); - nm_log_info (LOGD_DEVICE | LOGD_IP6, - "Activation (%s) Stage 4 of 5 (IPv6 Configure Timeout) started...", - iface); + _LOGI (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) @@ -4265,16 +4917,11 @@ nm_device_activate_ip6_config_timeout (gpointer user_data) priv->ip6_state = IP_FAIL; - /* If IPv6 failed and IPv4 failed, the activation fails */ - if (priv->ip4_state == IP_FAIL) - nm_device_state_changed (self, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + nm_device_check_ip_failed (self, FALSE); out: - nm_log_info (LOGD_DEVICE | LOGD_IP6, - "Activation (%s) Stage 4 of 5 (IPv6 Configure Timeout) complete.", - iface); + _LOGI (LOGD_DEVICE | LOGD_IP6, + "Activation: Stage 4 of 5 (IPv6 Configure Timeout) complete."); return FALSE; } @@ -4297,9 +4944,8 @@ nm_device_activate_schedule_ip6_config_timeout (NMDevice *self) activation_source_schedule (self, nm_device_activate_ip6_config_timeout, AF_INET6); - nm_log_info (LOGD_DEVICE | LOGD_IP6, - "Activation (%s) Stage 4 of 5 (IPv6 Configure Timeout) scheduled...", - nm_device_get_iface (self)); + _LOGI (LOGD_DEVICE | LOGD_IP6, + "Activation: Stage 4 of 5 (IPv6 Configure Timeout) scheduled..."); } static void @@ -4320,16 +4966,19 @@ share_init (void) "nf_nat_sip", "nf_nat_tftp", "nf_nat_pptp", "nf_nat_h323", NULL }; char **iter; + int errsv; if (!nm_platform_sysctl_set ("/proc/sys/net/ipv4/ip_forward", "1")) { - nm_log_err (LOGD_SHARING, "Error starting IP forwarding: (%d) %s", - errno, strerror (errno)); + errsv = errno; + nm_log_err (LOGD_SHARING, "share: error starting IP forwarding: (%d) %s", + errsv, strerror (errsv)); return FALSE; } if (!nm_platform_sysctl_set ("/proc/sys/net/ipv4/ip_dynaddr", "1")) { - nm_log_err (LOGD_SHARING, "error starting IP forwarding: (%d) %s", - errno, strerror (errno)); + errsv = errno; + nm_log_err (LOGD_SHARING, "share: error starting IP forwarding: (%d) %s", + errsv, strerror (errsv)); } for (iter = modules; *iter; iter++) { @@ -4339,7 +4988,7 @@ share_init (void) if (!g_spawn_sync ("/", argv, envp, G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, share_child_setup, NULL, NULL, NULL, &status, &error)) { - nm_log_err (LOGD_SHARING, "error loading NAT module %s: (%d) %s", + nm_log_err (LOGD_SHARING, "share: error loading NAT module %s: (%d) %s", *iter, error ? error->code : 0, (error && error->message) ? error->message : "unknown"); if (error) @@ -4412,9 +5061,8 @@ start_sharing (NMDevice *self, NMIP4Config *config) nm_act_request_set_shared (req, TRUE); if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, config, &error)) { - nm_log_err (LOGD_SHARING, "(%s/%s): failed to start dnsmasq: %s", - nm_device_get_iface (self), ip_iface, - (error && error->message) ? error->message : "(unknown)"); + _LOGE (LOGD_SHARING, "share: (%s) failed to start dnsmasq: %s", + ip_iface, (error && error->message) ? error->message : "(unknown)"); g_error_free (error); nm_act_request_set_shared (req, FALSE); return FALSE; @@ -4429,13 +5077,12 @@ start_sharing (NMDevice *self, NMIP4Config *config) static void send_arps (NMDevice *self, const char *mode_arg) { - const char *argv[] = { "/sbin/arping", mode_arg, "-q", "-I", nm_device_get_ip_iface (self), "-c", "1", NULL, NULL }; + const char *argv[] = { NULL, mode_arg, "-q", "-I", nm_device_get_ip_iface (self), "-c", "1", NULL, NULL }; int ip_arg = G_N_ELEMENTS (argv) - 2; NMConnection *connection; - NMSettingIP4Config *s_ip4; + NMSettingIPConfig *s_ip4; int i, num; - NMIP4Address *addr; - guint32 ipaddr; + NMIPAddress *addr; GError *error = NULL; connection = nm_device_get_connection (self); @@ -4444,24 +5091,33 @@ send_arps (NMDevice *self, const char *mode_arg) s_ip4 = nm_connection_get_setting_ip4_config (connection); if (!s_ip4) return; - num = nm_setting_ip4_config_get_num_addresses (s_ip4); + num = nm_setting_ip_config_get_num_addresses (s_ip4); + if (num == 0) + return; + + argv[0] = nm_utils_find_helper ("arping", NULL, NULL); + if (!argv[0]) { + _LOGW (LOGD_DEVICE | LOGD_IP4, "arping could not be found; no ARPs will be sent"); + return; + } for (i = 0; i < num; i++) { - addr = nm_setting_ip4_config_get_address (s_ip4, i); - ipaddr = nm_ip4_address_get_address (addr); - argv[ip_arg] = (char *) nm_utils_inet4_ntop (ipaddr, NULL); - - nm_log_dbg (LOGD_DEVICE | LOGD_IP4, - "Running arping %s -I %s %s", - mode_arg, nm_device_get_iface (self), argv[ip_arg]); - g_spawn_async (NULL, (char **) argv, NULL, - G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, - nm_unblock_posix_signals, - NULL, NULL, &error); - if (error) { - nm_log_warn (LOGD_DEVICE | LOGD_IP4, - "Could not send ARP for local address %s: %s", - argv[ip_arg], error->message); + gs_free char *tmp_str = NULL; + gboolean success; + + addr = nm_setting_ip_config_get_address (s_ip4, i); + argv[ip_arg] = nm_ip_address_get_address (addr); + + _LOGD (LOGD_DEVICE | LOGD_IP4, + "arping: run %s", (tmp_str = g_strjoinv (" ", (char **) argv))); + success = g_spawn_async (NULL, (char **) argv, NULL, + G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, + nm_unblock_posix_signals, + NULL, NULL, &error); + if (!success) { + _LOGW (LOGD_DEVICE | LOGD_IP4, + "arping: could not send ARP for local address %s: %s", + argv[ip_arg], error->message); g_clear_error (&error); } } @@ -4497,7 +5153,7 @@ arp_announce (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; - NMSettingIP4Config *s_ip4; + NMSettingIPConfig *s_ip4; int num; arp_cleanup (self); @@ -4511,7 +5167,7 @@ arp_announce (NMDevice *self) s_ip4 = nm_connection_get_setting_ip4_config (connection); if (!s_ip4) return; - num = nm_setting_ip4_config_get_num_addresses (s_ip4); + num = nm_setting_ip_config_get_num_addresses (s_ip4); if (num == 0) return; @@ -4525,33 +5181,33 @@ nm_device_activate_ip4_config_commit (gpointer user_data) NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActRequest *req; - const char *iface, *method; + const char *method; 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_INET); - iface = nm_device_get_iface (self); - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IPv4 Commit) started...", - iface); + _LOGI (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); g_assert (connection); - /* Device should be up before we can do anything with it */ - if (!nm_platform_link_is_up (nm_device_get_ip_ifindex (self))) { - nm_log_warn (LOGD_DEVICE, "(%s): interface %s not up for IP configuration", - iface, nm_device_get_ip_iface (self)); + /* Interface must be IFF_UP before IP config can be applied */ + ip_ifindex = nm_device_get_ip_ifindex (self); + if (!nm_platform_link_is_up (ip_ifindex) && !nm_device_uses_assumed_connection (self)) { + nm_platform_link_set_up (ip_ifindex); + if (!nm_platform_link_is_up (ip_ifindex)) + _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); } /* NULL to use the existing priv->dev_ip4_config */ if (!ip4_config_merge_and_apply (self, NULL, TRUE, &reason)) { - nm_log_info (LOGD_DEVICE | LOGD_IP4, - "Activation (%s) Stage 5 of 5 (IPv4 Commit) failed", - iface); + _LOGI (LOGD_DEVICE | LOGD_IP4, + "Activation: Stage 5 of 5 (IPv4 Commit) failed"); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); goto out; } @@ -4561,7 +5217,7 @@ nm_device_activate_ip4_config_commit (gpointer user_data) if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) { if (!start_sharing (self, priv->ip4_config)) { - nm_log_warn (LOGD_SHARING, "Activation (%s) Stage 5 of 5 (IPv4 Commit) start sharing failed.", iface); + _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; } @@ -4593,8 +5249,7 @@ nm_device_activate_ip4_config_commit (gpointer user_data) nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); out: - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IPv4 Commit) complete.", - iface); + _LOGI (LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv4 Commit) complete."); return FALSE; } @@ -4605,17 +5260,16 @@ nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *conf NMDevicePrivate *priv; g_return_if_fail (NM_IS_DEVICE (self)); - g_return_if_fail (NM_IS_IP4_CONFIG (config)); priv = NM_DEVICE_GET_PRIVATE (self); g_clear_object (&priv->dev_ip4_config); - priv->dev_ip4_config = g_object_ref (config); + if (config) + priv->dev_ip4_config = g_object_ref (config); activation_source_schedule (self, nm_device_activate_ip4_config_commit, AF_INET); - nm_log_info (LOGD_DEVICE | LOGD_IP4, - "Activation (%s) Stage 5 of 5 (IPv4 Configure Commit) scheduled...", - nm_device_get_iface (self)); + _LOGI (LOGD_DEVICE | LOGD_IP4, + "Activation: Stage 5 of 5 (IPv4 Configure Commit) scheduled..."); } gboolean @@ -4639,27 +5293,27 @@ nm_device_activate_ip6_config_commit (gpointer user_data) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); guint level = (priv->ip6_state == IP_DONE) ? LOGL_DEBUG : LOGL_INFO; NMActRequest *req; - const char *iface; 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); - iface = nm_device_get_iface (self); - nm_log (LOGD_DEVICE, level, "Activation (%s) Stage 5 of 5 (IPv6 Commit) started...", iface); + _LOG (level, 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); g_assert (connection); - /* Device should be up before we can do anything with it */ - g_warn_if_fail (nm_platform_link_is_up (nm_device_get_ip_ifindex (self))); - - /* Allow setting MTU etc */ - if (NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit) - NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit (self); + /* Interface must be IFF_UP before IP config can be applied */ + ip_ifindex = nm_device_get_ip_ifindex (self); + if (!nm_platform_link_is_up (ip_ifindex) && !nm_device_uses_assumed_connection (self)) { + nm_platform_link_set_up (ip_ifindex); + if (!nm_platform_link_is_up (ip_ifindex)) + _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); + } if (ip6_config_merge_and_apply (self, TRUE, &reason)) { /* If IPv6 wasn't the first IP to complete, and DHCP was used, @@ -4686,13 +5340,12 @@ nm_device_activate_ip6_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); } else { - nm_log_warn (LOGD_DEVICE | LOGD_IP6, - "Activation (%s) Stage 5 of 5 (IPv6 Commit) failed", - iface); + _LOGW (LOGD_DEVICE | LOGD_IP6, + "Activation: Stage 5 of 5 (IPv6 Commit) failed"); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); } - nm_log (LOGD_DEVICE, level, "Activation (%s) Stage 5 of 5 (IPv6 Commit) complete.", iface); + _LOG (level, LOGD_DEVICE, "Activation: Stage 5 of 5 (IPv6 Commit) complete."); return FALSE; } @@ -4705,11 +5358,16 @@ nm_device_activate_schedule_ip6_config_result (NMDevice *self) g_return_if_fail (NM_IS_DEVICE (self)); + /* If IP had previously failed, move it back to IP_CONF since we + * clearly now have configuration. + */ + if (priv->ip6_state == IP_FAIL) + priv->ip6_state = IP_CONF; + activation_source_schedule (self, nm_device_activate_ip6_config_commit, AF_INET6); - nm_log (LOGD_DEVICE | LOGD_IP6, level, - "Activation (%s) Stage 5 of 5 (IPv6 Commit) scheduled...", - nm_device_get_iface (self)); + _LOG (level, LOGD_DEVICE | LOGD_IP6, + "Activation: Stage 5 of 5 (IPv6 Commit) scheduled..."); } gboolean @@ -4775,7 +5433,7 @@ _update_ip4_address (NMDevice *self) fd = socket (PF_INET, SOCK_DGRAM, 0); if (fd < 0) { - nm_log_err (LOGD_IP4, "couldn't open control socket."); + _LOGE (LOGD_IP4, "couldn't open control socket."); return; } @@ -4790,17 +5448,17 @@ _update_ip4_address (NMDevice *self) } gboolean -nm_device_get_is_nm_owned (NMDevice *device) +nm_device_get_is_nm_owned (NMDevice *self) { - return NM_DEVICE_GET_PRIVATE (device)->is_nm_owned; + return NM_DEVICE_GET_PRIVATE (self)->is_nm_owned; } void -nm_device_set_nm_owned (NMDevice *device) +nm_device_set_nm_owned (NMDevice *self) { - g_return_if_fail (NM_IS_DEVICE (device)); + g_return_if_fail (NM_IS_DEVICE (self)); - NM_DEVICE_GET_PRIVATE (device)->is_nm_owned = TRUE; + NM_DEVICE_GET_PRIVATE (self)->is_nm_owned = TRUE; } /* @@ -4814,6 +5472,7 @@ static gboolean delete_on_deactivate_link_delete (gpointer user_data) { DeleteOnDeactivateData *data = user_data; + NMDevice *self = data->device; if (data->device) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (data->device); @@ -4822,8 +5481,8 @@ delete_on_deactivate_link_delete (gpointer user_data) priv->delete_on_deactivate_data = NULL; } - nm_log_dbg (LOGD_DEVICE, "delete_on_deactivate: cleanup and delete virtual link #%d (id=%u)", - data->ifindex, data->idle_add_id); + _LOGD (LOGD_DEVICE, "delete_on_deactivate: cleanup and delete virtual link #%d (id=%u)", + data->ifindex, data->idle_add_id); nm_platform_link_delete (data->ifindex); g_free (data); return FALSE; @@ -4841,8 +5500,8 @@ delete_on_deactivate_unschedule (NMDevice *self) g_source_remove (data->idle_add_id); g_object_remove_weak_pointer (G_OBJECT (self), (void **) &data->device); - nm_log_dbg (LOGD_DEVICE, "delete_on_deactivate: cancel cleanup and delete virtual link #%d (id=%u)", - data->ifindex, data->idle_add_id); + _LOGD (LOGD_DEVICE, "delete_on_deactivate: cancel cleanup and delete virtual link #%d (id=%u)", + data->ifindex, data->idle_add_id); g_free (data); } } @@ -4857,6 +5516,8 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex) return; if (!priv->is_nm_owned) return; + if (priv->queued_act_request) + return; if (!nm_device_is_software (self)) return; if (nm_device_get_state (self) == NM_DEVICE_STATE_UNMANAGED) @@ -4872,17 +5533,17 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex) data->idle_add_id = g_idle_add (delete_on_deactivate_link_delete, data); priv->delete_on_deactivate_data = data; - nm_log_dbg (LOGD_DEVICE, "delete_on_deactivate: schedule cleanup and delete virtual link #%d for [%s] (id=%u)", - ifindex, nm_device_get_iface (self), data->idle_add_id); + _LOGD (LOGD_DEVICE, "delete_on_deactivate: schedule cleanup and delete virtual link #%d (id=%u)", + ifindex, data->idle_add_id); } static void -disconnect_cb (NMDevice *device, +disconnect_cb (NMDevice *self, DBusGMethodInvocation *context, GError *error, gpointer user_data) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); GError *local = NULL; if (error) { @@ -4898,9 +5559,9 @@ disconnect_cb (NMDevice *device, dbus_g_method_return_error (context, local); g_error_free (local); } else { - priv->autoconnect = FALSE; + nm_device_set_autoconnect (self, FALSE); - nm_device_state_changed (device, + nm_device_state_changed (self, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_USER_REQUESTED); dbus_g_method_return (context); @@ -4908,12 +5569,12 @@ disconnect_cb (NMDevice *device, } static void -impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context) +impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context) { NMConnection *connection; GError *error = NULL; - if (NM_DEVICE_GET_PRIVATE (device)->act_request == NULL) { + if (NM_DEVICE_GET_PRIVATE (self)->act_request == NULL) { error = g_error_new_literal (NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ACTIVE, "This device is not active"); @@ -4922,11 +5583,11 @@ impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context) return; } - connection = nm_device_get_connection (device); + connection = nm_device_get_connection (self); g_assert (connection); /* Ask the manager to authenticate this request for us */ - g_signal_emit (device, signals[AUTH_REQUEST], 0, + g_signal_emit (self, signals[AUTH_REQUEST], 0, context, connection, NM_AUTH_PERMISSION_NETWORK_CONTROL, @@ -4936,6 +5597,46 @@ impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context) } static void +delete_cb (NMDevice *self, + DBusGMethodInvocation *context, + GError *error, + gpointer user_data) +{ + if (error) { + dbus_g_method_return_error (context, error); + return; + } + + /* Authorized */ + nm_platform_link_delete (nm_device_get_ifindex (self)); + dbus_g_method_return (context); +} + +static void +impl_device_delete (NMDevice *self, DBusGMethodInvocation *context) +{ + GError *error = NULL; + + if (!nm_device_is_software (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); + return; + } + + /* Ask the manager to authenticate this request for us */ + g_signal_emit (self, signals[AUTH_REQUEST], 0, + context, + NULL, + NM_AUTH_PERMISSION_NETWORK_CONTROL, + TRUE, + delete_cb, + NULL); +} + +static void _device_activate (NMDevice *self, NMActRequest *req) { NMDevicePrivate *priv; @@ -4949,9 +5650,8 @@ _device_activate (NMDevice *self, NMActRequest *req) connection = nm_act_request_get_connection (req); g_assert (connection); - nm_log_info (LOGD_DEVICE, "Activation (%s) starting connection '%s'", - nm_device_get_iface (self), - nm_connection_get_id (connection)); + _LOGI (LOGD_DEVICE, "Activation: starting connection '%s'", + nm_connection_get_id (connection)); delete_on_deactivate_unschedule (self); @@ -4986,8 +5686,7 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req) priv->queued_act_request = g_object_ref (req); /* Deactivate existing activation request first */ - nm_log_info (LOGD_DEVICE, "(%s): disconnecting for new activation request.", - nm_device_get_iface (self)); + _LOGI (LOGD_DEVICE, "disconnecting for new activation request."); nm_device_state_changed (self, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_NONE); @@ -5000,14 +5699,14 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req) * */ gboolean -nm_device_is_activating (NMDevice *device) +nm_device_is_activating (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDeviceState state; - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - state = nm_device_get_state (device); + state = nm_device_get_state (self); if (state >= NM_DEVICE_STATE_PREPARE && state <= NM_DEVICE_STATE_SECONDARIES) return TRUE; @@ -5021,7 +5720,7 @@ nm_device_is_activating (NMDevice *device) /* IP Configuration stuff */ -NMDHCP4Config * +NMDhcp4Config * nm_device_get_dhcp4_config (NMDevice *self) { g_return_val_if_fail (NM_IS_DEVICE (self), NULL); @@ -5041,6 +5740,7 @@ nm_device_get_ip4_config (NMDevice *self) static gboolean nm_device_set_ip4_config (NMDevice *self, NMIP4Config *new_config, + guint32 default_route_metric, gboolean commit, NMDeviceStateReason *reason) { @@ -5062,7 +5762,12 @@ nm_device_set_ip4_config (NMDevice *self, /* Always commit to nm-platform to update lifetimes */ if (commit && new_config) { - success = nm_ip4_config_commit (new_config, ip_ifindex); + gboolean assumed = nm_device_uses_assumed_connection (self); + + /* for assumed devices we set the device_route_metric to the default which will + * stop nm_platform_ip4_address_sync() to replace the device routes. */ + success = nm_ip4_config_commit (new_config, ip_ifindex, + assumed ? NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE : default_route_metric); if (!success) reason_local = NM_DEVICE_STATE_REASON_CONFIG_FAILED; } @@ -5073,8 +5778,8 @@ nm_device_set_ip4_config (NMDevice *self, * this causes a re-read and reset. This should only happen for relevant changes */ nm_ip4_config_replace (old_config, new_config, &has_changes); if (has_changes) { - nm_log_dbg (LOGD_IP4, "(%s): update IP4Config instance (%s)", - ip_iface, nm_ip4_config_get_dbus_path (old_config)); + _LOGD (LOGD_IP4, "update IP4Config instance (%s)", + nm_ip4_config_get_dbus_path (old_config)); } } else { has_changes = TRUE; @@ -5085,18 +5790,20 @@ nm_device_set_ip4_config (NMDevice *self, nm_ip4_config_export (new_config); } - nm_log_dbg (LOGD_IP4, "(%s): set IP4Config instance (%s)", - ip_iface, nm_ip4_config_get_dbus_path (new_config)); + _LOGD (LOGD_IP4, "set IP4Config instance (%s)", + nm_ip4_config_get_dbus_path (new_config)); } } else if (old_config) { has_changes = TRUE; priv->ip4_config = NULL; - nm_log_dbg (LOGD_IP4, "(%s): clear IP4Config instance (%s)", - ip_iface, nm_ip4_config_get_dbus_path (old_config)); + _LOGD (LOGD_IP4, "clear IP4Config instance (%s)", + nm_ip4_config_get_dbus_path (old_config)); /* Device config is invalid if combined config is invalid */ g_clear_object (&priv->dev_ip4_config); } + nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self); + if (has_changes) { _update_ip4_address (self); @@ -5107,7 +5814,7 @@ nm_device_set_ip4_config (NMDevice *self, if (old_config != priv->ip4_config && old_config) g_object_unref (old_config); - if (nm_device_uses_generated_connection (self)) { + if (nm_device_uses_generated_assumed_connection (self)) { NMConnection *connection = nm_device_get_connection (self); NMSetting *s_ip4; @@ -5128,9 +5835,9 @@ nm_device_set_ip4_config (NMDevice *self, } void -nm_device_set_vpn4_config (NMDevice *device, NMIP4Config *config) +nm_device_set_vpn4_config (NMDevice *self, NMIP4Config *config) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); if (priv->vpn4_config == config) return; @@ -5140,10 +5847,25 @@ nm_device_set_vpn4_config (NMDevice *device, NMIP4Config *config) priv->vpn4_config = g_object_ref (config); /* NULL to use existing configs */ - if (!ip4_config_merge_and_apply (device, NULL, TRUE, NULL)) { - nm_log_warn (LOGD_IP4, "(%s): failed to set VPN routes for device", - nm_device_get_ip_iface (device)); - } + if (!ip4_config_merge_and_apply (self, NULL, TRUE, NULL)) + _LOGW (LOGD_IP4, "failed to set VPN routes for device"); +} + +void +nm_device_set_wwan_ip4_config (NMDevice *self, NMIP4Config *config) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->wwan_ip4_config == config) + return; + + g_clear_object (&priv->wwan_ip4_config); + if (config) + priv->wwan_ip4_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 WWAN IPv4 configuration"); } static gboolean @@ -5181,8 +5903,8 @@ nm_device_set_ip6_config (NMDevice *self, * this causes a re-read and reset. This should only happen for relevant changes */ nm_ip6_config_replace (old_config, new_config, &has_changes); if (has_changes) { - nm_log_dbg (LOGD_IP6, "(%s): update IP6Config instance (%s)", - ip_iface, nm_ip6_config_get_dbus_path (old_config)); + _LOGD (LOGD_IP6, "update IP6Config instance (%s)", + nm_ip6_config_get_dbus_path (old_config)); } } else { has_changes = TRUE; @@ -5193,16 +5915,18 @@ nm_device_set_ip6_config (NMDevice *self, nm_ip6_config_export (new_config); } - nm_log_dbg (LOGD_IP4, "(%s): set IP6Config instance (%s)", - ip_iface, nm_ip6_config_get_dbus_path (new_config)); + _LOGD (LOGD_IP4, "set IP6Config instance (%s)", + nm_ip6_config_get_dbus_path (new_config)); } } else if (old_config) { has_changes = TRUE; priv->ip6_config = NULL; - nm_log_dbg (LOGD_IP6, "(%s): clear IP6Config instance (%s)", - ip_iface, nm_ip6_config_get_dbus_path (old_config)); + _LOGD (LOGD_IP6, "clear IP6Config instance (%s)", + nm_ip6_config_get_dbus_path (old_config)); } + nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self); + if (has_changes) { if (old_config != priv->ip6_config) g_object_notify (G_OBJECT (self), NM_DEVICE_IP6_CONFIG); @@ -5211,7 +5935,7 @@ nm_device_set_ip6_config (NMDevice *self, if (old_config != priv->ip6_config && old_config) g_object_unref (old_config); - if (nm_device_uses_generated_connection (self)) { + if (nm_device_uses_generated_assumed_connection (self)) { NMConnection *connection = nm_device_get_connection (self); NMSetting *s_ip6; @@ -5232,9 +5956,9 @@ nm_device_set_ip6_config (NMDevice *self, } void -nm_device_set_vpn6_config (NMDevice *device, NMIP6Config *config) +nm_device_set_vpn6_config (NMDevice *self, NMIP6Config *config) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); if (priv->vpn6_config == config) return; @@ -5244,13 +5968,28 @@ nm_device_set_vpn6_config (NMDevice *device, NMIP6Config *config) priv->vpn6_config = g_object_ref (config); /* NULL to use existing configs */ - if (!ip6_config_merge_and_apply (device, TRUE, NULL)) { - nm_log_warn (LOGD_IP6, "(%s): failed to set VPN routes for device", - nm_device_get_ip_iface (device)); - } + if (!ip6_config_merge_and_apply (self, TRUE, NULL)) + _LOGW (LOGD_IP6, "failed to set VPN routes for device"); +} + +void +nm_device_set_wwan_ip6_config (NMDevice *self, NMIP6Config *config) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->wwan_ip6_config == config) + return; + + g_clear_object (&priv->wwan_ip6_config); + if (config) + priv->wwan_ip6_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 WWAN IPv6 configuration"); } -NMDHCP6Config * +NMDhcp6Config * nm_device_get_dhcp6_config (NMDevice *self) { g_return_val_if_fail (NM_IS_DEVICE (self), NULL); @@ -5336,16 +6075,7 @@ ip_check_gw_ping_cleanup (NMDevice *self) } if (priv->gw_ping.pid) { - guint count = 20; - int status; - - kill (priv->gw_ping.pid, SIGKILL); - do { - if (waitpid (priv->gw_ping.pid, &status, WNOHANG) != 0) - break; - g_usleep (G_USEC_PER_SEC / 20); - } while (count--); - + nm_utils_kill_child_async (priv->gw_ping.pid, SIGTERM, priv->gw_ping.log_domain, "ping", 1000, NULL, NULL); priv->gw_ping.pid = 0; } } @@ -5355,7 +6085,6 @@ 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); - const char *iface; guint log_domain = priv->gw_ping.log_domain; if (!priv->gw_ping.watch) @@ -5363,17 +6092,15 @@ ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data) priv->gw_ping.watch = 0; priv->gw_ping.pid = 0; - iface = nm_device_get_iface (self); - if (WIFEXITED (status)) { if (WEXITSTATUS (status) == 0) - nm_log_dbg (log_domain, "(%s): gateway ping succeeded", iface); + _LOGD (log_domain, "ping: gateway ping succeeded"); else { - nm_log_warn (log_domain, "(%s): gateway ping failed with error code %d", - iface, WEXITSTATUS (status)); + _LOGW (log_domain, "ping: gateway ping failed with error code %d", + WEXITSTATUS (status)); } } else - nm_log_warn (log_domain, "(%s): ping stopped unexpectedly with status %d", iface, status); + _LOGW (log_domain, "ping: stopped unexpectedly with status %d", status); /* We've got connectivity, proceed to pre_up */ ip_check_gw_ping_cleanup (self); @@ -5388,8 +6115,7 @@ ip_check_ping_timeout_cb (gpointer user_data) priv->gw_ping.timeout = 0; - nm_log_warn (priv->gw_ping.log_domain, "(%s): gateway ping timed out", - nm_device_get_iface (self)); + _LOGW (priv->gw_ping.log_domain, "ping: gateway ping timed out"); ip_check_gw_ping_cleanup (self); ip_check_pre_up (self); @@ -5406,7 +6132,8 @@ spawn_ping (NMDevice *self, NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const char *args[] = { binary, "-I", nm_device_get_ip_iface (self), "-c", "1", "-w", NULL, address, NULL }; GError *error = NULL; - char *str_timeout, *cmd; + char *str_timeout; + gs_free char *tmp_str = NULL; gboolean success; g_return_val_if_fail (priv->gw_ping.watch == 0, FALSE); @@ -5414,13 +6141,8 @@ spawn_ping (NMDevice *self, args[6] = str_timeout = g_strdup_printf ("%u", timeout); - if (nm_logging_enabled (LOGL_DEBUG, log_domain)) { - cmd = g_strjoinv (" ", (gchar **) args); - nm_log_dbg (log_domain, "(%s): running '%s'", - nm_device_get_iface (self), - cmd); - g_free (cmd); - } + _LOGD (log_domain, "ping: running '%s'", + (tmp_str = g_strjoinv (" ", (gchar **) args))); success = g_spawn_async ("/", (gchar **) args, @@ -5435,7 +6157,7 @@ spawn_ping (NMDevice *self, priv->gw_ping.watch = g_child_watch_add (priv->gw_ping.pid, ip_check_ping_watch_cb, self); priv->gw_ping.timeout = g_timeout_add_seconds (timeout + 1, ip_check_ping_timeout_cb, self); } else { - nm_log_warn (log_domain, "could not spawn %s: %s", binary, error->message); + _LOGW (log_domain, "ping: could not spawn %s: %s", binary, error->message); g_clear_error (&error); } @@ -5471,7 +6193,7 @@ nm_device_start_ip_check (NMDevice *self) timeout = nm_setting_connection_get_gateway_ping_timeout (s_con); if (timeout) { - if (priv->ip4_state == IP_DONE) { + if (priv->ip4_config && priv->ip4_state == IP_DONE) { guint gw = 0; ping_binary = "/usr/bin/ping"; @@ -5524,9 +6246,9 @@ nm_device_is_up (NMDevice *self) } static gboolean -is_up (NMDevice *device) +is_up (NMDevice *self) { - int ifindex = nm_device_get_ip_ifindex (device); + int ifindex = nm_device_get_ip_ifindex (self); return ifindex > 0 ? nm_platform_link_is_up (ifindex) : TRUE; } @@ -5539,7 +6261,7 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - nm_log_dbg (LOGD_HW, "(%s): bringing up device.", nm_device_get_iface (self)); + _LOGD (LOGD_HW, "bringing up device."); if (NM_DEVICE_GET_CLASS (self)->bring_up) { if (!NM_DEVICE_GET_CLASS (self)->bring_up (self, no_firmware)) @@ -5561,9 +6283,9 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) if (!device_is_up) { if (block) - nm_log_warn (LOGD_HW, "(%s): device not up after timeout!", nm_device_get_iface (self)); + _LOGW (LOGD_HW, "device not up after timeout!"); else - nm_log_dbg (LOGD_HW, "(%s): device not up immediately", nm_device_get_iface (self)); + _LOGD (LOGD_HW, "device not up immediately"); return FALSE; } @@ -5589,18 +6311,18 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) } static void -check_carrier (NMDevice *device) +check_carrier (NMDevice *self) { - int ifindex = nm_device_get_ip_ifindex (device); + int ifindex = nm_device_get_ip_ifindex (self); - if (!device_has_capability (device, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) - nm_device_set_carrier (device, nm_platform_link_is_connected (ifindex)); + if (!device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) + nm_device_set_carrier (self, nm_platform_link_is_connected (ifindex)); } static gboolean -bring_up (NMDevice *device, gboolean *no_firmware) +bring_up (NMDevice *self, gboolean *no_firmware) { - int ifindex = nm_device_get_ip_ifindex (device); + int ifindex = nm_device_get_ip_ifindex (self); gboolean result; if (ifindex <= 0) { @@ -5614,8 +6336,8 @@ bring_up (NMDevice *device, gboolean *no_firmware) *no_firmware = nm_platform_get_error () == NM_PLATFORM_ERROR_NO_FIRMWARE; /* Store carrier immediately. */ - if (result && device_has_capability (device, NM_DEVICE_CAP_CARRIER_DETECT)) - check_carrier (device); + if (result && device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) + check_carrier (self); return result; } @@ -5627,7 +6349,7 @@ nm_device_take_down (NMDevice *self, gboolean block) g_return_if_fail (NM_IS_DEVICE (self)); - nm_log_dbg (LOGD_HW, "(%s): taking down device.", nm_device_get_iface (self)); + _LOGD (LOGD_HW, "taking down device."); if (NM_DEVICE_GET_CLASS (self)->take_down) { if (!NM_DEVICE_GET_CLASS (self)->take_down (self)) @@ -5649,22 +6371,22 @@ nm_device_take_down (NMDevice *self, gboolean block) if (device_is_up) { if (block) - nm_log_warn (LOGD_HW, "(%s): device not down after timeout!", nm_device_get_iface (self)); + _LOGW (LOGD_HW, "device not down after timeout!"); else - nm_log_dbg (LOGD_HW, "(%s): device not down immediately", nm_device_get_iface (self)); + _LOGD (LOGD_HW, "device not down immediately"); } } static gboolean -take_down (NMDevice *device) +take_down (NMDevice *self) { - int ifindex = nm_device_get_ip_ifindex (device); + int ifindex = nm_device_get_ip_ifindex (self); if (ifindex > 0) return nm_platform_link_set_down (ifindex); /* devices without ifindex are always up. */ - nm_log_dbg (LOGD_HW, "(%s): cannot take down device without ifindex", nm_device_get_iface (device)); + _LOGD (LOGD_HW, "cannot take down device without ifindex"); return FALSE; } @@ -5689,11 +6411,11 @@ nm_device_get_firmware_missing (NMDevice *self) } static NMIP4Config * -find_ip4_lease_config (NMDevice *device, +find_ip4_lease_config (NMDevice *self, NMConnection *connection, NMIP4Config *ext_ip4_config) { - const char *ip_iface = nm_device_get_ip_iface (device); + const char *ip_iface = nm_device_get_ip_iface (self); GSList *leases, *liter; NMIP4Config *found = NULL; @@ -5703,7 +6425,8 @@ find_ip4_lease_config (NMDevice *device, leases = nm_dhcp_manager_get_lease_ip_configs (nm_dhcp_manager_get (), ip_iface, nm_connection_get_uuid (connection), - FALSE); + FALSE, + nm_device_get_ip4_route_metric (self)); for (liter = leases; liter && !found; liter = liter->next) { NMIP4Config *lease_config = liter->data; const NMPlatformIP4Address *address = nm_ip4_config_get_address (lease_config, 0); @@ -5722,13 +6445,13 @@ find_ip4_lease_config (NMDevice *device, } static void -capture_lease_config (NMDevice *device, +capture_lease_config (NMDevice *self, NMIP4Config *ext_ip4_config, NMIP4Config **out_ip4_config, NMIP6Config *ext_ip6_config, NMIP6Config **out_ip6_config) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const GSList *connections, *citer; guint i; gboolean dhcp_used = FALSE; @@ -5767,13 +6490,13 @@ capture_lease_config (NMDevice *device, NMConnection *candidate = citer->data; const char *method; - if (!nm_device_check_connection_compatible (device, candidate)) + if (!nm_device_check_connection_compatible (self, candidate)) continue; /* IPv4 leases */ method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP4_CONFIG); if (out_ip4_config && strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) { - *out_ip4_config = find_ip4_lease_config (device, candidate, ext_ip4_config); + *out_ip4_config = find_ip4_lease_config (self, candidate, ext_ip4_config); if (*out_ip4_config) return; } @@ -5805,7 +6528,8 @@ update_ip_config (NMDevice *self, gboolean initial) /* IPv4 */ g_clear_object (&priv->ext_ip4_config); priv->ext_ip4_config = nm_ip4_config_capture (ifindex, capture_resolv_conf); - + priv->ext_ip4_config_had_any_addresses = ( priv->ext_ip4_config + && nm_ip4_config_get_num_addresses (priv->ext_ip4_config) > 0); if (priv->ext_ip4_config) { if (initial) { g_clear_object (&priv->dev_ip4_config); @@ -5815,6 +6539,8 @@ update_ip_config (NMDevice *self, gboolean initial) 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); + if (priv->wwan_ip4_config) + nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config); ip4_config_merge_and_apply (self, NULL, FALSE, NULL); } @@ -5822,16 +6548,20 @@ update_ip_config (NMDevice *self, gboolean initial) /* IPv6 */ g_clear_object (&priv->ext_ip6_config); priv->ext_ip6_config = nm_ip6_config_capture (ifindex, capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + priv->ext_ip6_config_had_any_addresses = ( priv->ext_ip6_config + && nm_ip6_config_get_num_addresses (priv->ext_ip6_config) > 0); if (priv->ext_ip6_config) { /* Check this before modifying ext_ip6_config */ linklocal6_just_completed = priv->linklocal6_timeout_id && - linklocal6_config_is_ready (priv->ext_ip6_config); + have_ip6_address (priv->ext_ip6_config, TRUE); if (priv->ac_ip6_config) nm_ip6_config_subtract (priv->ext_ip6_config, priv->ac_ip6_config); if (priv->dhcp6_ip6_config) 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); @@ -5847,9 +6577,9 @@ update_ip_config (NMDevice *self, gboolean initial) } void -nm_device_capture_initial_config (NMDevice *dev) +nm_device_capture_initial_config (NMDevice *self) { - update_ip_config (dev, TRUE); + update_ip_config (self, TRUE); } static gboolean @@ -5864,22 +6594,33 @@ queued_ip_config_change (gpointer user_data) priv->queued_ip_config_id = 0; update_ip_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); + return FALSE; } static void -device_ip_changed (NMPlatform *platform, int ifindex, gpointer platform_object, NMPlatformSignalChangeType change_type, NMPlatformReason reason, gpointer user_data) +device_ip_changed (NMPlatform *platform, + int ifindex, + gpointer platform_object, + NMPlatformSignalChangeType change_type, + NMPlatformReason reason, + NMDevice *self) { - NMDevice *self = user_data; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); if (nm_device_get_ip_ifindex (self) == ifindex) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (!priv->queued_ip_config_id) priv->queued_ip_config_id = g_idle_add (queued_ip_config_change, self); - nm_log_dbg (LOGD_DEVICE, "(%s): queued IP config change", - nm_device_get_iface (self)); + _LOGD (LOGD_DEVICE, "queued IP config change"); } } @@ -5889,8 +6630,7 @@ nm_device_queued_ip_config_change_clear (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); if (priv->queued_ip_config_id) { - nm_log_dbg (LOGD_DEVICE, "(%s): clearing queued IP config change", - nm_device_get_iface (self)); + _LOGD (LOGD_DEVICE, "clearing queued IP config change"); g_source_remove (priv->queued_ip_config_id); priv->queued_ip_config_id = 0; } @@ -5898,19 +6638,19 @@ nm_device_queued_ip_config_change_clear (NMDevice *self) /** * nm_device_get_managed(): - * @device: the #NMDevice + * @self: the #NMDevice * * Returns: %TRUE if the device is managed */ gboolean -nm_device_get_managed (NMDevice *device) +nm_device_get_managed (NMDevice *self) { NMDevicePrivate *priv; gboolean managed; - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - priv = NM_DEVICE_GET_PRIVATE (device); + priv = NM_DEVICE_GET_PRIVATE (self); /* Return the composite of all managed flags. However, if the device * is a default-unmanaged device, and would be managed except for the @@ -5926,30 +6666,30 @@ nm_device_get_managed (NMDevice *device) /** * nm_device_get_unmanaged_flag(): - * @device: the #NMDevice + * @self: the #NMDevice * * Returns: %TRUE if the device is unmanaged for @flag. */ gboolean -nm_device_get_unmanaged_flag (NMDevice *device, NMUnmanagedFlags flag) +nm_device_get_unmanaged_flag (NMDevice *self, NMUnmanagedFlags flag) { - return NM_DEVICE_GET_PRIVATE (device)->unmanaged_flags & flag; + return NM_FLAGS_ANY (NM_DEVICE_GET_PRIVATE (self)->unmanaged_flags, flag); } /** * nm_device_get_default_unmanaged(): - * @device: the #NMDevice + * @self: the #NMDevice * * Returns: %TRUE if the device is by default unmanaged */ static gboolean -nm_device_get_default_unmanaged (NMDevice *device) +nm_device_get_default_unmanaged (NMDevice *self) { - return nm_device_get_unmanaged_flag (device, NM_UNMANAGED_DEFAULT); + return nm_device_get_unmanaged_flag (self, NM_UNMANAGED_DEFAULT); } void -nm_device_set_unmanaged (NMDevice *device, +nm_device_set_unmanaged (NMDevice *self, NMUnmanagedFlags flag, gboolean unmanaged, NMDeviceStateReason reason) @@ -5957,42 +6697,40 @@ nm_device_set_unmanaged (NMDevice *device, NMDevicePrivate *priv; gboolean was_managed, now_managed; - g_return_if_fail (NM_IS_DEVICE (device)); + g_return_if_fail (NM_IS_DEVICE (self)); g_return_if_fail (flag <= NM_UNMANAGED_LAST); - priv = NM_DEVICE_GET_PRIVATE (device); + priv = NM_DEVICE_GET_PRIVATE (self); - was_managed = nm_device_get_managed (device); + was_managed = nm_device_get_managed (self); if (unmanaged) priv->unmanaged_flags |= flag; else priv->unmanaged_flags &= ~flag; - now_managed = nm_device_get_managed (device); + now_managed = nm_device_get_managed (self); if (was_managed != now_managed) { - nm_log_dbg (LOGD_DEVICE, "(%s): now %s", - nm_device_get_iface (device), - unmanaged ? "unmanaged" : "managed"); + _LOGD (LOGD_DEVICE, "now %s", unmanaged ? "unmanaged" : "managed"); - g_object_notify (G_OBJECT (device), NM_DEVICE_MANAGED); + g_object_notify (G_OBJECT (self), NM_DEVICE_MANAGED); if (unmanaged) - nm_device_state_changed (device, NM_DEVICE_STATE_UNMANAGED, reason); + nm_device_state_changed (self, NM_DEVICE_STATE_UNMANAGED, reason); else - nm_device_state_changed (device, NM_DEVICE_STATE_UNAVAILABLE, reason); + nm_device_state_changed (self, NM_DEVICE_STATE_UNAVAILABLE, reason); } } void -nm_device_set_unmanaged_quitting (NMDevice *device) +nm_device_set_unmanaged_quitting (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); /* It's OK to block here because we're quitting */ - if (nm_device_is_activating (device) || priv->state == NM_DEVICE_STATE_ACTIVATED) - _set_state_full (device, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_REMOVED, TRUE); + if (nm_device_is_activating (self) || priv->state == NM_DEVICE_STATE_ACTIVATED) + _set_state_full (self, NM_DEVICE_STATE_DEACTIVATING, NM_DEVICE_STATE_REASON_REMOVED, TRUE); - nm_device_set_unmanaged (device, + nm_device_set_unmanaged (self, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED); @@ -6000,26 +6738,26 @@ nm_device_set_unmanaged_quitting (NMDevice *device) /** * nm_device_set_initial_unmanaged_flag(): - * @device: the #NMDevice + * @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 exported - * and does not trigger state changes. Should only be used when initializing - * a device. + * Like nm_device_set_unmanaged(), 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 *device, +nm_device_set_initial_unmanaged_flag (NMDevice *self, NMUnmanagedFlags flag, gboolean unmanaged) { NMDevicePrivate *priv; - g_return_if_fail (NM_IS_DEVICE (device)); + g_return_if_fail (NM_IS_DEVICE (self)); g_return_if_fail (flag <= NM_UNMANAGED_LAST); - priv = NM_DEVICE_GET_PRIVATE (device); - g_return_if_fail (priv->path == NULL); + priv = NM_DEVICE_GET_PRIVATE (self); + g_return_if_fail (priv->initialized == FALSE); if (unmanaged) priv->unmanaged_flags |= flag; @@ -6028,60 +6766,54 @@ nm_device_set_initial_unmanaged_flag (NMDevice *device, } void -nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout) +nm_device_set_dhcp_timeout (NMDevice *self, guint32 timeout) { - g_return_if_fail (NM_IS_DEVICE (device)); + g_return_if_fail (NM_IS_DEVICE (self)); - NM_DEVICE_GET_PRIVATE (device)->dhcp_timeout = timeout; + NM_DEVICE_GET_PRIVATE (self)->dhcp_timeout = timeout; } void -nm_device_set_dhcp_anycast_address (NMDevice *device, guint8 *addr) +nm_device_set_dhcp_anycast_address (NMDevice *self, const char *addr) { NMDevicePrivate *priv; - g_return_if_fail (NM_IS_DEVICE (device)); - - priv = NM_DEVICE_GET_PRIVATE (device); + g_return_if_fail (NM_IS_DEVICE (self)); + g_return_if_fail (!addr || nm_utils_hwaddr_valid (addr, ETH_ALEN)); - if (priv->dhcp_anycast_address) { - g_byte_array_free (priv->dhcp_anycast_address, TRUE); - priv->dhcp_anycast_address = NULL; - } + priv = NM_DEVICE_GET_PRIVATE (self); - if (addr) { - priv->dhcp_anycast_address = g_byte_array_sized_new (ETH_ALEN); - g_byte_array_append (priv->dhcp_anycast_address, addr, ETH_ALEN); - } + g_free (priv->dhcp_anycast_address); + priv->dhcp_anycast_address = g_strdup (addr); } /** * nm_device_connection_is_available(): - * @device: the #NMDevice + * @self: the #NMDevice * @connection: the #NMConnection to check for availability * @allow_device_override: set to %TRUE to let the device do specific checks * - * Check if @connection is available to be activated on @device. Normally this - * only checks if the connection is in @device's AvailableConnections property. + * Check if @connection is available to be activated on @self. Normally this + * only checks if the connection is in @self's AvailableConnections property. * If @allow_device_override is %TRUE then the device is asked to do specific * checks that may bypass the AvailableConnections property. * - * Returns: %TRUE if @connection can be activated on @device + * Returns: %TRUE if @connection can be activated on @self */ gboolean -nm_device_connection_is_available (NMDevice *device, +nm_device_connection_is_available (NMDevice *self, NMConnection *connection, gboolean allow_device_override) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean available = FALSE; - if (nm_device_get_default_unmanaged (device) && (priv->state == NM_DEVICE_STATE_UNMANAGED)) { + if (nm_device_get_default_unmanaged (self) && (priv->state == NM_DEVICE_STATE_UNMANAGED)) { /* default-unmanaged devices in UNMANAGED state have no available connections * so we must manually check whether the connection is available here. */ - if ( nm_device_check_connection_compatible (device, connection) - && NM_DEVICE_GET_CLASS (device)->check_connection_available (device, connection, NULL)) + if ( nm_device_check_connection_compatible (self, connection) + && NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, NULL)) return TRUE; } @@ -6092,32 +6824,33 @@ nm_device_connection_is_available (NMDevice *device, * activating but the network isn't available let the device recheck * availability. */ - if ( nm_device_check_connection_compatible (device, connection) - && NM_DEVICE_GET_CLASS (device)->check_connection_available_wifi_hidden) - available = NM_DEVICE_GET_CLASS (device)->check_connection_available_wifi_hidden (device, connection); + if ( nm_device_check_connection_compatible (self, connection) + && NM_DEVICE_GET_CLASS (self)->check_connection_available_wifi_hidden) + available = NM_DEVICE_GET_CLASS (self)->check_connection_available_wifi_hidden (self, connection); } return available; } static void -_signal_available_connections_changed (NMDevice *device) +_signal_available_connections_changed (NMDevice *self) { - g_object_notify (G_OBJECT (device), NM_DEVICE_AVAILABLE_CONNECTIONS); + g_object_notify (G_OBJECT (self), NM_DEVICE_AVAILABLE_CONNECTIONS); } static void -_clear_available_connections (NMDevice *device, gboolean do_signal) +_clear_available_connections (NMDevice *self, gboolean do_signal) { - g_hash_table_remove_all (NM_DEVICE_GET_PRIVATE (device)->available_connections); + g_hash_table_remove_all (NM_DEVICE_GET_PRIVATE (self)->available_connections); if (do_signal == TRUE) - _signal_available_connections_changed (device); + _signal_available_connections_changed (self); } static gboolean _try_add_available_connection (NMDevice *self, NMConnection *connection) { - if (nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED) + if ( nm_device_get_state (self) < NM_DEVICE_STATE_DISCONNECTED + && !nm_device_get_default_unmanaged (self)) return FALSE; if (nm_device_check_connection_compatible (self, connection)) { @@ -6132,98 +6865,49 @@ _try_add_available_connection (NMDevice *self, NMConnection *connection) } static gboolean -_del_available_connection (NMDevice *device, NMConnection *connection) +_del_available_connection (NMDevice *self, NMConnection *connection) { - return g_hash_table_remove (NM_DEVICE_GET_PRIVATE (device)->available_connections, connection); + return g_hash_table_remove (NM_DEVICE_GET_PRIVATE (self)->available_connections, connection); } static gboolean -connection_requires_carrier (NMConnection *connection) -{ - NMSettingIP4Config *s_ip4; - NMSettingIP6Config *s_ip6; - const char *method; - gboolean ip4_carrier_wanted = FALSE, ip6_carrier_wanted = FALSE; - gboolean ip4_used = FALSE, ip6_used = FALSE; - - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if ( strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) != 0 - && strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) != 0) { - ip4_carrier_wanted = TRUE; - - /* If IPv4 wants a carrier and cannot fail, the whole connection - * requires a carrier regardless of the IPv6 method. - */ - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (s_ip4 && !nm_setting_ip4_config_get_may_fail (s_ip4)) - return TRUE; - } - ip4_used = (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) != 0); - - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - if ( strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) != 0 - && strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) != 0) { - ip6_carrier_wanted = TRUE; - - /* If IPv6 wants a carrier and cannot fail, the whole connection - * requires a carrier regardless of the IPv4 method. - */ - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (s_ip6 && !nm_setting_ip6_config_get_may_fail (s_ip6)) - return TRUE; - } - ip6_used = (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) != 0); - - /* If an IP version wants a carrier and and the other IP version isn't - * used, the connection requires carrier since it will just fail without one. - */ - if (ip4_carrier_wanted && !ip6_used) - return TRUE; - if (ip6_carrier_wanted && !ip4_used) - return TRUE; - - /* If both want a carrier, the whole connection wants a carrier */ - return ip4_carrier_wanted && ip6_carrier_wanted; -} - -static gboolean -check_connection_available (NMDevice *device, +check_connection_available (NMDevice *self, NMConnection *connection, const char *specific_object) { /* Connections which require a network connection are not available when * the device has no carrier, even with ignore-carrer=TRUE. */ - if (NM_DEVICE_GET_PRIVATE (device)->carrier == FALSE) + if (NM_DEVICE_GET_PRIVATE (self)->carrier == FALSE) return connection_requires_carrier (connection) ? FALSE : TRUE; return TRUE; } void -nm_device_recheck_available_connections (NMDevice *device) +nm_device_recheck_available_connections (NMDevice *self) { NMDevicePrivate *priv; const GSList *connections, *iter; - g_return_if_fail (NM_IS_DEVICE (device)); + g_return_if_fail (NM_IS_DEVICE (self)); - priv = NM_DEVICE_GET_PRIVATE(device); + priv = NM_DEVICE_GET_PRIVATE(self); if (priv->con_provider) { - _clear_available_connections (device, FALSE); + _clear_available_connections (self, FALSE); connections = nm_connection_provider_get_connections (priv->con_provider); for (iter = connections; iter; iter = g_slist_next (iter)) - _try_add_available_connection (device, NM_CONNECTION (iter->data)); + _try_add_available_connection (self, NM_CONNECTION (iter->data)); - _signal_available_connections_changed (device); + _signal_available_connections_changed (self); } } /** * nm_device_get_available_connections: - * @device: the #NMDevice + * @self: the #NMDevice * @specific_object: a specific object path if any * * Returns a list of connections available to activate on the device, taking @@ -6233,9 +6917,9 @@ nm_device_recheck_available_connections (NMDevice *device) * Returns: caller-owned #GPtrArray of #NMConnections */ GPtrArray * -nm_device_get_available_connections (NMDevice *device, const char *specific_object) +nm_device_get_available_connections (NMDevice *self, const char *specific_object) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); GHashTableIter iter; guint num_available; NMConnection *connection = NULL; @@ -6250,7 +6934,7 @@ nm_device_get_available_connections (NMDevice *device, const char *specific_obje * compatible with it. */ if ( !specific_object - || NM_DEVICE_GET_CLASS (device)->check_connection_available (device, connection, specific_object)) + || NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, specific_object)) g_ptr_array_add (array, connection); } } @@ -6286,14 +6970,14 @@ cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gpoin } gboolean -nm_device_supports_vlans (NMDevice *device) +nm_device_supports_vlans (NMDevice *self) { - return nm_platform_link_supports_vlans (nm_device_get_ifindex (device)); + return nm_platform_link_supports_vlans (nm_device_get_ifindex (self)); } /** * nm_device_add_pending_action(): - * @device: the #NMDevice to add the pending action to + * @self: the #NMDevice to add the pending action to * @action: a static string that identifies the action * @assert_not_yet_pending: if %TRUE, assert that the @action is currently not yet pending. * Otherwise, ignore duplicate scheduling of the same action silently. @@ -6305,9 +6989,9 @@ nm_device_supports_vlans (NMDevice *device) * a second time. */ gboolean -nm_device_add_pending_action (NMDevice *device, const char *action, gboolean assert_not_yet_pending) +nm_device_add_pending_action (NMDevice *self, const char *action, gboolean assert_not_yet_pending) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); GSList *iter; guint count = 0; @@ -6317,16 +7001,12 @@ nm_device_add_pending_action (NMDevice *device, const char *action, gboolean ass for (iter = priv->pending_actions; iter; iter = iter->next) { if (!strcmp (action, iter->data)) { if (assert_not_yet_pending) { - nm_log_warn (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s' already pending", - nm_device_get_iface (device), - count + g_slist_length (iter), - action); + _LOGW (LOGD_DEVICE, "add_pending_action (%d): '%s' already pending", + count + g_slist_length (iter), action); g_return_val_if_reached (FALSE); } else { - nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s' already pending (expected)", - nm_device_get_iface (device), - count + g_slist_length (iter), - action); + _LOGD (LOGD_DEVICE, "add_pending_action (%d): '%s' already pending (expected)", + count + g_slist_length (iter), action); } return FALSE; } @@ -6336,20 +7016,17 @@ nm_device_add_pending_action (NMDevice *device, const char *action, gboolean ass priv->pending_actions = g_slist_append (priv->pending_actions, g_strdup (action)); count++; - nm_log_dbg (LOGD_DEVICE, "(%s): add_pending_action (%d): '%s'", - nm_device_get_iface (device), - count, - action); + _LOGD (LOGD_DEVICE, "add_pending_action (%d): '%s'", count, action); if (count == 1) - g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION); + g_object_notify (G_OBJECT (self), NM_DEVICE_HAS_PENDING_ACTION); return TRUE; } /** * nm_device_remove_pending_action(): - * @device: the #NMDevice to remove the pending action from + * @self: the #NMDevice to remove the pending action from * @action: a static string that identifies the action * @assert_is_pending: if %TRUE, assert that the @action is pending. * If %FALSE, don't do anything if the current action is not pending and @@ -6360,9 +7037,9 @@ nm_device_add_pending_action (NMDevice *device, const char *action, gboolean ass * Returns: whether the @action was pending and is now removed. */ gboolean -nm_device_remove_pending_action (NMDevice *device, const char *action, gboolean assert_is_pending) +nm_device_remove_pending_action (NMDevice *self, const char *action, gboolean assert_is_pending) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); GSList *iter; guint count = 0; @@ -6370,38 +7047,31 @@ nm_device_remove_pending_action (NMDevice *device, const char *action, gboolean for (iter = priv->pending_actions; iter; iter = iter->next) { if (!strcmp (action, iter->data)) { - nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s'", - nm_device_get_iface (device), - count + g_slist_length (iter->next), /* length excluding 'iter' */ - action); + _LOGD (LOGD_DEVICE, "remove_pending_action (%d): '%s'", + count + g_slist_length (iter->next), /* length excluding 'iter' */ + action); g_free (iter->data); priv->pending_actions = g_slist_delete_link (priv->pending_actions, iter); if (priv->pending_actions == NULL) - g_object_notify (G_OBJECT (device), NM_DEVICE_HAS_PENDING_ACTION); + g_object_notify (G_OBJECT (self), NM_DEVICE_HAS_PENDING_ACTION); return TRUE; } count++; } if (assert_is_pending) { - nm_log_warn (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s' not pending", - nm_device_get_iface (device), - count, - action); + _LOGW (LOGD_DEVICE, "remove_pending_action (%d): '%s' not pending", count, action); g_return_val_if_reached (FALSE); - } else { - nm_log_dbg (LOGD_DEVICE, "(%s): remove_pending_action (%d): '%s' not pending (expected)", - nm_device_get_iface (device), - count, - action); - } + } else + _LOGD (LOGD_DEVICE, "remove_pending_action (%d): '%s' not pending (expected)", count, action); + return FALSE; } gboolean -nm_device_has_pending_action (NMDevice *device) +nm_device_has_pending_action (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); return !!priv->pending_actions; } @@ -6409,25 +7079,41 @@ nm_device_has_pending_action (NMDevice *device) /***********************************************************/ static void +_cleanup_ip_pre (NMDevice *self, gboolean deconfigure) +{ + 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, deconfigure, FALSE); + arp_cleanup (self); + dhcp6_cleanup (self, deconfigure, FALSE); + linklocal6_cleanup (self); + addrconf6_cleanup (self); + dnsmasq_cleanup (self); + aipd_cleanup (self); +} + +static void _cleanup_generic_pre (NMDevice *self, gboolean deconfigure) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMConnection *connection; /* Clean up when device was deactivated during call to firewall */ - if (priv->fw_manager) { - NMConnection *connection; - - if (priv->fw_call) { - nm_firewall_manager_cancel_call (priv->fw_manager, priv->fw_call); - priv->fw_call = NULL; - } + if (priv->fw_call) { + nm_firewall_manager_cancel_call (nm_firewall_manager_get (), priv->fw_call); + priv->fw_call = NULL; + } - connection = nm_device_get_connection (self); - if (deconfigure && connection) { - nm_firewall_manager_remove_from_zone (priv->fw_manager, - nm_device_get_ip_iface (self), - NULL); - } + connection = nm_device_get_connection (self); + if ( 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); } ip_check_gw_ping_cleanup (self); @@ -6438,17 +7124,8 @@ _cleanup_generic_pre (NMDevice *self, gboolean deconfigure) /* Clear any queued transitions */ nm_device_queued_state_clear (self); - nm_device_queued_ip_config_change_clear (self); - priv->ip4_state = priv->ip6_state = IP_NONE; - - dhcp4_cleanup (self, deconfigure, FALSE); - arp_cleanup (self); - dhcp6_cleanup (self, deconfigure, FALSE); - linklocal6_cleanup (self); - addrconf6_cleanup (self); - dnsmasq_cleanup (self); - aipd_cleanup (self); + _cleanup_ip_pre (self, deconfigure); } static void @@ -6457,20 +7134,33 @@ _cleanup_generic_post (NMDevice *self, gboolean deconfigure) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; + priv->default_route.v4_has = FALSE; + priv->default_route.v4_is_assumed = TRUE; + priv->default_route.v6_has = FALSE; + priv->default_route.v6_is_assumed = TRUE; + + nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self); + nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self); + /* Clean up IP configs; this does not actually deconfigure the * interface; the caller must flush routes and addresses explicitly. */ - nm_device_set_ip4_config (self, NULL, TRUE, &ignored); + nm_device_set_ip4_config (self, NULL, 0, TRUE, &ignored); nm_device_set_ip6_config (self, NULL, TRUE, &ignored); 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->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); + priv->ext_ip4_config_had_any_addresses = FALSE; + priv->ext_ip6_config_had_any_addresses = FALSE; + clear_act_request (self); /* Clear legacy IPv4 address property */ @@ -6507,13 +7197,10 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason) g_return_if_fail (NM_IS_DEVICE (self)); - if (reason == NM_DEVICE_STATE_REASON_NOW_MANAGED) { - nm_log_info (LOGD_DEVICE, "(%s): preparing device", - nm_device_get_iface (self)); - } else { - nm_log_info (LOGD_DEVICE, "(%s): deactivating device (reason '%s') [%d]", - nm_device_get_iface (self), reason_to_string (reason), reason); - } + if (reason == NM_DEVICE_STATE_REASON_NOW_MANAGED) + _LOGI (LOGD_DEVICE, "preparing device"); + else + _LOGI (LOGD_DEVICE, "deactivating device (reason '%s') [%d]", reason_to_string (reason), reason); /* Save whether or not we tried IPv6 for later */ priv = NM_DEVICE_GET_PRIVATE (self); @@ -6521,7 +7208,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason) _cleanup_generic_pre (self, TRUE); /* Turn off kernel IPv6 */ - nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); + set_disable_ipv6 (self, "1"); nm_device_ipv6_sysctl_set (self, "accept_ra", "0"); nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0"); @@ -6547,6 +7234,170 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason) _cleanup_generic_post (self, TRUE); } +static char * +bin2hexstr (const char *bytes, gsize len) +{ + GString *str; + int i; + + g_return_val_if_fail (bytes != NULL, NULL); + g_return_val_if_fail (len > 0, NULL); + + str = g_string_sized_new (len * 2 + 1); + for (i = 0; i < len; i++) { + if (str->len) + g_string_append_c (str, ':'); + g_string_append_printf (str, "%02x", (guint8) bytes[i]); + } + return g_string_free (str, FALSE); +} + +static char * +find_dhcp4_address (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + guint i, n; + + if (!priv->ip4_config) + return NULL; + + n = nm_ip4_config_get_num_addresses (priv->ip4_config); + for (i = 0; i < n; i++) { + const NMPlatformIP4Address *a = nm_ip4_config_get_address (priv->ip4_config, i); + + if (a->source == NM_IP_CONFIG_SOURCE_DHCP) + return g_strdup (nm_utils_inet4_ntop (a->address, NULL)); + } + return NULL; +} + +void +nm_device_spawn_iface_helper (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean configured = FALSE; + NMConnection *connection; + GError *error = NULL; + const char *method; + GPtrArray *argv; + gs_free char *dhcp4_address = NULL; + + if (priv->state != NM_DEVICE_STATE_ACTIVATED) + return; + if (!nm_device_can_assume_connections (self)) + return; + + connection = nm_device_get_connection (self); + g_assert (connection); + + argv = g_ptr_array_sized_new (10); + g_ptr_array_set_free_func (argv, g_free); + + g_ptr_array_add (argv, g_strdup (LIBEXECDIR "/nm-iface-helper")); + g_ptr_array_add (argv, g_strdup ("--ifname")); + g_ptr_array_add (argv, g_strdup (nm_device_get_ip_iface (self))); + g_ptr_array_add (argv, g_strdup ("--uuid")); + g_ptr_array_add (argv, g_strdup (nm_connection_get_uuid (connection))); + + dhcp4_address = find_dhcp4_address (self); + + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + if ( priv->ip4_config + && priv->ip4_state == IP_DONE + && g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0 + && priv->dhcp4_client + && dhcp4_address) { + NMSettingIPConfig *s_ip4; + GBytes *client_id; + char *hex_client_id; + const char *hostname; + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + g_assert (s_ip4); + + g_ptr_array_add (argv, g_strdup ("--priority4")); + g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_ip4_route_metric (self))); + + g_ptr_array_add (argv, g_strdup ("--dhcp4")); + g_ptr_array_add (argv, g_strdup (dhcp4_address)); + if (nm_setting_ip_config_get_may_fail (s_ip4) == FALSE) + g_ptr_array_add (argv, g_strdup ("--dhcp4-required")); + + client_id = nm_dhcp_client_get_client_id (priv->dhcp4_client); + if (client_id) { + g_ptr_array_add (argv, g_strdup ("--dhcp4-clientid")); + hex_client_id = bin2hexstr (g_bytes_get_data (client_id, NULL), + g_bytes_get_size (client_id)); + g_ptr_array_add (argv, hex_client_id); + } + + hostname = nm_dhcp_client_get_hostname (priv->dhcp4_client); + if (client_id) { + g_ptr_array_add (argv, g_strdup ("--dhcp4-hostname")); + g_ptr_array_add (argv, g_strdup (hostname)); + } + + configured = TRUE; + } + + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + if ( priv->ip6_config + && priv->ip6_state == IP_DONE + && g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 + && priv->rdisc + && priv->ac_ip6_config) { + NMSettingIPConfig *s_ip6; + char *hex_iid; + NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT; + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + g_assert (s_ip6); + + g_ptr_array_add (argv, g_strdup ("--priority6")); + g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_ip6_route_metric (self))); + + g_ptr_array_add (argv, g_strdup ("--slaac")); + + if (nm_setting_ip_config_get_may_fail (s_ip6) == FALSE) + g_ptr_array_add (argv, g_strdup ("--slaac-required")); + + g_ptr_array_add (argv, g_strdup ("--slaac-tempaddr")); + g_ptr_array_add (argv, g_strdup_printf ("%d", priv->rdisc_use_tempaddr)); + + if (nm_device_get_ip_iface_identifier (self, &iid)) { + g_ptr_array_add (argv, g_strdup ("--iid")); + hex_iid = bin2hexstr ((const char *) iid.id_u8, sizeof (NMUtilsIPv6IfaceId)); + g_ptr_array_add (argv, hex_iid); + } + + configured = TRUE; + } + + if (configured) { + GPid pid; + + g_ptr_array_add (argv, NULL); + + if (nm_logging_enabled (LOGL_DEBUG, LOGD_DEVICE)) { + char *tmp; + + tmp = g_strjoinv (" ", (char **) argv->pdata); + _LOGD (LOGD_DEVICE, "running '%s'", tmp); + g_free (tmp); + } + + if (g_spawn_async (NULL, (char **) argv->pdata, NULL, + G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &error)) { + _LOGI (LOGD_DEVICE, "spawned helper PID %u", (guint) pid); + } else { + _LOGW (LOGD_DEVICE, "failed to spawn helper: %s", error->message); + g_error_free (error); + } + } + + g_ptr_array_unref (argv); +} + /***********************************************************/ static gboolean @@ -6558,22 +7409,33 @@ ip_config_valid (NMDeviceState state) } static void -notify_ip_properties (NMDevice *device) +notify_ip_properties (NMDevice *self) +{ + g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE); + g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_CONFIG); + g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG); + g_object_notify (G_OBJECT (self), NM_DEVICE_IP6_CONFIG); + g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG); +} + +static void +ip6_managed_setup (NMDevice *self) { - g_object_notify (G_OBJECT (device), NM_DEVICE_IP_IFACE); - g_object_notify (G_OBJECT (device), NM_DEVICE_IP4_CONFIG); - g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP4_CONFIG); - g_object_notify (G_OBJECT (device), NM_DEVICE_IP6_CONFIG); - g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP6_CONFIG); + set_nm_ipv6ll (self, TRUE); + set_disable_ipv6 (self, "1"); + nm_device_ipv6_sysctl_set (self, "accept_ra_defrtr", "0"); + nm_device_ipv6_sysctl_set (self, "accept_ra_pinfo", "0"); + nm_device_ipv6_sysctl_set (self, "accept_ra_rtr_pref", "0"); + nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0"); } static void -_set_state_full (NMDevice *device, +_set_state_full (NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, gboolean quitting) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDeviceState old_state; NMActRequest *req; gboolean no_firmware = FALSE; @@ -6583,7 +7445,7 @@ _set_state_full (NMDevice *device, g_warn_if_fail (priv->in_state_changed == FALSE); priv->in_state_changed = TRUE; - g_return_if_fail (NM_IS_DEVICE (device)); + g_return_if_fail (NM_IS_DEVICE (self)); /* 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 @@ -6599,78 +7461,96 @@ _set_state_full (NMDevice *device, priv->state = state; priv->state_reason = reason; - nm_log_info (LOGD_DEVICE, "(%s): device state change: %s -> %s (reason '%s') [%d %d %d]", - nm_device_get_iface (device), - state_to_string (old_state), - state_to_string (state), - reason_to_string (reason), - old_state, - state, - reason); + _LOGI (LOGD_DEVICE, "device state change: %s -> %s (reason '%s') [%d %d %d]", + state_to_string (old_state), + state_to_string (state), + reason_to_string (reason), + old_state, + state, + reason); /* Clear any queued transitions */ - nm_device_queued_state_clear (device); + nm_device_queued_state_clear (self); - dispatcher_cleanup (device); + dispatcher_cleanup (self); /* Cache the activation request for the dispatcher */ req = priv->act_request ? g_object_ref (priv->act_request) : NULL; if (state <= NM_DEVICE_STATE_UNAVAILABLE) { - _clear_available_connections (device, TRUE); + _clear_available_connections (self, TRUE); g_clear_object (&priv->queued_act_request); } /* Update the available connections list when a device first becomes available */ - if ( state >= NM_DEVICE_STATE_DISCONNECTED - && old_state < NM_DEVICE_STATE_DISCONNECTED) - nm_device_recheck_available_connections (device); + if ( (state >= NM_DEVICE_STATE_DISCONNECTED && old_state < NM_DEVICE_STATE_DISCONNECTED) + || nm_device_get_default_unmanaged (self)) + nm_device_recheck_available_connections (self); /* Handle the new state here; but anything that could trigger * another state change should be done below. */ switch (state) { case NM_DEVICE_STATE_UNMANAGED: - nm_device_set_firmware_missing (device, FALSE); + nm_device_set_firmware_missing (self, FALSE); if (old_state > NM_DEVICE_STATE_UNMANAGED) { /* Clean up if the device is now unmanaged but was activated */ - if (nm_device_get_act_request (device)) - nm_device_cleanup (device, reason); - nm_device_take_down (device, TRUE); - restore_ip6_properties (device); + if (nm_device_get_act_request (self)) + nm_device_cleanup (self, reason); + nm_device_take_down (self, TRUE); + set_nm_ipv6ll (self, FALSE); + restore_ip6_properties (self); } break; case NM_DEVICE_STATE_UNAVAILABLE: if (old_state == NM_DEVICE_STATE_UNMANAGED) { - save_ip6_properties (device); - if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) { - nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1"); - nm_device_ipv6_sysctl_set (device, "accept_ra_defrtr", "0"); - nm_device_ipv6_sysctl_set (device, "accept_ra_pinfo", "0"); - nm_device_ipv6_sysctl_set (device, "accept_ra_rtr_pref", "0"); - nm_device_ipv6_sysctl_set (device, "use_tempaddr", "0"); - } + save_ip6_properties (self); + if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) + ip6_managed_setup (self); } - if (old_state == NM_DEVICE_STATE_UNMANAGED || priv->firmware_missing) { - if (!nm_device_bring_up (device, TRUE, &no_firmware) && no_firmware) - nm_log_warn (LOGD_HW, "(%s): firmware may be missing.", nm_device_get_iface (device)); - nm_device_set_firmware_missing (device, no_firmware ? TRUE : FALSE); + if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) { + if (old_state == NM_DEVICE_STATE_UNMANAGED || priv->firmware_missing) { + if (!nm_device_bring_up (self, TRUE, &no_firmware) && no_firmware) + _LOGW (LOGD_HW, "firmware may be missing."); + nm_device_set_firmware_missing (self, no_firmware ? TRUE : FALSE); + } + + /* Ensure the device gets deactivated in response to stuff like + * carrier changes or rfkill. But don't deactivate devices that are + * about to assume a connection since that defeats the purpose of + * assuming the device's existing connection. + * + * Note that we "deactivate" the device even when coming from + * UNMANAGED, to ensure that it's in a clean state. + */ + nm_device_cleanup (self, reason); } - /* Ensure the device gets deactivated in response to stuff like - * carrier changes or rfkill. But don't deactivate devices that are - * about to assume a connection since that defeats the purpose of - * assuming the device's existing connection. - * - * Note that we "deactivate" the device even when coming from - * UNMANAGED, to ensure that it's in a clean state. - */ - if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) - nm_device_cleanup (device, reason); break; case NM_DEVICE_STATE_DISCONNECTED: - if (old_state > NM_DEVICE_STATE_UNAVAILABLE) - nm_device_cleanup (device, reason); + if (old_state > NM_DEVICE_STATE_DISCONNECTED) { + /* Ensure devices that previously assumed a connection now have + * userspace IPv6LL enabled. + */ + set_nm_ipv6ll (self, TRUE); + + nm_device_cleanup (self, reason); + } else if (old_state < NM_DEVICE_STATE_DISCONNECTED) { + if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) { + /* Ensure IPv6 is set up as it may not have been done when + * entering the UNAVAILABLE state depending on the reason. + */ + ip6_managed_setup (self); + } + } + break; + case NM_DEVICE_STATE_NEED_AUTH: + if (old_state > NM_DEVICE_STATE_NEED_AUTH) { + /* Clean up any half-done IP operations if the device's layer2 + * finds out it needs authentication during IP config. + */ + _cleanup_ip_pre (self, TRUE); + } break; default: break; @@ -6679,11 +7559,11 @@ _set_state_full (NMDevice *device, /* Reset autoconnect flag when the device is activating or connected. */ if ( state >= NM_DEVICE_STATE_PREPARE && state <= NM_DEVICE_STATE_ACTIVATED) - priv->autoconnect = TRUE; + nm_device_set_autoconnect (self, TRUE); - g_object_notify (G_OBJECT (device), NM_DEVICE_STATE); - g_object_notify (G_OBJECT (device), NM_DEVICE_STATE_REASON); - g_signal_emit_by_name (device, "state-changed", state, old_state, reason); + 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); /* Post-process the event after internal notification */ @@ -6695,35 +7575,33 @@ _set_state_full (NMDevice *device, * we can't change states again from the state handler for a variety of * reasons. */ - if (nm_device_is_available (device)) { - nm_log_dbg (LOGD_DEVICE, "(%s): device is available, will transition to DISCONNECTED", - nm_device_get_iface (device)); - nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE); + if (nm_device_is_available (self)) { + _LOGD (LOGD_DEVICE, "device is available, will transition to DISCONNECTED"); + nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE); } else { - if (old_state == NM_DEVICE_STATE_UNMANAGED) { - nm_log_dbg (LOGD_DEVICE, "(%s): device not yet available for transition to DISCONNECTED", - nm_device_get_iface (device)); - } else if ( old_state > NM_DEVICE_STATE_UNAVAILABLE - && nm_device_get_default_unmanaged (device)) - nm_device_queue_state (device, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE); + if (old_state == NM_DEVICE_STATE_UNMANAGED) + _LOGD (LOGD_DEVICE, "device not yet available for transition to DISCONNECTED"); + else if ( old_state > NM_DEVICE_STATE_UNAVAILABLE + && nm_device_get_default_unmanaged (self)) + nm_device_queue_state (self, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE); } break; case NM_DEVICE_STATE_DEACTIVATING: if (quitting) { nm_dispatcher_call_sync (DISPATCHER_ACTION_PRE_DOWN, nm_act_request_get_connection (req), - device); + 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), - device, + self, dispatcher_complete_proceed_state, - device, + self, &priv->dispatcher.call_id)) { /* Just proceed on errors */ - dispatcher_complete_proceed_state (0, device); + dispatcher_complete_proceed_state (0, self); } } break; @@ -6733,26 +7611,32 @@ _set_state_full (NMDevice *device, queued_req = priv->queued_act_request; priv->queued_act_request = NULL; - _device_activate (device, queued_req); + _device_activate (self, queued_req); g_object_unref (queued_req); } else if ( old_state > NM_DEVICE_STATE_DISCONNECTED - && nm_device_get_default_unmanaged (device)) - nm_device_queue_state (device, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE); + && nm_device_get_default_unmanaged (self)) + nm_device_queue_state (self, NM_DEVICE_STATE_UNMANAGED, NM_DEVICE_STATE_REASON_NONE); break; case NM_DEVICE_STATE_ACTIVATED: - nm_log_info (LOGD_DEVICE, "Activation (%s) successful, device activated.", - nm_device_get_iface (device)); - nm_dispatcher_call (DISPATCHER_ACTION_UP, nm_act_request_get_connection (req), device, NULL, NULL, NULL); + _LOGI (LOGD_DEVICE, "Activation: successful, device activated."); + nm_dispatcher_call (DISPATCHER_ACTION_UP, nm_act_request_get_connection (req), self, NULL, NULL, NULL); break; case NM_DEVICE_STATE_FAILED: - connection = nm_device_get_connection (device); - nm_log_warn (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s) failed for connection '%s'", - nm_device_get_iface (device), - connection ? nm_connection_get_id (connection) : "<unknown>"); + if (nm_device_uses_assumed_connection (self)) { + /* Avoid tearing down assumed connection, assume it's connected */ + nm_device_queue_state (self, + NM_DEVICE_STATE_ACTIVATED, + NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + break; + } + + connection = nm_device_get_connection (self); + _LOGW (LOGD_DEVICE | LOGD_WIFI, + "Activation: failed for connection '%s'", + connection ? nm_connection_get_id (connection) : "<unknown>"); /* Notify any slaves of the unexpected failure */ - nm_device_master_release_slaves (device); + nm_device_master_release_slaves (self); /* If the connection doesn't yet have a timestamp, set it to zero so that * we can distinguish between connections we've tried to activate and have @@ -6769,41 +7653,40 @@ _set_state_full (NMDevice *device, * immediately because we can't change states again from the state * handler for a variety of reasons. */ - nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE); + 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 (device); + 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. */ - notify_ip_properties (device); + notify_ip_properties (self); break; case NM_DEVICE_STATE_SECONDARIES: - ip_check_gw_ping_cleanup (device); - nm_log_dbg (LOGD_DEVICE, "(%s): device entered SECONDARIES state", - nm_device_get_iface (device)); + ip_check_gw_ping_cleanup (self); + _LOGD (LOGD_DEVICE, "device entered SECONDARIES state"); break; default: break; } if (state > NM_DEVICE_STATE_DISCONNECTED) - delete_on_deactivate_unschedule (device); + delete_on_deactivate_unschedule (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), device); + 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), device, NULL, NULL, NULL); + nm_dispatcher_call (DISPATCHER_ACTION_DOWN, nm_act_request_get_connection (req), self, NULL, NULL, NULL); } /* IP-related properties are only valid when the device has IP configuration. * If it no longer does, ensure their change notifications are emitted. */ if (ip_config_valid (old_state) && !ip_config_valid (state)) - notify_ip_properties (device); + notify_ip_properties (self); /* Dispose of the cached activation request */ if (req) @@ -6813,11 +7696,11 @@ _set_state_full (NMDevice *device, } void -nm_device_state_changed (NMDevice *device, +nm_device_state_changed (NMDevice *self, NMDeviceState state, NMDeviceStateReason reason) { - _set_state_full (device, state, reason, FALSE); + _set_state_full (self, state, reason, FALSE); } static gboolean @@ -6829,10 +7712,9 @@ queued_set_state (gpointer user_data) NMDeviceStateReason new_reason; if (priv->queued_state.id) { - nm_log_dbg (LOGD_DEVICE, "(%s): running queued state change to %s (id %d)", - nm_device_get_iface (self), - state_to_string (priv->queued_state.state), - priv->queued_state.id); + _LOGD (LOGD_DEVICE, "running queued state change to %s (id %d)", + state_to_string (priv->queued_state.state), + priv->queued_state.id); /* Clear queued state struct before triggering state change, since * the state change may queue another state. @@ -6871,10 +7753,9 @@ nm_device_queue_state (NMDevice *self, /* We should only ever have one delayed state transition at a time */ if (priv->queued_state.id) { - nm_log_warn (LOGD_DEVICE, "(%s): overwriting previously queued state change to %s (%s)", - nm_device_get_iface (self), - state_to_string (priv->queued_state.state), - reason_to_string (priv->queued_state.reason)); + _LOGW (LOGD_DEVICE, "overwriting previously queued state change to %s (%s)", + state_to_string (priv->queued_state.state), + reason_to_string (priv->queued_state.reason)); nm_device_queued_state_clear (self); } @@ -6882,9 +7763,9 @@ nm_device_queue_state (NMDevice *self, priv->queued_state.reason = reason; priv->queued_state.id = g_idle_add (queued_set_state, self); - nm_log_dbg (LOGD_DEVICE, "(%s): queued state change to %s due to %s (id %d)", - nm_device_get_iface (self), state_to_string (state), reason_to_string (reason), - priv->queued_state.id); + _LOGD (LOGD_DEVICE, "queued state change to %s due to %s (id %d)", + state_to_string (state), reason_to_string (reason), + priv->queued_state.id); } NMDeviceState @@ -6905,8 +7786,8 @@ nm_device_queued_state_clear (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); if (priv->queued_state.id) { - nm_log_dbg (LOGD_DEVICE, "(%s): clearing queued state transition (id %d)", - nm_device_get_iface (self), priv->queued_state.id); + _LOGD (LOGD_DEVICE, "clearing queued state transition (id %d)", + priv->queued_state.id); g_source_remove (priv->queued_state.id); nm_device_remove_pending_action (self, queued_state_to_string (priv->queued_state.state), TRUE); } @@ -6914,152 +7795,112 @@ nm_device_queued_state_clear (NMDevice *self) } NMDeviceState -nm_device_get_state (NMDevice *device) +nm_device_get_state (NMDevice *self) { - g_return_val_if_fail (NM_IS_DEVICE (device), NM_DEVICE_STATE_UNKNOWN); + g_return_val_if_fail (NM_IS_DEVICE (self), NM_DEVICE_STATE_UNKNOWN); - return NM_DEVICE_GET_PRIVATE (device)->state; + return NM_DEVICE_GET_PRIVATE (self)->state; } /***********************************************************/ /* NMConfigDevice interface related stuff */ -static guint -nm_device_get_hw_address_length (NMDevice *dev, gboolean *out_permanent) -{ - return NM_DEVICE_GET_CLASS (dev)->get_hw_address_length (dev, out_permanent); -} - -const guint8 * -nm_device_get_hw_address (NMDevice *dev, guint *out_len) +const char * +nm_device_get_hw_address (NMDevice *self) { NMDevicePrivate *priv; - g_return_val_if_fail (NM_IS_DEVICE (dev), NULL); - priv = NM_DEVICE_GET_PRIVATE (dev); - - if (out_len) - *out_len = priv->hw_addr_len; + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->hw_addr_len == 0) - return NULL; - else - return priv->hw_addr; + return priv->hw_addr_len ? priv->hw_addr : NULL; } -gboolean -nm_device_update_hw_address (NMDevice *dev) +static void +nm_device_update_hw_address (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); - gboolean changed = FALSE, permanent = FALSE; - - priv->hw_addr_len = nm_device_get_hw_address_length (dev, &permanent); - - /* If the address can't be changed, don't bother trying */ - if (permanent) - return FALSE; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + int ifindex = nm_device_get_ifindex (self); + const guint8 *hwaddr; + gsize hwaddrlen = 0; - if (priv->hw_addr_len) { - int ifindex = nm_device_get_ip_ifindex (dev); - gsize addrlen; - const guint8 *binaddr; + if (ifindex <= 0) + return; - g_return_val_if_fail (ifindex > 0, FALSE); + hwaddr = nm_platform_link_get_address (ifindex, &hwaddrlen); - binaddr = nm_platform_link_get_address (ifindex, &addrlen); + if (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); - if (addrlen != priv->hw_addr_len) { - nm_log_err (LOGD_HW | LOGD_DEVICE, - "(%s): hardware address is wrong length (got %zd, expected %d)", - nm_device_get_iface (dev), addrlen, priv->hw_addr_len); - } else { - changed = !!memcmp (priv->hw_addr, binaddr, addrlen); - if (changed) { - char *addrstr = nm_utils_hwaddr_ntoa_len (binaddr, priv->hw_addr_len); - - memcpy (priv->hw_addr, binaddr, addrlen); - nm_log_dbg (LOGD_HW | LOGD_DEVICE, - "(%s): hardware address is %s", - nm_device_get_iface (dev), addrstr); - g_free (addrstr); - g_object_notify (G_OBJECT (dev), NM_DEVICE_HW_ADDRESS); - } + _LOGD (LOGD_HW | LOGD_DEVICE, "hardware address now %s", priv->hw_addr); + g_object_notify (G_OBJECT (self), NM_DEVICE_HW_ADDRESS); } } else { - int i; - - /* hw_addr_len is now 0; see if hw_addr was already empty */ - for (i = 0; i < sizeof (priv->hw_addr) && !changed; i++) { - if (priv->hw_addr[i]) - changed = TRUE; - } - if (changed) { - memset (priv->hw_addr, 0, sizeof (priv->hw_addr)); - nm_log_dbg (LOGD_HW | LOGD_DEVICE, - "(%s): previous hardware address is no longer valid", - nm_device_get_iface (dev)); - g_object_notify (G_OBJECT (dev), NM_DEVICE_HW_ADDRESS); + /* Invalid or no hardware address */ + if (priv->hw_addr_len != 0) { + g_clear_pointer (&priv->hw_addr, g_free); + _LOGD (LOGD_HW | LOGD_DEVICE, + "previous hardware address is no longer valid"); + g_object_notify (G_OBJECT (self), NM_DEVICE_HW_ADDRESS); } } - - return changed; + priv->hw_addr_len = hwaddrlen; } gboolean -nm_device_set_hw_addr (NMDevice *device, const guint8 *addr, +nm_device_set_hw_addr (NMDevice *self, const char *addr, const char *detail, guint64 hw_log_domain) { - const char *iface; - char *mac_str = NULL; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean success = FALSE; - guint len; - const guint8 *cur_addr = nm_device_get_hw_address (device, &len); + const char *cur_addr = nm_device_get_hw_address (self); + guint8 addr_bytes[NM_UTILS_HWADDR_LEN_MAX]; g_return_val_if_fail (addr != NULL, FALSE); - iface = nm_device_get_iface (device); - /* Do nothing if current MAC is same */ - if (cur_addr && !memcmp (cur_addr, addr, len)) { - nm_log_dbg (LOGD_DEVICE | hw_log_domain, "(%s): no MAC address change needed", iface); + if (cur_addr && nm_utils_hwaddr_matches (cur_addr, -1, addr, -1)) { + _LOGD (LOGD_DEVICE | hw_log_domain, "no MAC address change needed"); return TRUE; } - - mac_str = nm_utils_hwaddr_ntoa_len (addr, len); + if (!nm_utils_hwaddr_aton (addr, addr_bytes, priv->hw_addr_len)) { + _LOGW (LOGD_DEVICE | hw_log_domain, "invalid MAC address %s", addr); + return FALSE; + } /* Can't change MAC address while device is up */ - nm_device_take_down (device, FALSE); + nm_device_take_down (self, FALSE); - success = nm_platform_link_set_address (nm_device_get_ip_ifindex (device), addr, len); + success = nm_platform_link_set_address (nm_device_get_ip_ifindex (self), addr_bytes, priv->hw_addr_len); if (success) { /* MAC address succesfully changed; update the current MAC to match */ - nm_device_update_hw_address (device); - cur_addr = nm_device_get_hw_address (device, NULL); - if (memcmp (cur_addr, addr, len) == 0) { - nm_log_info (LOGD_DEVICE | hw_log_domain, "(%s): %s MAC address to %s", - iface, detail, mac_str); + nm_device_update_hw_address (self); + cur_addr = nm_device_get_hw_address (self); + if (cur_addr && nm_utils_hwaddr_matches (cur_addr, -1, addr, -1)) { + _LOGI (LOGD_DEVICE | hw_log_domain, "%s MAC address to %s", + detail, addr); } else { - nm_log_warn (LOGD_DEVICE | hw_log_domain, "(%s): new MAC address %s " - "not successfully set", - iface, mac_str); + _LOGW (LOGD_DEVICE | hw_log_domain, + "new MAC address %s not successfully set", addr); success = FALSE; } } else { - nm_log_warn (LOGD_DEVICE | hw_log_domain, "(%s): failed to %s MAC address to %s", - iface, detail, mac_str); + _LOGW (LOGD_DEVICE | hw_log_domain, "failed to %s MAC address to %s", + detail, addr); } - nm_device_bring_up (device, TRUE, NULL); - g_free (mac_str); + nm_device_bring_up (self, TRUE, NULL); return success; } /** * nm_device_spec_match_list: - * @device: an #NMDevice + * @self: an #NMDevice * @specs: (element-type utf8): a list of device specs * - * Checks if @device matches any of the specifications in @specs. The + * Checks if @self matches any of the specifications in @specs. The * currently-supported spec types are: * * "mac:00:11:22:33:44:55" - matches a device with the given @@ -7073,52 +7914,37 @@ nm_device_set_hw_addr (NMDevice *device, const guint8 *addr, * * "*" - matches any device * - * Returns: #TRUE if @device matches one of the specs in @specs + * Returns: #TRUE if @self matches one of the specs in @specs */ gboolean -nm_device_spec_match_list (NMDevice *device, const GSList *specs) +nm_device_spec_match_list (NMDevice *self, const GSList *specs) { - g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); if (!specs) return FALSE; - return NM_DEVICE_GET_CLASS (device)->spec_match_list (device, specs); + return NM_DEVICE_GET_CLASS (self)->spec_match_list (self, specs); } static gboolean -spec_match_list (NMDevice *device, const GSList *specs) +spec_match_list (NMDevice *self, const GSList *specs) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); - char *hwaddr_str; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean matched = FALSE; if (nm_match_spec_string (specs, "*")) return TRUE; - if (priv->hw_addr_len) { - hwaddr_str = nm_utils_hwaddr_ntoa_len (priv->hw_addr, priv->hw_addr_len); - matched = nm_match_spec_hwaddr (specs, hwaddr_str); - g_free (hwaddr_str); - } + if (priv->hw_addr_len) + matched = nm_match_spec_hwaddr (specs, priv->hw_addr); if (!matched) - matched = nm_match_spec_interface_name (specs, nm_device_get_iface (device)); + matched = nm_match_spec_interface_name (specs, nm_device_get_iface (self)); return matched; } -static guint -get_hw_address_length (NMDevice *dev, gboolean *out_permanent) -{ - size_t len; - - if (nm_platform_link_get_address (nm_device_get_ip_ifindex (dev), &len)) - return len; - else - return 0; -} - /***********************************************************/ #define DEFAULT_AUTOCONNECT TRUE @@ -7138,13 +7964,9 @@ nm_device_init (NMDevice *self) priv->unmanaged_flags = NM_UNMANAGED_INTERNAL; priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); -} -static void -nm_device_config_device_interface_init (NMConfigDeviceInterface *iface) -{ - iface->spec_match_list = (gboolean (*) (NMConfigDevice *, const GSList *)) nm_device_spec_match_list; - iface->get_hw_address = (const guint8 * (*) (NMConfigDevice *, guint *)) nm_device_get_hw_address; + priv->default_route.v4_is_assumed = TRUE; + priv->default_route.v6_is_assumed = TRUE; } /* @@ -7152,7 +7974,7 @@ nm_device_config_device_interface_init (NMConfigDeviceInterface *iface) * Returns driver and firmware versions to 'driver_version and' 'firmware_version' */ static gboolean -device_get_driver_info (const char *iface, char **driver_version, char **firmware_version) +device_get_driver_info (NMDevice *self, const char *iface, char **driver_version, char **firmware_version) { struct ethtool_drvinfo drvinfo; struct ifreq req; @@ -7160,7 +7982,7 @@ device_get_driver_info (const char *iface, char **driver_version, char **firmwar fd = socket (PF_INET, SOCK_DGRAM, 0); if (fd < 0) { - nm_log_warn (LOGD_HW, "couldn't open control socket."); + _LOGW (LOGD_HW, "couldn't open control socket."); return FALSE; } @@ -7173,8 +7995,8 @@ device_get_driver_info (const char *iface, char **driver_version, char **firmwar errno = 0; if (ioctl (fd, SIOCETHTOOL, &req) < 0) { - nm_log_dbg (LOGD_HW, "SIOCETHTOOL ioctl() failed: cmd=ETHTOOL_GDRVINFO, iface=%s, errno=%d", - iface, errno); + _LOGD (LOGD_HW, "SIOCETHTOOL ioctl() failed: cmd=ETHTOOL_GDRVINFO, iface=%s, errno=%d", + iface, errno); close (fd); return FALSE; } @@ -7193,7 +8015,7 @@ constructor (GType type, GObjectConstructParam *construct_params) { GObject *object; - NMDevice *dev; + NMDevice *self; NMDevicePrivate *priv; NMPlatform *platform; static guint32 id = 0; @@ -7204,11 +8026,13 @@ constructor (GType type, if (!object) return NULL; - dev = NM_DEVICE (object); - priv = NM_DEVICE_GET_PRIVATE (dev); + 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) { - nm_log_err (LOGD_DEVICE, "No device interface provided, ignoring"); + _LOGE (LOGD_DEVICE, "No device interface provided, ignoring"); goto error; } @@ -7217,52 +8041,60 @@ constructor (GType type, priv->udi = g_strdup_printf ("/virtual/device/placeholder/%d", id++); } - if (NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities) - priv->capabilities |= NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities (dev); + if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities) + priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self); - priv->fw_manager = nm_firewall_manager_get (); + if (priv->ifindex <= 0 && !device_has_capability (self, NM_DEVICE_CAP_IS_NON_KERNEL)) + _LOGW (LOGD_HW, "failed to look up interface index"); - device_get_driver_info (priv->iface, &priv->driver_version, &priv->firmware_version); + device_get_driver_info (self, priv->iface, &priv->driver_version, &priv->firmware_version); /* Watch for external IP config changes */ platform = nm_platform_get (); - g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), dev); - g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), dev); - g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), dev); - g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), dev); - g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), dev); + g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), self); + g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ip_changed), self); + g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), self); + g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ip_changed), self); + g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self); + + if (nm_platform_check_support_user_ipv6ll ()) { + int ip_ifindex = nm_device_get_ip_ifindex (self); + + if (ip_ifindex > 0) + priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (ip_ifindex); + } return object; error: - g_object_unref (dev); + g_object_unref (self); return NULL; } static void constructed (GObject *object) { - NMDevice *dev = NM_DEVICE (object); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); + NMDevice *self = NM_DEVICE (object); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + int master; - nm_device_update_hw_address (dev); + nm_device_update_hw_address (self); - if (NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address) - NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address (dev); + if (NM_DEVICE_GET_CLASS (self)->update_permanent_hw_address) + NM_DEVICE_GET_CLASS (self)->update_permanent_hw_address (self); - if (NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address) - NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address (dev); + if (NM_DEVICE_GET_CLASS (self)->update_initial_hw_address) + NM_DEVICE_GET_CLASS (self)->update_initial_hw_address (self); /* Have to call update_initial_hw_address() before calling get_ignore_carrier() */ - if (device_has_capability (dev, NM_DEVICE_CAP_CARRIER_DETECT)) { - priv->ignore_carrier = nm_config_get_ignore_carrier (nm_config_get (), NM_CONFIG_DEVICE (dev)); - - check_carrier (dev); - nm_log_info (LOGD_HW, - "(%s): carrier is %s%s", - nm_device_get_iface (NM_DEVICE (dev)), - priv->carrier ? "ON" : "OFF", - priv->ignore_carrier ? " (but ignored)" : ""); + if (device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { + priv->ignore_carrier = nm_config_get_ignore_carrier (nm_config_get (), self); + + check_carrier (self); + _LOGI (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; @@ -7271,27 +8103,42 @@ constructed (GObject *object) if (priv->ifindex > 0) { priv->is_software = nm_platform_link_is_software (priv->ifindex); priv->physical_port_id = nm_platform_link_get_physical_port_id (priv->ifindex); + priv->mtu = nm_platform_link_get_mtu (priv->ifindex); } + /* Indicate software device in capabilities. */ + if (priv->is_software) + priv->capabilities |= NM_DEVICE_CAP_IS_SOFTWARE; - if (priv->ifindex > 0) - priv->mtu = nm_platform_link_get_mtu (priv->ifindex); + /* Enslave ourselves */ + master = nm_platform_link_get_master (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, NM_CP_SIGNAL_CONNECTION_ADDED, G_CALLBACK (cp_connection_added), - dev); + self); g_signal_connect (priv->con_provider, NM_CP_SIGNAL_CONNECTION_REMOVED, G_CALLBACK (cp_connection_removed), - dev); + self); g_signal_connect (priv->con_provider, NM_CP_SIGNAL_CONNECTION_UPDATED, G_CALLBACK (cp_connection_updated), - dev); + self); + + /* Update default-unmanaged device available connections immediately, + * since they don't transition from UNMANAGED (and thus the state handler + * doesn't run and update them) until something external happens. + */ + if (nm_device_get_default_unmanaged (self)) { + nm_device_set_autoconnect (self, FALSE); + nm_device_recheck_available_connections (self); + } G_OBJECT_CLASS (nm_device_parent_class)->constructed (object); } @@ -7303,6 +8150,8 @@ dispose (GObject *object) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMPlatform *platform; + _LOGD (LOGD_DEVICE, "dispose(): %s", G_OBJECT_TYPE_NAME (self)); + dispatcher_cleanup (self); _cleanup_generic_pre (self, FALSE); @@ -7310,6 +8159,9 @@ dispose (GObject *object) g_warn_if_fail (priv->slaves == NULL); g_assert (priv->master_ready_id == 0); + /* Let the kernel manage IPv6LL again */ + set_nm_ipv6ll (self, FALSE); + _cleanup_generic_post (self, FALSE); g_clear_pointer (&priv->ip6_saved_properties, g_hash_table_unref); @@ -7342,8 +8194,6 @@ dispose (GObject *object) g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ip_changed), self); g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self); - g_clear_object (&priv->fw_manager); - G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); } @@ -7353,6 +8203,9 @@ finalize (GObject *object) NMDevice *self = NM_DEVICE (object); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + _LOGD (LOGD_DEVICE, "finalize(): %s", G_OBJECT_TYPE_NAME (self)); + + g_free (priv->hw_addr); g_slist_free_full (priv->pending_actions, g_free); g_clear_pointer (&priv->physical_port_id, g_free); g_free (priv->udi); @@ -7363,8 +8216,7 @@ finalize (GObject *object) g_free (priv->driver_version); g_free (priv->firmware_version); g_free (priv->type_desc); - if (priv->dhcp_anycast_address) - g_byte_array_free (priv->dhcp_anycast_address, TRUE); + g_free (priv->dhcp_anycast_address); G_OBJECT_CLASS (nm_device_parent_class)->finalize (object); } @@ -7373,9 +8225,11 @@ static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object); + NMDevice *self = NM_DEVICE (object); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMPlatformLink *platform_device; - const char *hw_addr; + const char *hw_addr, *p; + guint count; switch (prop_id) { case PROP_PLATFORM_DEVICE: @@ -7386,6 +8240,7 @@ set_property (GObject *object, guint prop_id, g_free (priv->iface); priv->iface = g_strdup (platform_device->name); priv->ifindex = platform_device->ifindex; + priv->up = platform_device->up; g_free (priv->driver); priv->driver = g_strdup (platform_device->driver); } @@ -7399,18 +8254,10 @@ set_property (GObject *object, guint prop_id, case PROP_IFACE: if (g_value_get_string (value)) { g_free (priv->iface); - priv->ifindex = 0; priv->iface = g_value_dup_string (value); - - /* Only look up the ifindex if it appears to be an actual kernel - * interface name. eg Bluetooth devices won't have one until we know - * the IP interface. - */ - if (priv->iface && !strchr (priv->iface, ':')) { - priv->ifindex = nm_platform_link_get_ifindex (priv->iface); - if (priv->ifindex <= 0) - nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", priv->iface); - } + priv->ifindex = nm_platform_link_get_ifindex (priv->iface); + if (priv->ifindex > 0) + priv->up = nm_platform_link_is_up (priv->ifindex); } break; case PROP_DRIVER: @@ -7434,7 +8281,7 @@ set_property (GObject *object, guint prop_id, priv->ip4_address = g_value_get_uint (value); break; case PROP_AUTOCONNECT: - priv->autoconnect = g_value_get_boolean (value); + nm_device_set_autoconnect (self, g_value_get_boolean (value)); break; case PROP_FIRMWARE_MISSING: priv->firmware_missing = g_value_get_boolean (value); @@ -7454,19 +8301,30 @@ set_property (GObject *object, guint prop_id, priv->is_master = g_value_get_boolean (value); break; case PROP_HW_ADDRESS: - priv->hw_addr_len = nm_device_get_hw_address_length (NM_DEVICE (object), NULL); - - hw_addr = g_value_get_string (value); - if (!hw_addr) - break; - if (priv->hw_addr_len == 0) { - g_warn_if_fail (*hw_addr == '\0'); + /* construct only */ + p = hw_addr = g_value_get_string (value); + + /* Hardware address length is the number of ':' plus 1 */ + count = 1; + while (p && *p) { + if (*p++ == ':') + count++; + } + if (count < ETH_ALEN || count > NM_UTILS_HWADDR_LEN_MAX) { + if (hw_addr && *hw_addr) { + _LOGW (LOGD_DEVICE, "ignoring hardware address '%s' with unexpected length %d", + hw_addr, count); + } break; } - if (!nm_utils_hwaddr_aton_len (hw_addr, priv->hw_addr, priv->hw_addr_len)) { - g_warning ("Could not parse hw-address '%s'", hw_addr); - memset (priv->hw_addr, 0, sizeof (priv->hw_addr)); + priv->hw_addr_len = count; + g_free (priv->hw_addr); + if (nm_utils_hwaddr_valid (hw_addr, priv->hw_addr_len)) + priv->hw_addr = g_strdup (hw_addr); + else { + _LOGW (LOGD_DEVICE, "could not parse hw-address '%s'", hw_addr); + priv->hw_addr = NULL; } break; default: @@ -7596,10 +8454,7 @@ get_property (GObject *object, guint prop_id, g_value_set_object (value, priv->master); break; case PROP_HW_ADDRESS: - if (priv->hw_addr_len) - g_value_take_string (value, nm_utils_hwaddr_ntoa_len (priv->hw_addr, priv->hw_addr_len)); - else - g_value_set_string (value, NULL); + g_value_set_string (value, priv->hw_addr); break; case PROP_HAS_PENDING_ACTION: g_value_set_boolean (value, nm_device_has_pending_action (self)); @@ -7644,257 +8499,226 @@ nm_device_class_init (NMDeviceClass *klass) klass->bring_up = bring_up; klass->take_down = take_down; klass->carrier_changed = carrier_changed; - klass->get_hw_address_length = get_hw_address_length; + klass->get_ip_iface_identifier = get_ip_iface_identifier; /* Properties */ g_object_class_install_property (object_class, PROP_PLATFORM_DEVICE, - g_param_spec_pointer (NM_DEVICE_PLATFORM_DEVICE, - "Platform Device", - "NMPlatform device object", - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + 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, - "UDI", - "Unique Device Identifier", + g_param_spec_string (NM_DEVICE_UDI, "", "", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_IFACE, - g_param_spec_string (NM_DEVICE_IFACE, - "Interface", - "Interface", + g_param_spec_string (NM_DEVICE_IFACE, "", "", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_IP_IFACE, - g_param_spec_string (NM_DEVICE_IP_IFACE, - "IP Interface", - "IP Interface", + g_param_spec_string (NM_DEVICE_IP_IFACE, "", "", NULL, - G_PARAM_READABLE)); + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_DRIVER, - g_param_spec_string (NM_DEVICE_DRIVER, - "Driver", - "Driver", + g_param_spec_string (NM_DEVICE_DRIVER, "", "", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_DRIVER_VERSION, - g_param_spec_string (NM_DEVICE_DRIVER_VERSION, - "Driver Version", - "Driver Version", + g_param_spec_string (NM_DEVICE_DRIVER_VERSION, "", "", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_FIRMWARE_VERSION, - g_param_spec_string (NM_DEVICE_FIRMWARE_VERSION, - "Firmware Version", - "Firmware Version", + g_param_spec_string (NM_DEVICE_FIRMWARE_VERSION, "", "", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_CAPABILITIES, - g_param_spec_uint (NM_DEVICE_CAPABILITIES, - "Capabilities", - "Capabilities", + g_param_spec_uint (NM_DEVICE_CAPABILITIES, "", "", 0, G_MAXUINT32, NM_DEVICE_CAP_NONE, - G_PARAM_READABLE)); + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_CARRIER, - g_param_spec_boolean (NM_DEVICE_CARRIER, - "Carrier", - "Carrier", + g_param_spec_boolean (NM_DEVICE_CARRIER, "", "", FALSE, - G_PARAM_READABLE)); + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_MTU, - g_param_spec_uint (NM_DEVICE_MTU, - "MTU", - "MTU", + g_param_spec_uint (NM_DEVICE_MTU, "", "", 0, G_MAXUINT32, 1500, - G_PARAM_READABLE)); + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_IP4_ADDRESS, - g_param_spec_uint (NM_DEVICE_IP4_ADDRESS, - "IP4 address", - "IP4 address", + g_param_spec_uint (NM_DEVICE_IP4_ADDRESS, "", "", 0, G_MAXUINT32, 0, /* FIXME */ - G_PARAM_READWRITE)); + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_IP4_CONFIG, - g_param_spec_boxed (NM_DEVICE_IP4_CONFIG, - "IP4 Config", - "IP4 Config", + g_param_spec_boxed (NM_DEVICE_IP4_CONFIG, "", "", DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE)); + 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, - "DHCP4 Config", - "DHCP4 Config", + g_param_spec_boxed (NM_DEVICE_DHCP4_CONFIG, "", "", DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE)); + 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, - "IP6 Config", - "IP6 Config", + g_param_spec_boxed (NM_DEVICE_IP6_CONFIG, "", "", DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE)); + 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, - "DHCP6 Config", - "DHCP6 Config", + g_param_spec_boxed (NM_DEVICE_DHCP6_CONFIG, "", "", DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READWRITE)); + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_STATE, - g_param_spec_uint (NM_DEVICE_STATE, - "State", - "State", + g_param_spec_uint (NM_DEVICE_STATE, "", "", 0, G_MAXUINT32, NM_DEVICE_STATE_UNKNOWN, - G_PARAM_READABLE)); + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_STATE_REASON, - g_param_spec_boxed (NM_DEVICE_STATE_REASON, - "StateReason", - "StateReason", + g_param_spec_boxed (NM_DEVICE_STATE_REASON, "", "", DBUS_TYPE_STATE_REASON_STRUCT, - G_PARAM_READABLE)); + 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, - "ActiveConnection", - "ActiveConnection", + g_param_spec_boxed (NM_DEVICE_ACTIVE_CONNECTION, "", "", DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READABLE)); + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_DEVICE_TYPE, - g_param_spec_uint (NM_DEVICE_DEVICE_TYPE, - "DeviceType", - "DeviceType", + g_param_spec_uint (NM_DEVICE_DEVICE_TYPE, "", "", 0, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + 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, - "Managed", - "Managed", + g_param_spec_boolean (NM_DEVICE_MANAGED, "", "", FALSE, - G_PARAM_READABLE)); + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_AUTOCONNECT, - g_param_spec_boolean (NM_DEVICE_AUTOCONNECT, - "Autoconnect", - "Autoconnect", + g_param_spec_boolean (NM_DEVICE_AUTOCONNECT, "", "", DEFAULT_AUTOCONNECT, - G_PARAM_READWRITE)); + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_FIRMWARE_MISSING, - g_param_spec_boolean (NM_DEVICE_FIRMWARE_MISSING, - "FirmwareMissing", - "Firmware missing", + g_param_spec_boolean (NM_DEVICE_FIRMWARE_MISSING, "", "", FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + 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, - "Type Description", - "Device type description", + g_param_spec_string (NM_DEVICE_TYPE_DESC, "", "", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_RFKILL_TYPE, - g_param_spec_uint (NM_DEVICE_RFKILL_TYPE, - "Rfkill Type", - "Type of rfkill switch (if any) supported by this device", + g_param_spec_uint (NM_DEVICE_RFKILL_TYPE, "", "", RFKILL_TYPE_WLAN, RFKILL_TYPE_MAX, RFKILL_TYPE_UNKNOWN, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_IFINDEX, - g_param_spec_int (NM_DEVICE_IFINDEX, - "Ifindex", - "Ifindex", + g_param_spec_int (NM_DEVICE_IFINDEX, "", "", 0, G_MAXINT, 0, - G_PARAM_READABLE)); + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_AVAILABLE_CONNECTIONS, - g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS, - "AvailableConnections", - "AvailableConnections", + g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS, "", "", DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, - G_PARAM_READABLE)); + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_PHYSICAL_PORT_ID, - g_param_spec_string (NM_DEVICE_PHYSICAL_PORT_ID, - "PhysicalPortId", - "PhysicalPortId", + g_param_spec_string (NM_DEVICE_PHYSICAL_PORT_ID, "", "", NULL, - G_PARAM_READABLE)); + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_IS_MASTER, - g_param_spec_boolean (NM_DEVICE_IS_MASTER, - "IsMaster", - "IsMaster", + g_param_spec_boolean (NM_DEVICE_IS_MASTER, "", "", FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_MASTER, - g_param_spec_object (NM_DEVICE_MASTER, - "Master", - "Master", + g_param_spec_object (NM_DEVICE_MASTER, "", "", NM_TYPE_DEVICE, - G_PARAM_READABLE)); + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_HW_ADDRESS, - g_param_spec_string (NM_DEVICE_HW_ADDRESS, - "Hardware Address", - "Hardware address", + g_param_spec_string (NM_DEVICE_HW_ADDRESS, "", "", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_HAS_PENDING_ACTION, - g_param_spec_boolean (NM_DEVICE_HAS_PENDING_ACTION, - "Has pending action", - "Has pending action", + g_param_spec_boolean (NM_DEVICE_HAS_PENDING_ACTION, "", "", FALSE, - G_PARAM_READABLE)); + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); /* Signals */ signals[STATE_CHANGED] = |