diff options
Diffstat (limited to 'src/devices/nm-device.c')
| -rw-r--r-- | src/devices/nm-device.c | 1945 |
1 files changed, 1403 insertions, 542 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 62259bc4..199acc66 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -21,6 +21,8 @@ #include "nm-default.h" +#include "nm-device.h" + #include <netinet/in.h> #include <string.h> #include <unistd.h> @@ -52,7 +54,7 @@ #include "nm-firewall-manager.h" #include "nm-enum-types.h" #include "nm-settings-connection.h" -#include "nm-connection-provider.h" +#include "nm-settings.h" #include "nm-auth-utils.h" #include "nm-dispatcher.h" #include "nm-config.h" @@ -69,10 +71,22 @@ _LOG_DECLARE_SELF (NMDevice); #include "nmdbus-device.h" +#include "nmdbus-device-statistics.h" G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, NM_TYPE_EXPORTED_OBJECT) -#define NM_DEVICE_GET_PRIVATE(o) ((o)->priv) +#define NM_DEVICE_GET_PRIVATE(self) \ + ({ \ + /* preserve the const-ness of self. Unfortunately, that + * way, @self cannot be a void pointer */ \ + typeof (self) _self = (self); \ + \ + /* Get compiler error if variable is of wrong type */ \ + _nm_unused const NMDevice *_self2 = (_self); \ + \ + nm_assert (NM_IS_DEVICE (_self)); \ + _self->priv; \ + }) enum { STATE_CHANGED, @@ -119,11 +133,15 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice, PROP_IS_MASTER, PROP_MASTER, PROP_HW_ADDRESS, + PROP_PERM_HW_ADDRESS, PROP_HAS_PENDING_ACTION, PROP_METERED, PROP_LLDP_NEIGHBORS, PROP_REAL, PROP_SLAVES, + PROP_REFRESH_RATE_MS, + PROP_TX_BYTES, + PROP_RX_BYTES, ); #define DEFAULT_AUTOCONNECT TRUE @@ -134,6 +152,9 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice, #define PENDING_ACTION_DHCP6 "dhcp6" #define PENDING_ACTION_AUTOCONF6 "autoconf6" +#define DHCP_RESTART_TIMEOUT 120 +#define DHCP_NUM_TRIES_MAX 3 + typedef void (*ActivationHandleFunc) (NMDevice *self); typedef struct { @@ -142,9 +163,9 @@ typedef struct { } ActivationHandleData; typedef enum { - CLEANUP_TYPE_DECONFIGURE, CLEANUP_TYPE_KEEP, CLEANUP_TYPE_REMOVED, + CLEANUP_TYPE_DECONFIGURE, } CleanupType; typedef enum { @@ -163,9 +184,9 @@ typedef struct { typedef struct { NMDevice *slave; - gboolean slave_is_enslaved; - gboolean configure; gulong watch_id; + bool slave_is_enslaved; + bool configure; } SlaveInfo; typedef struct { @@ -192,8 +213,15 @@ typedef struct { NMIP4Config **configs; } ArpingData; +typedef enum { + HW_ADDR_TYPE_UNSET = 0, + HW_ADDR_TYPE_PERMANENT, + HW_ADDR_TYPE_EXPLICIT, + HW_ADDR_TYPE_GENERATED, +} HwAddrType; + typedef struct _NMDevicePrivate { - gboolean in_state_changed; + bool in_state_changed; guint device_link_changed_id; guint device_ip_link_changed_id; @@ -209,7 +237,12 @@ typedef struct _NMDevicePrivate { char * udi; char * iface; /* may change, could be renamed by user */ int ifindex; - gboolean real; + + guint hw_addr_len; + guint8 /*HwAddrType*/ hw_addr_type; + + bool real; + char * ip_iface; int ip_ifindex; NMDeviceType type; @@ -221,19 +254,19 @@ typedef struct _NMDevicePrivate { char * driver_version; char * firmware_version; RfKillType rfkill_type; - gboolean firmware_missing; - gboolean nm_plugin_missing; + bool firmware_missing:1; + bool nm_plugin_missing:1; + bool hw_addr_perm_fake:1; /* whether the permanent HW address could not be read and is a fake */ GHashTable * available_connections; char * hw_addr; - guint hw_addr_len; - char * perm_hw_addr; - char * initial_hw_addr; + char * hw_addr_perm; + char * hw_addr_initial; char * physical_port_id; guint dev_id; NMUnmanagedFlags unmanaged_mask; NMUnmanagedFlags unmanaged_flags; - gboolean is_nm_owned; /* whether the device is a device owned and created by NM */ + bool is_nm_owned; /* whether the device is a device owned and created by NM */ DeleteOnDeactivateData *delete_on_deactivate_data; /* data for scheduled cleanup when deleting link (g_idle_add) */ GCancellable *deactivating_cancellable; @@ -241,7 +274,7 @@ typedef struct _NMDevicePrivate { guint32 ip4_address; NMActRequest * queued_act_request; - gboolean queued_act_request_is_waiting_for_carrier; + bool queued_act_request_is_waiting_for_carrier; NMActRequest * act_request; ActivationHandleData act_handle4; /* for layer2 and IPv4. */ ActivationHandleData act_handle6; @@ -261,11 +294,12 @@ typedef struct _NMDevicePrivate { guint link_connected_id; guint link_disconnected_id; guint carrier_defer_id; - gboolean carrier; + bool carrier; guint carrier_wait_id; - gboolean ignore_carrier; + bool ignore_carrier; + gulong ignore_carrier_id; guint32 mtu; - gboolean up; /* IFF_UP */ + bool up; /* IFF_UP */ /* Generic DHCP stuff */ guint32 dhcp_timeout; @@ -288,14 +322,17 @@ typedef struct _NMDevicePrivate { NMPlatformIP6Route v6; } default_route; - gboolean v4_commit_first_time; - gboolean v6_commit_first_time; + bool v4_commit_first_time; + bool v6_commit_first_time; /* DHCPv4 tracking */ - NMDhcpClient * dhcp4_client; - gulong dhcp4_state_sigid; - NMDhcp4Config * dhcp4_config; - guint dhcp4_restart_id; + struct { + NMDhcpClient * client; + gulong state_sigid; + NMDhcp4Config * config; + guint restart_id; + guint num_tries_left; + } dhcp4; PingInfo gw_ping; @@ -304,7 +341,7 @@ typedef struct _NMDevicePrivate { gulong dnsmasq_state_id; /* Firewall */ - gboolean fw_ready; + bool fw_ready; NMFirewallManagerCallId fw_call; /* IPv4LL stuff */ @@ -325,8 +362,9 @@ typedef struct _NMDevicePrivate { NMIP6Config * ext_ip6_config; /* Stuff added outside NM */ NMIP6Config * ext_ip6_config_captured; /* Configuration captured from platform. */ GSList * vpn6_configs; /* VPNs which use this device */ - gboolean nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */ + bool nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */ guint32 ip6_mtu; + NMIP6Config * dad6_ip6_config; NMRDisc * rdisc; gulong rdisc_changed_id; @@ -340,35 +378,47 @@ typedef struct _NMDevicePrivate { GHashTable * ip6_saved_properties; - NMDhcpClient * dhcp6_client; - NMRDiscDHCPLevel dhcp6_mode; - gulong dhcp6_state_sigid; - NMDhcp6Config * dhcp6_config; - /* IP6 config from DHCP */ - NMIP6Config * dhcp6_ip6_config; - /* Event ID of the current IP6 config from DHCP */ - char * dhcp6_event_id; - guint dhcp6_restart_id; + struct { + NMDhcpClient * client; + NMRDiscDHCPLevel mode; + gulong state_sigid; + NMDhcp6Config * config; + /* IP6 config from DHCP */ + NMIP6Config * ip6_config; + /* Event ID of the current IP6 config from DHCP */ + char * event_id; + guint restart_id; + guint num_tries_left; + } dhcp6; /* allow autoconnect feature */ - gboolean autoconnect; + bool autoconnect; /* master interface for bridge/bond/team slave */ NMDevice * master; - gboolean is_enslaved; - gboolean master_ready_handled; + bool is_enslaved; + bool master_ready_handled; gulong master_ready_id; /* slave management */ - gboolean is_master; + bool is_master; GSList * slaves; /* list of SlaveInfo */ NMMetered metered; - NMConnectionProvider *con_provider; + NMSettings *settings; + NMLldpListener *lldp_listener; guint check_delete_unrealized_id; + + struct { + guint timeout_id; + guint refresh_rate_ms; + guint64 tx_bytes; + guint64 rx_bytes; + } stats; + } NMDevicePrivate; static gboolean nm_device_set_ip4_config (NMDevice *self, @@ -417,6 +467,8 @@ static gboolean dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceState static void nm_device_start_ip_check (NMDevice *self); static void realize_start_setup (NMDevice *self, const NMPlatformLink *plink); static void nm_device_set_mtu (NMDevice *self, guint32 mtu); +static void dhcp_schedule_restart (NMDevice *self, int family, const char *reason); +static void _cancel_activation (NMDevice *self); /***********************************************************/ @@ -524,6 +576,12 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_reason_to_string, NMDeviceStateReason, /***********************************************************/ +NMSettings * +nm_device_get_settings (NMDevice *self) +{ + return NM_DEVICE_GET_PRIVATE (self)->settings; +} + static void init_ip4_config_dns_priority (NMDevice *self, NMIP4Config *config) { @@ -570,6 +628,41 @@ nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps) return NM_FLAGS_ANY (NM_DEVICE_GET_PRIVATE (self)->capabilities, caps); } +static void +_add_capabilities (NMDevice *self, NMDeviceCapabilities capabilities) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (!NM_FLAGS_ALL (priv->capabilities, capabilities)) { + priv->capabilities |= capabilities; + _notify (self, PROP_CAPABILITIES); + } +} + +/***********************************************************/ + +static const char * +_get_stable_id (NMConnection *connection, NMUtilsStableType *out_stable_type) +{ + NMSettingConnection *s_con; + const char *stable_id; + + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (out_stable_type); + + s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (s_con, NULL); + + stable_id = nm_setting_connection_get_stable_id (s_con); + if (!stable_id) { + *out_stable_type = NM_UTILS_STABLE_TYPE_UUID; + return nm_connection_get_uuid (connection); + } + + *out_stable_type = NM_UTILS_STABLE_TYPE_STABLE_ID; + return stable_id; +} + /***********************************************************/ const char * @@ -688,43 +781,175 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface) g_free (old_ip_iface); } +/*****************************************************************************/ + +static void +_stats_update_counters (NMDevice *self, + guint64 tx_bytes, + guint64 rx_bytes) +{ + NMDevicePrivate *priv; + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->stats.tx_bytes != tx_bytes) { + priv->stats.tx_bytes = tx_bytes; + _notify (self, PROP_TX_BYTES); + } + if (priv->stats.rx_bytes != rx_bytes) { + priv->stats.rx_bytes = rx_bytes; + _notify (self, PROP_RX_BYTES); + } +} + +static void +_stats_update_counters_from_pllink (NMDevice *self, const NMPlatformLink *pllink) +{ + _stats_update_counters (self, pllink->tx_bytes, pllink->rx_bytes); +} + +static gboolean +_stats_timeout_cb (gpointer user_data) +{ + NMDevice *self = user_data; + int ifindex; + + ifindex = nm_device_get_ip_ifindex (self); + + _LOGT (LOGD_DEVICE, "stats: refresh %d", ifindex); + + if (ifindex > 0) + nm_platform_link_refresh (NM_PLATFORM_GET, ifindex); + + return G_SOURCE_CONTINUE; +} + +static guint +_stats_refresh_rate_real (guint refresh_rate_ms) +{ + const guint STATS_REFRESH_RATE_MS_MIN = 200; + + if (refresh_rate_ms == 0) + return 0; + + if (refresh_rate_ms < STATS_REFRESH_RATE_MS_MIN) { + /* you cannot set the refresh-rate arbitrarly small. E.g. + * setting to 1ms is just killing. Have a lowest number. */ + return STATS_REFRESH_RATE_MS_MIN; + } + + return refresh_rate_ms; +} + +static void +_stats_set_refresh_rate (NMDevice *self, guint refresh_rate_ms) +{ + NMDevicePrivate *priv; + int ifindex; + guint old_rate; + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->stats.refresh_rate_ms == refresh_rate_ms) + return; + + old_rate = priv->stats.refresh_rate_ms; + priv->stats.refresh_rate_ms = refresh_rate_ms; + _notify (self, PROP_REFRESH_RATE_MS); + + _LOGD (LOGD_DEVICE, "stats: set refresh to %u ms", priv->stats.refresh_rate_ms); + + if (!nm_device_is_real (self)) + return; + + refresh_rate_ms = _stats_refresh_rate_real (refresh_rate_ms); + if (_stats_refresh_rate_real (old_rate) == refresh_rate_ms) + return; + + nm_clear_g_source (&priv->stats.timeout_id); + + if (!refresh_rate_ms) + return; + + /* trigger an inital refresh of the data whenever the refresh-rate changes. + * As we process the result in an idle handler with device_link_changed(), + * we don't get the result right away. */ + ifindex = nm_device_get_ip_ifindex (self); + if (ifindex > 0) + nm_platform_link_refresh (NM_PLATFORM_GET, ifindex); + + priv->stats.timeout_id = g_timeout_add (refresh_rate_ms, _stats_timeout_cb, self); +} + +/*****************************************************************************/ + static gboolean get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMLinkType link_type; - const guint8 *hwaddr = NULL; - size_t hwaddr_len = 0; + const NMPlatformLink *pllink; 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); + g_return_val_if_fail (ifindex > 0, FALSE); - link_type = nm_platform_link_get_type (NM_PLATFORM_GET, ifindex); - g_return_val_if_fail (link_type > NM_LINK_TYPE_UNKNOWN, 0); + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex); + if ( !pllink + || NM_IN_SET (pllink->type, NM_LINK_TYPE_NONE, NM_LINK_TYPE_UNKNOWN)) + return FALSE; - hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &hwaddr_len); - if (!hwaddr_len) + if (pllink->addr.len <= 0) return FALSE; + if (pllink->addr.len > NM_UTILS_HWADDR_LEN_MAX) + g_return_val_if_reached (FALSE); - success = nm_utils_get_ipv6_interface_identifier (link_type, - hwaddr, - hwaddr_len, + success = nm_utils_get_ipv6_interface_identifier (pllink->type, + pllink->addr.data, + pllink->addr.len, priv->dev_id, out_iid); if (!success) { _LOGW (LOGD_HW, "failed to generate interface identifier " - "for link type %u hwaddr_len %zu", link_type, hwaddr_len); + "for link type %u hwaddr_len %u", pllink->type, (unsigned) pllink->addr.len); } return success; } +/** + * nm_device_get_ip_iface_identifier: + * @self: an #NMDevice + * @iid: where to place the interface identifier + * @ignore_token: force creation of a non-tokenized address + * + * Return the interface's identifier for the EUI64 address generation mode. + * It's either a manually set token or and identifier generated in a + * hardware-specific way. + * + * Unless @ignore_token is set the token is preferred. That is the case + * for link-local addresses (to mimic kernel behavior). + * + * Returns: #TRUE if the @iid could be set + */ static gboolean -nm_device_get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *iid) +nm_device_get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *iid, gboolean ignore_token) { - return NM_DEVICE_GET_CLASS (self)->get_ip_iface_identifier (self, iid); + NMSettingIP6Config *s_ip6; + const char *token = NULL; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + if (!ignore_token) { + s_ip6 = (NMSettingIP6Config *) + nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); + g_return_val_if_fail (s_ip6, FALSE); + token = nm_setting_ip6_config_get_token (s_ip6); + } + if (token) + return nm_utils_ipv6_interface_identifier_get_from_token (iid, token); + else + return NM_DEVICE_GET_CLASS (self)->get_ip_iface_identifier (self, iid); } const char * @@ -1029,7 +1254,11 @@ nm_device_get_settings_connection (NMDevice *self) NMConnection * nm_device_get_applied_connection (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + priv = NM_DEVICE_GET_PRIVATE (self); return priv->act_request ? nm_act_request_get_applied_connection (priv->act_request) : NULL; } @@ -1046,23 +1275,12 @@ nm_device_has_unmodified_applied_connection (NMDevice *self, NMSettingCompareFla } NMSetting * -nm_device_get_applied_setting (NMDevice *device, GType setting_type) +nm_device_get_applied_setting (NMDevice *self, GType setting_type) { - NMActRequest *req; - NMSetting *setting = NULL; - - g_return_val_if_fail (NM_IS_DEVICE (device), NULL); - - req = nm_device_get_act_request (device); - if (req) { - NMConnection *connection; - - connection = nm_act_request_get_applied_connection (req); - if (connection) - setting = nm_connection_get_setting (connection, setting_type); - } + NMConnection *connection; - return setting; + connection = nm_device_get_applied_connection (self); + return connection ? nm_connection_get_setting (connection, setting_type) : NULL; } RfKillType @@ -1355,7 +1573,7 @@ nm_device_update_dynamic_ip_setup (NMDevice *self) g_hash_table_remove_all (priv->ip6_saved_properties); - if (priv->dhcp4_client) { + if (priv->dhcp4.client) { if (!nm_device_dhcp4_renew (self, FALSE)) { nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, @@ -1363,7 +1581,7 @@ nm_device_update_dynamic_ip_setup (NMDevice *self) return; } } - if (priv->dhcp6_client) { + if (priv->dhcp6.client) { if (!nm_device_dhcp6_renew (self, FALSE)) { nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, @@ -1467,8 +1685,6 @@ link_disconnect_action_cb (gpointer user_data) priv->carrier_defer_id = 0; - _LOGI (LOGD_DEVICE, "link disconnected (calling deferred action)"); - NM_DEVICE_GET_CLASS (self)->carrier_changed (self, FALSE); return FALSE; @@ -1508,11 +1724,11 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier) nm_device_remove_pending_action (self, "carrier wait", TRUE); _carrier_wait_check_queued_act_request (self); } - } else if (state <= NM_DEVICE_STATE_DISCONNECTED) { - _LOGI (LOGD_DEVICE, "link disconnected"); + } else if ( state <= NM_DEVICE_STATE_DISCONNECTED + && !priv->queued_act_request) { + _LOGD (LOGD_DEVICE, "link disconnected"); klass->carrier_changed (self, FALSE); } else { - _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, self); _LOGD (LOGD_DEVICE, "link disconnected (deferring action for %d seconds) (id=%u)", @@ -1563,13 +1779,14 @@ device_link_changed (NMDevice *self) { NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMUtilsIPv6IfaceId token_iid; gboolean ip_ifname_changed = FALSE; const char *udi; NMPlatformLink info; const NMPlatformLink *pllink; int ifindex; gboolean was_up; + gboolean update_unmanaged_specs = FALSE; + gboolean got_hw_addr = FALSE, had_hw_addr; priv->device_link_changed_id = 0; @@ -1607,6 +1824,14 @@ device_link_changed (NMDevice *self) _notify (self, PROP_DRIVER); } + if (ifindex == nm_device_get_ip_ifindex (self)) + _stats_update_counters_from_pllink (self, &info); + + had_hw_addr = (priv->hw_addr != NULL); + nm_device_update_hw_address (self); + got_hw_addr = (!had_hw_addr && priv->hw_addr); + nm_device_update_permanent_hw_address (self); + if (info.name[0] && strcmp (priv->iface, info.name) != 0) { _LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'", priv->ifindex, priv->iface, info.name); @@ -1616,6 +1841,11 @@ device_link_changed (NMDevice *self) /* If the device has no explicit ip_iface, then changing iface changes ip_iface too. */ ip_ifname_changed = !priv->ip_iface; + if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) + nm_device_set_unmanaged_by_user_settings (self, nm_settings_get_unmanaged_specs (priv->settings)); + else + update_unmanaged_specs = TRUE; + _notify (self, PROP_IFACE); if (ip_ifname_changed) _notify (self, PROP_IP_IFACE); @@ -1629,10 +1859,11 @@ device_link_changed (NMDevice *self) nm_device_emit_recheck_auto_activate (self); } - if (priv->rdisc && nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &token_iid)) { - _LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); - if (nm_rdisc_set_iid (priv->rdisc, token_iid)) + if (priv->rdisc && info.inet6_token.id) { + if (nm_rdisc_set_iid (priv->rdisc, info.inet6_token)) { + _LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); nm_rdisc_start (priv->rdisc); + } } if (klass->link_changed) @@ -1651,18 +1882,23 @@ device_link_changed (NMDevice *self) nm_device_set_unmanaged_by_user_udev (self); + reason = NM_DEVICE_STATE_REASON_NOW_MANAGED; + /* If the device is a external-down candidated but no longer has external * down set, we must clear the platform-unmanaged flag with reason * "assumed". */ if ( nm_device_get_unmanaged_mask (self, NM_UNMANAGED_EXTERNAL_DOWN) && !nm_device_get_unmanaged_flags (self, NM_UNMANAGED_EXTERNAL_DOWN)) { - /* Ensure the assume check is queued before any queued state changes - * from the transition to UNAVAILABLE. - */ - nm_device_queue_recheck_assume (self); - reason = NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED; - } else - reason = NM_DEVICE_STATE_REASON_NOW_MANAGED; + /* actually, user-udev overwrites external-down. So we only assume the device, + * when it is a external-down candidate, which is not managed via udev. */ + if (!nm_device_get_unmanaged_mask (self, NM_UNMANAGED_USER_UDEV)) { + /* Ensure the assume check is queued before any queued state changes + * from the transition to UNAVAILABLE. + */ + nm_device_queue_recheck_assume (self); + reason = NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED; + } + } nm_device_set_unmanaged_by_flags (self, NM_UNMANAGED_PLATFORM_INIT, FALSE, reason); } @@ -1684,6 +1920,23 @@ device_link_changed (NMDevice *self) } } + if (update_unmanaged_specs) + nm_device_set_unmanaged_by_user_settings (self, nm_settings_get_unmanaged_specs (priv->settings)); + + if ( got_hw_addr + && !priv->up + && nm_device_get_state (self) == NM_DEVICE_STATE_UNAVAILABLE) { + /* + * If the device is UNAVAILABLE, any previous try to + * bring it up probably has failed because of the + * invalid hardware address; try again. + */ + nm_device_bring_up (self, TRUE, NULL); + nm_device_queue_recheck_available (self, + NM_DEVICE_STATE_REASON_NONE, + NM_DEVICE_STATE_REASON_NONE); + } + return G_SOURCE_REMOVE; } @@ -1702,6 +1955,8 @@ device_ip_link_changed (NMDevice *self) if (!pllink) return G_SOURCE_REMOVE; + _stats_update_counters_from_pllink (self, pllink); + if (pllink->name[0] && g_strcmp0 (priv->ip_iface, pllink->name)) { _LOGI (LOGD_DEVICE, "interface index %d renamed ip_iface (%d) from '%s' to '%s'", priv->ifindex, nm_device_get_ip_ifindex (self), @@ -1712,6 +1967,7 @@ device_ip_link_changed (NMDevice *self) _notify (self, PROP_IP_IFACE); nm_device_update_dynamic_ip_setup (self); } + return G_SOURCE_REMOVE; } @@ -1817,6 +2073,8 @@ nm_device_realize_start (NMDevice *self, gboolean *out_compatible, GError **error) { + NMPlatformLink plink_copy; + NM_SET_OUT (out_compatible, TRUE); if (plink) { @@ -1831,6 +2089,10 @@ nm_device_realize_start (NMDevice *self, return FALSE; } + if (plink) { + plink_copy = *plink; + plink = &plink_copy; + } realize_start_setup (self, plink); return TRUE; @@ -1962,6 +2224,9 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink) NMDevicePrivate *priv; NMDeviceClass *klass; static guint32 id = 0; + NMDeviceCapabilities capabilities = 0; + NMConfig *config; + guint real_rate; g_return_if_fail (NM_IS_DEVICE (self)); @@ -1985,6 +2250,7 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink) if (plink) { g_return_if_fail (link_type_compatible (self, plink->type, NULL, NULL)); update_device_from_platform_link (self, plink); + _stats_update_counters_from_pllink (self, plink); } if (priv->ifindex > 0) { @@ -1994,7 +2260,7 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink) priv->dev_id = nm_platform_link_get_dev_id (NM_PLATFORM_GET, priv->ifindex); if (nm_platform_link_is_software (NM_PLATFORM_GET, priv->ifindex)) - priv->capabilities |= NM_DEVICE_CAP_IS_SOFTWARE; + capabilities |= NM_DEVICE_CAP_IS_SOFTWARE; priv->mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET, priv->ifindex); _notify (self, PROP_MTU); @@ -2014,7 +2280,9 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink) } if (klass->get_generic_capabilities) - priv->capabilities |= klass->get_generic_capabilities (self); + capabilities |= klass->get_generic_capabilities (self); + + _add_capabilities (self, capabilities); if (!priv->udi) { /* Use a placeholder UDI until we get a real one */ @@ -2028,17 +2296,19 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink) nm_device_update_hw_address (self); nm_device_update_initial_hw_address (self); + nm_device_update_permanent_hw_address (self); /* Note: initial hardware address must be read before calling get_ignore_carrier() */ - if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { - NMConfig *config = nm_config_get (); - - priv->ignore_carrier = nm_config_data_get_ignore_carrier (nm_config_get_data (config), self); - g_signal_connect (G_OBJECT (config), - NM_CONFIG_SIGNAL_CONFIG_CHANGED, - G_CALLBACK (config_changed_update_ignore_carrier), - self); + config = nm_config_get (); + priv->ignore_carrier = nm_config_data_get_ignore_carrier (nm_config_get_data (config), self); + if (!priv->ignore_carrier_id) { + priv->ignore_carrier_id = g_signal_connect (config, + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK (config_changed_update_ignore_carrier), + self); + } + if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { check_carrier (self); _LOGD (LOGD_HW, "carrier is %s%s", @@ -2049,7 +2319,10 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink) priv->carrier = TRUE; } - _notify (self, PROP_CAPABILITIES); + nm_assert (!priv->stats.timeout_id); + real_rate = _stats_refresh_rate_real (priv->stats.refresh_rate_ms); + if (real_rate) + priv->stats.timeout_id = g_timeout_add (real_rate, _stats_timeout_cb, self); klass->realize_start_notify (self, plink); @@ -2214,6 +2487,7 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) _notify (self, PROP_UDI); } if (priv->hw_addr) { + priv->hw_addr_len = 0; g_clear_pointer (&priv->hw_addr, g_free); _notify (self, PROP_HW_ADDRESS); } @@ -2222,14 +2496,21 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) _notify (self, PROP_PHYSICAL_PORT_ID); } - g_clear_pointer (&priv->perm_hw_addr, g_free); - g_clear_pointer (&priv->initial_hw_addr, g_free); + nm_clear_g_source (&priv->stats.timeout_id); + _stats_update_counters (self, 0, 0); + + priv->hw_addr_type = HW_ADDR_TYPE_UNSET; + g_clear_pointer (&priv->hw_addr_perm, g_free); + _notify (self, PROP_PERM_HW_ADDRESS); + g_clear_pointer (&priv->hw_addr_initial, g_free); priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED; if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities) priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self); _notify (self, PROP_CAPABILITIES); + nm_clear_g_signal_handler (nm_config_get (), &priv->ignore_carrier_id); + priv->real = FALSE; _notify (self, PROP_REAL); @@ -2660,6 +2941,8 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason) nm_connection_get_id (connection), master_status); + /* Cancel any pending activation sources */ + _cancel_activation (self); nm_device_queue_state (self, new_state, reason); } else _LOGI (LOGD_DEVICE, "released from master device %s", nm_device_get_iface (priv->master)); @@ -2999,6 +3282,7 @@ nm_device_generate_connection (NMDevice *self, NMDevice *master) gs_free char *uuid = NULL; const char *ip4_method, *ip6_method; GError *error = NULL; + const NMPlatformLink *pllink; /* If update_connection() is not implemented, just fail. */ if (!klass->update_connection) @@ -3045,6 +3329,15 @@ nm_device_generate_connection (NMDevice *self, NMDevice *master) s_ip6 = nm_ip6_config_create_setting (priv->ip6_config); nm_connection_add_setting (connection, s_ip6); + + pllink = nm_platform_link_get (NM_PLATFORM_GET, priv->ifindex); + if (pllink && pllink->inet6_token.id) { + _LOGD (LOGD_IP6, "IPv6 tokenized identifier present"); + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, NM_IN6_ADDR_GEN_MODE_EUI64, + NM_SETTING_IP6_CONFIG_TOKEN, nm_utils_inet6_interface_identifier_to_token (pllink->inet6_token, NULL), + NULL); + } } klass->update_connection (self, connection); @@ -3202,7 +3495,7 @@ nm_device_check_slave_connection_compatible (NMDevice *self, NMConnection *slave static gboolean nm_device_can_assume_connections (NMDevice *self) { - return !!NM_DEVICE_GET_CLASS (self)->update_connection; + return !!NM_DEVICE_GET_CLASS (self)->update_connection; } /** @@ -3218,7 +3511,7 @@ nm_device_can_assume_connections (NMDevice *self) * if there is no active connection or the active connection cannot be * assumed. */ -gboolean +static gboolean nm_device_can_assume_active_connection (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -3255,16 +3548,55 @@ nm_device_can_assume_active_connection (NMDevice *self) return FALSE; method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - if (!_nm_utils_string_in_list (method, assumable_ip6_methods)) + if (!g_strv_contains (assumable_ip6_methods, method)) return FALSE; method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if (!_nm_utils_string_in_list (method, assumable_ip4_methods)) + if (!g_strv_contains (assumable_ip4_methods, method)) + return FALSE; + + return TRUE; +} + +static gboolean +unmanaged_on_quit (NMDevice *self) +{ + /* Leave certain devices alone when quitting so their configuration + * can be taken over when NM restarts. This ensures connectivity while + * NM is stopped. + */ + if (nm_device_uses_assumed_connection (self)) { + /* An assume connection must be left alone */ + return FALSE; + } + + if (!nm_device_get_act_request (self)) { + /* a device without any active connection is either UNAVAILABLE or DISCONNECTED + * state. Since we don't know whether the device was upped by NetworkManager, + * we must leave it up on exit. + */ + return FALSE; + } + + if (!nm_platform_link_can_assume (NM_PLATFORM_GET, nm_device_get_ifindex (self))) { + /* The device has no layer 3 configuration. Leave it up. */ + return FALSE; + } + + if (nm_device_can_assume_active_connection (self)) return FALSE; return TRUE; } +gboolean +nm_device_unmanage_on_quit (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + return NM_DEVICE_GET_CLASS (self)->unmanaged_on_quit (self); +} + static gboolean nm_device_emit_recheck_assume (gpointer user_data) { @@ -3351,7 +3683,7 @@ dnsmasq_state_changed_cb (NMDnsMasqManager *manager, guint32 status, gpointer us switch (status) { case NM_DNSMASQ_STATUS_DEAD: - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED); + nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_SHARED_START_FAILED); break; default: break; @@ -3753,15 +4085,21 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self) if (!priv->master_ready_handled) { NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request); + NMActiveConnection *master; + + master = nm_active_connection_get_master (active); - if (!nm_active_connection_get_master (active)) { + if (!master) { g_warn_if_fail (!priv->master_ready_id); priv->master_ready_handled = TRUE; } else { /* If the master connection is ready for slaves, attach ourselves */ if (nm_active_connection_get_master_ready (active)) master_ready (self, active); - else { + else if (nm_active_connection_get_state (master) >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) { + _LOGD (LOGD_DEVICE, "master connection is deactivating"); + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED); + } else { _LOGD (LOGD_DEVICE, "waiting for master connection to become ready"); if (priv->master_ready_id == 0) { @@ -3815,6 +4153,26 @@ check_ip_failed (NMDevice *self, gboolean may_fail) NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); } +void +nm_device_ip_method_failed (NMDevice *self, int family, NMDeviceStateReason reason) +{ + NMDevicePrivate *priv; + + g_return_if_fail (NM_IS_DEVICE (self)); + g_return_if_fail (family == AF_INET || family == AF_INET6); + priv = NM_DEVICE_GET_PRIVATE (self); + + if (family == AF_INET) + priv->ip4_state = IP_FAIL; + else + priv->ip6_state = IP_FAIL; + + if (get_ip_config_may_fail (self, family)) + check_ip_failed (self, FALSE); + else + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); +} + /* * check_ip_done * @@ -4050,14 +4408,14 @@ ipv4ll_get_ip4_config (NMDevice *self, guint32 lla) memset (&address, 0, sizeof (address)); nm_platform_ip4_address_set_addr (&address, lla, 16); - address.source = NM_IP_CONFIG_SOURCE_IP4LL; + address.addr_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_IP_CONFIG_SOURCE_IP4LL; + route.rt_source = NM_IP_CONFIG_SOURCE_IP4LL; route.metric = nm_device_get_ip4_route_metric (self); nm_ip4_config_add_route (config, &route); @@ -4094,23 +4452,20 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) r = sd_ipv4ll_get_address (ll, &address); if (r < 0) { _LOGE (LOGD_AUTOIP4, "invalid IPv4 link-local address received, error %d.", r); - priv->ip4_state = IP_FAIL; - check_ip_failed (self, FALSE); + nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED); return; } if ((address.s_addr & IPV4LL_NETMASK) != IPV4LL_NETWORK) { _LOGE (LOGD_AUTOIP4, "invalid address %08x received (not link-local).", address.s_addr); - priv->ip4_state = IP_FAIL; - check_ip_failed (self, FALSE); + nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_ERROR); return; } config = ipv4ll_get_ip4_config (self, address.s_addr); if (config == NULL) { _LOGE (LOGD_AUTOIP4, "failed to get IPv4LL config"); - priv->ip4_state = IP_FAIL; - check_ip_failed (self, FALSE); + nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED); return; } @@ -4120,8 +4475,7 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) } else if (priv->ip4_state == IP_DONE) { if (!ip4_config_merge_and_apply (self, config, TRUE, NULL)) { _LOGE (LOGD_AUTOIP4, "failed to update IP4 config for autoip change."); - priv->ip4_state = IP_FAIL; - check_ip_failed (self, FALSE); + nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED); } } else g_assert_not_reached (); @@ -4130,8 +4484,7 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) break; default: _LOGW (LOGD_AUTOIP4, "IPv4LL address no longer valid after event %d.", event); - priv->ip4_state = IP_FAIL; - check_ip_failed (self, FALSE); + nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED); } } @@ -4188,9 +4541,9 @@ ipv4ll_start (NMDevice *self, NMDeviceStateReason *reason) goto fail; } - r = sd_ipv4ll_set_index (priv->ipv4ll, ifindex); + r = sd_ipv4ll_set_ifindex (priv->ipv4ll, ifindex); if (r < 0) { - _LOGE (LOGD_AUTOIP4, "IPv4LL: set_index() failed with error %d", r); + _LOGE (LOGD_AUTOIP4, "IPv4LL: set_ifindex() failed with error %d", r); goto fail; } @@ -4325,23 +4678,23 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - nm_clear_g_source (&priv->dhcp4_restart_id); + nm_clear_g_source (&priv->dhcp4.restart_id); - if (priv->dhcp4_client) { + if (priv->dhcp4.client) { /* Stop any ongoing DHCP transaction on this device */ - nm_clear_g_signal_handler (priv->dhcp4_client, &priv->dhcp4_state_sigid); + nm_clear_g_signal_handler (priv->dhcp4.client, &priv->dhcp4.state_sigid); nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE); if ( cleanup_type == CLEANUP_TYPE_DECONFIGURE || cleanup_type == CLEANUP_TYPE_REMOVED) - nm_dhcp_client_stop (priv->dhcp4_client, release); + nm_dhcp_client_stop (priv->dhcp4.client, release); - g_clear_object (&priv->dhcp4_client); + g_clear_object (&priv->dhcp4.client); } - if (priv->dhcp4_config) { - nm_exported_object_clear_and_unexport (&priv->dhcp4_config); + if (priv->dhcp4.config) { + nm_exported_object_clear_and_unexport (&priv->dhcp4.config); _notify (self, PROP_DHCP4_CONFIG); } } @@ -4495,7 +4848,7 @@ ip4_config_merge_and_apply (NMDevice *self, priv->default_route.v4_has = TRUE; memset (&priv->default_route.v4, 0, sizeof (priv->default_route.v4)); - priv->default_route.v4.source = NM_IP_CONFIG_SOURCE_USER; + priv->default_route.v4.rt_source = NM_IP_CONFIG_SOURCE_USER; priv->default_route.v4.gateway = gateway; priv->default_route.v4.metric = default_route_metric; priv->default_route.v4.mss = nm_ip4_config_get_mss (composite); @@ -4541,28 +4894,30 @@ END_ADD_DEFAULT_ROUTE: return success; } -static void +static gboolean dhcp4_lease_change (NMDevice *self, NMIP4Config *config) { NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - g_return_if_fail (config != NULL); + g_return_val_if_fail (config != NULL, FALSE); if (!ip4_config_merge_and_apply (self, config, TRUE, &reason)) { _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 */ - nm_dispatcher_call (DISPATCHER_ACTION_DHCP4_CHANGE, - nm_device_get_settings_connection (self), - nm_device_get_applied_connection (self), - self, - NULL, - NULL, - NULL); - - nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE); + return FALSE; } + + /* Notify dispatcher scripts of new DHCP4 config */ + nm_dispatcher_call (DISPATCHER_ACTION_DHCP4_CHANGE, + nm_device_get_settings_connection (self), + nm_device_get_applied_connection (self), + self, + NULL, + NULL, + NULL); + + nm_device_remove_pending_action (self, PENDING_ACTION_DHCP4, FALSE); + + return TRUE; } static gboolean @@ -4576,11 +4931,11 @@ dhcp4_restart_cb (gpointer user_data) g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); priv = NM_DEVICE_GET_PRIVATE (self); - priv->dhcp4_restart_id = 0; + priv->dhcp4.restart_id = 0; connection = nm_device_get_applied_connection (self); if (dhcp4_start (self, connection, &reason) == NM_ACT_STAGE_RETURN_FAILURE) - priv->dhcp4_restart_id = g_timeout_add_seconds (120, dhcp4_restart_cb, self); + dhcp_schedule_restart (self, AF_INET, NULL); return FALSE; } @@ -4590,6 +4945,9 @@ dhcp4_fail (NMDevice *self, gboolean timeout) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + _LOGD (LOGD_DHCP4, "DHCPv4 failed: timeout %d, num tries left %u", + timeout, priv->dhcp4.num_tries_left); + dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); /* Don't fail if there are static addresses configured on @@ -4598,8 +4956,7 @@ dhcp4_fail (NMDevice *self, gboolean timeout) if ( priv->ip4_state == IP_DONE && priv->con_ip4_config && nm_ip4_config_get_num_addresses (priv->con_ip4_config) > 0) { - _LOGI (LOGD_DHCP4, "Scheduling DHCPv4 restart because device has IP addresses"); - priv->dhcp4_restart_id = g_timeout_add_seconds (120, dhcp4_restart_cb, self); + dhcp_schedule_restart (self, AF_INET, "device has IP addresses"); return; } @@ -4608,16 +4965,23 @@ dhcp4_fail (NMDevice *self, gboolean timeout) * retry DHCP again. */ if (nm_device_uses_assumed_connection (self)) { - _LOGI (LOGD_DHCP4, "Scheduling DHCPv4 restart because the connection is assumed"); - priv->dhcp4_restart_id = g_timeout_add_seconds (120, dhcp4_restart_cb, self); + dhcp_schedule_restart (self, AF_INET, "connection is assumed"); return; } - if (timeout || (priv->ip4_state == IP_CONF)) + if ( priv->dhcp4.num_tries_left == DHCP_NUM_TRIES_MAX + && (timeout || (priv->ip4_state == IP_CONF))) 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 + else if (priv->ip4_state == IP_DONE) { + /* Don't fail immediately when the lease expires but try to + * restart DHCP for a predefined number of times. + */ + if (priv->dhcp4.num_tries_left) { + priv->dhcp4.num_tries_left--; + dhcp_schedule_restart (self, AF_INET, "lease expired"); + } else + nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + } else g_warn_if_reached (); } @@ -4654,14 +5018,13 @@ dhcp4_state_changed (NMDhcpClient *client, 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); + dhcp4_fail (self, FALSE); break; } - nm_dhcp4_config_set_options (priv->dhcp4_config, options); + nm_dhcp4_config_set_options (priv->dhcp4.config, options); _notify (self, PROP_DHCP4_CONFIG); + priv->dhcp4.num_tries_left = DHCP_NUM_TRIES_MAX; if (priv->ip4_state == IP_CONF) { connection = nm_device_get_applied_connection (self); @@ -4678,8 +5041,10 @@ dhcp4_state_changed (NMDhcpClient *client, ipv4_dad_start (self, configs, dhcp4_dad_cb); } else if (priv->ip4_state == IP_DONE) { - dhcp4_lease_change (self, ip4_config); - nm_device_update_metered (self); + if (dhcp4_lease_change (self, ip4_config)) + nm_device_update_metered (self); + else + dhcp4_fail (self, FALSE); } break; case NM_DHCP_STATE_TIMEOUT: @@ -4735,8 +5100,8 @@ dhcp4_start (NMDevice *self, s_ip4 = nm_connection_get_setting_ip4_config (connection); /* Clear old exported DHCP options */ - nm_exported_object_clear_and_unexport (&priv->dhcp4_config); - priv->dhcp4_config = nm_dhcp4_config_new (); + nm_exported_object_clear_and_unexport (&priv->dhcp4.config); + priv->dhcp4.config = nm_dhcp4_config_new (); hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), &hw_addr_len); if (hw_addr_len) { @@ -4745,8 +5110,8 @@ dhcp4_start (NMDevice *self, } /* 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 (), + 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, @@ -4763,12 +5128,12 @@ dhcp4_start (NMDevice *self, if (tmp) g_byte_array_free (tmp, TRUE); - if (!priv->dhcp4_client) { + if (!priv->dhcp4.client) { *reason = NM_DEVICE_STATE_REASON_DHCP_START_FAILED; return NM_ACT_STAGE_RETURN_FAILURE; } - priv->dhcp4_state_sigid = g_signal_connect (priv->dhcp4_client, + priv->dhcp4.state_sigid = g_signal_connect (priv->dhcp4.client, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (dhcp4_state_changed), self); @@ -4787,7 +5152,7 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release) NMDeviceStateReason reason; NMConnection *connection; - g_return_val_if_fail (priv->dhcp4_client != NULL, FALSE); + g_return_val_if_fail (priv->dhcp4.client != NULL, FALSE); _LOGI (LOGD_DHCP4, "DHCPv4 lease renewal requested"); @@ -4862,7 +5227,7 @@ shared4_new_config (NMDevice *self, NMConnection *connection, NMDeviceStateReaso } config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); - address.source = NM_IP_CONFIG_SOURCE_SHARED; + address.addr_source = NM_IP_CONFIG_SOURCE_SHARED; nm_ip4_config_add_address (config, &address); /* Remove the address lock when the object gets disposed */ @@ -4888,7 +5253,7 @@ connection_ip4_method_requires_carrier (NMConnection *connection, 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); + return g_strv_contains (ip4_carrier_methods, method); } static gboolean @@ -4905,7 +5270,7 @@ connection_ip6_method_requires_carrier (NMConnection *connection, 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); + return g_strv_contains (ip6_carrier_methods, method); } static gboolean @@ -5013,6 +5378,7 @@ act_stage3_ip4_config_start (NMDevice *self, } method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + priv->dhcp4.num_tries_left = DHCP_NUM_TRIES_MAX; /* Start IPv4 addressing based on the method requested */ if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) @@ -5056,25 +5422,25 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE; - g_clear_object (&priv->dhcp6_ip6_config); - g_clear_pointer (&priv->dhcp6_event_id, g_free); - nm_clear_g_source (&priv->dhcp6_restart_id); + priv->dhcp6.mode = NM_RDISC_DHCP_LEVEL_NONE; + g_clear_object (&priv->dhcp6.ip6_config); + g_clear_pointer (&priv->dhcp6.event_id, g_free); + nm_clear_g_source (&priv->dhcp6.restart_id); - if (priv->dhcp6_client) { - nm_clear_g_signal_handler (priv->dhcp6_client, &priv->dhcp6_state_sigid); + if (priv->dhcp6.client) { + nm_clear_g_signal_handler (priv->dhcp6.client, &priv->dhcp6.state_sigid); if ( cleanup_type == CLEANUP_TYPE_DECONFIGURE || cleanup_type == CLEANUP_TYPE_REMOVED) - nm_dhcp_client_stop (priv->dhcp6_client, release); + nm_dhcp_client_stop (priv->dhcp6.client, release); - g_clear_object (&priv->dhcp6_client); + g_clear_object (&priv->dhcp6.client); } nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE); - if (priv->dhcp6_config) { - nm_exported_object_clear_and_unexport (&priv->dhcp6_config); + if (priv->dhcp6.config) { + nm_exported_object_clear_and_unexport (&priv->dhcp6.config); _notify (self, PROP_DHCP6_CONFIG); } } @@ -5104,6 +5470,7 @@ ip6_config_merge_and_apply (NMDevice *self, gboolean ignore_auto_routes = FALSE; gboolean ignore_auto_dns = FALSE; gboolean auto_method = FALSE; + const char *token = NULL; /* Apply ignore-auto-routes and ignore-auto-dns settings */ connection = nm_device_get_applied_connection (self); @@ -5111,9 +5478,14 @@ ip6_config_merge_and_apply (NMDevice *self, NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config (connection); if (s_ip6) { + NMSettingIP6Config *ip6 = NM_SETTING_IP6_CONFIG (s_ip6); + ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes (s_ip6); ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns (s_ip6); + if (nm_setting_ip6_config_get_addr_gen_mode (ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) + token = nm_setting_ip6_config_get_token (ip6); + if (NM_IN_STRSET (nm_setting_ip_config_get_method (s_ip6), NM_SETTING_IP6_CONFIG_METHOD_AUTO, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) @@ -5136,8 +5508,8 @@ ip6_config_merge_and_apply (NMDevice *self, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); } - if (priv->dhcp6_ip6_config) { - nm_ip6_config_merge (composite, priv->dhcp6_ip6_config, + if (priv->dhcp6.ip6_config) { + nm_ip6_config_merge (composite, priv->dhcp6.ip6_config, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); } @@ -5230,7 +5602,7 @@ ip6_config_merge_and_apply (NMDevice *self, priv->default_route.v6_has = TRUE; memset (&priv->default_route.v6, 0, sizeof (priv->default_route.v6)); - priv->default_route.v6.source = NM_IP_CONFIG_SOURCE_USER; + priv->default_route.v6.rt_source = NM_IP_CONFIG_SOURCE_USER; priv->default_route.v6.gateway = *gateway; priv->default_route.v6.metric = nm_device_get_ip6_route_metric (self); priv->default_route.v6.mss = nm_ip6_config_get_mss (composite); @@ -5261,6 +5633,14 @@ END_ADD_DEFAULT_ROUTE: /* Allow setting MTU etc */ if (commit) { + NMUtilsIPv6IfaceId iid; + + if (token && nm_utils_ipv6_interface_identifier_get_from_token (&iid, token)) { + nm_platform_link_set_ipv6_token (NM_PLATFORM_GET, + nm_device_get_ip_ifindex (self), + iid); + } + if (NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit) NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit (self, composite); } @@ -5276,37 +5656,38 @@ END_ADD_DEFAULT_ROUTE: return success; } -static void +static gboolean dhcp6_lease_change (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMSettingsConnection *settings_connection; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - if (priv->dhcp6_ip6_config == NULL) { + if (priv->dhcp6.ip6_config == NULL) { _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; + return FALSE; } - g_assert (priv->dhcp6_client); /* sanity check */ + g_assert (priv->dhcp6.client); /* sanity check */ settings_connection = nm_device_get_settings_connection (self); g_assert (settings_connection); /* Apply the updated config */ - if (ip6_config_merge_and_apply (self, TRUE, &reason) == FALSE) { - _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, - settings_connection, - nm_device_get_applied_connection (self), - self, NULL, NULL, NULL); - - nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE); + if (!ip6_config_merge_and_apply (self, TRUE, &reason)) { + _LOGW (LOGD_DHCP6, "failed to update IPv6 config in response to DHCP event"); + return FALSE; } + + /* Notify dispatcher scripts of new DHCPv6 config */ + nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE, + settings_connection, + nm_device_get_applied_connection (self), + self, NULL, NULL, NULL); + + nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE); + + return TRUE; } static gboolean @@ -5319,30 +5700,63 @@ dhcp6_restart_cb (gpointer user_data) g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); priv = NM_DEVICE_GET_PRIVATE (self); - priv->dhcp6_restart_id = 0; + priv->dhcp6.restart_id = 0; if (!dhcp6_start (self, FALSE, &reason)) - priv->dhcp6_restart_id = g_timeout_add_seconds (120, dhcp6_restart_cb, self); + dhcp_schedule_restart (self, AF_INET6, NULL); return FALSE; } static void +dhcp_schedule_restart (NMDevice *self, int family, const char *reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean inet4; + guint tries_left; + gs_free char *tries_str = NULL; + + g_return_if_fail (family == AF_INET || family == AF_INET6); + inet4 = family == AF_INET; + + tries_left = inet4 ? priv->dhcp4.num_tries_left : priv->dhcp6.num_tries_left; + if (tries_left != DHCP_NUM_TRIES_MAX) + tries_str = g_strdup_printf (", %u tries left", tries_left + 1); + + _LOGI (inet4 ? LOGD_DHCP4 : LOGD_DHCP6, + "scheduling DHCPv%c restart in %u seconds%s%s%s%s", + inet4 ? '4' : '6', + DHCP_RESTART_TIMEOUT, + tries_str ? tries_str : "", + NM_PRINT_FMT_QUOTED (reason, " (reason: ", reason, ")", "")); + + if (inet4) { + priv->dhcp4.restart_id = g_timeout_add_seconds (DHCP_RESTART_TIMEOUT, + dhcp4_restart_cb, self); + } else { + priv->dhcp6.restart_id = g_timeout_add_seconds (DHCP_RESTART_TIMEOUT, + dhcp6_restart_cb, self); + } +} + +static void dhcp6_fail (NMDevice *self, gboolean timeout) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + _LOGD (LOGD_DHCP6, "DHCPv6 failed: timeout %d, num tries left %u", + timeout, priv->dhcp6.num_tries_left); + dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); - if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) { + if (priv->dhcp6.mode == NM_RDISC_DHCP_LEVEL_MANAGED) { /* Don't fail if there are static addresses configured on * the device, instead retry after some time. */ if ( priv->ip6_state == IP_DONE && priv->con_ip6_config && nm_ip6_config_get_num_addresses (priv->con_ip6_config)) { - _LOGI (LOGD_DHCP6, "Scheduling DHCPv6 restart because device has IP addresses"); - priv->dhcp6_restart_id = g_timeout_add_seconds (120, dhcp6_restart_cb, self); + dhcp_schedule_restart (self, AF_INET6, "device has IP addresses"); return; } @@ -5351,16 +5765,23 @@ dhcp6_fail (NMDevice *self, gboolean timeout) * retry DHCP again. */ if (nm_device_uses_assumed_connection (self)) { - _LOGI (LOGD_DHCP6, "Scheduling DHCPv6 restart because the connection is assumed"); - priv->dhcp6_restart_id = g_timeout_add_seconds (120, dhcp6_restart_cb, self); + dhcp_schedule_restart (self, AF_INET6, "connection is assumed"); return; } - if (timeout || (priv->ip6_state == IP_CONF)) + if ( priv->dhcp6.num_tries_left == DHCP_NUM_TRIES_MAX + && (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 + else if (priv->ip6_state == IP_DONE) { + /* Don't fail immediately when the lease expires but try to + * restart DHCP for a predefined number of times. + */ + if (priv->dhcp6.num_tries_left) { + priv->dhcp6.num_tries_left--; + dhcp_schedule_restart (self, AF_INET6, "lease expired"); + } else + nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED); + } else g_warn_if_reached (); } else { /* not a hard failure; just live with the RA info */ @@ -5374,7 +5795,7 @@ dhcp6_timeout (NMDevice *self, NMDhcpClient *client) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_MANAGED) + if (priv->dhcp6.mode == NM_RDISC_DHCP_LEVEL_MANAGED) dhcp6_fail (self, TRUE); else { /* not a hard failure; just live with the RA info */ @@ -5409,32 +5830,34 @@ dhcp6_state_changed (NMDhcpClient *client, */ if ( ip6_config && event_id - && priv->dhcp6_event_id - && !strcmp (event_id, priv->dhcp6_event_id)) { + && priv->dhcp6.event_id + && !strcmp (event_id, priv->dhcp6.event_id)) { for (i = 0; i < nm_ip6_config_get_num_addresses (ip6_config); i++) { - nm_ip6_config_add_address (priv->dhcp6_ip6_config, + nm_ip6_config_add_address (priv->dhcp6.ip6_config, nm_ip6_config_get_address (ip6_config, i)); } } else { - g_clear_object (&priv->dhcp6_ip6_config); - g_clear_pointer (&priv->dhcp6_event_id, g_free); + g_clear_object (&priv->dhcp6.ip6_config); + g_clear_pointer (&priv->dhcp6.event_id, g_free); if (ip6_config) { - priv->dhcp6_ip6_config = g_object_ref (ip6_config); - priv->dhcp6_event_id = g_strdup (event_id); - nm_dhcp6_config_set_options (priv->dhcp6_config, options); + priv->dhcp6.ip6_config = g_object_ref (ip6_config); + priv->dhcp6.event_id = g_strdup (event_id); + nm_dhcp6_config_set_options (priv->dhcp6.config, options); _notify (self, PROP_DHCP6_CONFIG); } } + priv->dhcp6.num_tries_left = DHCP_NUM_TRIES_MAX; + 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 (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DHCP_FAILED); + if (priv->dhcp6.ip6_config == NULL) { + nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_DHCP_FAILED); break; } nm_device_activate_schedule_ip6_config_result (self); } else if (priv->ip6_state == IP_DONE) - dhcp6_lease_change (self); + if (!dhcp6_lease_change (self)) + dhcp6_fail (self, FALSE); break; case NM_DHCP_STATE_TIMEOUT: dhcp6_timeout (self, client); @@ -5449,7 +5872,7 @@ dhcp6_state_changed (NMDhcpClient *client, * may exit right after getting a response from the server. That's * normal. In that case we just ignore the exit. */ - if (priv->dhcp6_mode == NM_RDISC_DHCP_LEVEL_OTHERCONF) + if (priv->dhcp6.mode == NM_RDISC_DHCP_LEVEL_OTHERCONF) break; /* Otherwise, fall through */ case NM_DHCP_STATE_FAIL: @@ -5485,7 +5908,7 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) g_return_val_if_fail (ll_addr, FALSE); - priv->dhcp6_client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (), + 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, @@ -5496,19 +5919,19 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) nm_setting_ip_config_get_dhcp_hostname (s_ip6), priv->dhcp_timeout, priv->dhcp_anycast_address, - (priv->dhcp6_mode == 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); - if (priv->dhcp6_client) { - priv->dhcp6_state_sigid = g_signal_connect (priv->dhcp6_client, + if (priv->dhcp6.client) { + priv->dhcp6.state_sigid = g_signal_connect (priv->dhcp6.client, NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED, G_CALLBACK (dhcp6_state_changed), self); } - return !!priv->dhcp6_client; + return !!priv->dhcp6.client; } static gboolean @@ -5518,12 +5941,12 @@ dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason) NMConnection *connection; NMSettingIPConfig *s_ip6; - nm_exported_object_clear_and_unexport (&priv->dhcp6_config); - priv->dhcp6_config = nm_dhcp6_config_new (); + nm_exported_object_clear_and_unexport (&priv->dhcp6.config); + priv->dhcp6.config = nm_dhcp6_config_new (); - g_warn_if_fail (priv->dhcp6_ip6_config == NULL); - g_clear_object (&priv->dhcp6_ip6_config); - g_clear_pointer (&priv->dhcp6_event_id, g_free); + g_warn_if_fail (priv->dhcp6.ip6_config == NULL); + g_clear_object (&priv->dhcp6.ip6_config); + g_clear_pointer (&priv->dhcp6.event_id, g_free); connection = nm_device_get_applied_connection (self); g_assert (connection); @@ -5559,7 +5982,7 @@ nm_device_dhcp6_renew (NMDevice *self, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - g_return_val_if_fail (priv->dhcp6_client != NULL, FALSE); + g_return_val_if_fail (priv->dhcp6.client != NULL, FALSE); _LOGI (LOGD_DHCP6, "DHCPv6 lease renewal requested"); @@ -5668,11 +6091,17 @@ check_and_add_ipv6ll_addr (NMDevice *self) s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection)); if (s_ip6 && nm_setting_ip6_config_get_addr_gen_mode (s_ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) { - if (!nm_utils_ipv6_addr_set_stable_privacy (&lladdr, - nm_device_get_iface (self), - nm_connection_get_uuid (connection), - priv->linklocal6_dad_counter++, - &error)) { + NMUtilsStableType stable_type; + const char *stable_id; + + stable_id = _get_stable_id (connection, &stable_type); + if ( !stable_id + || !nm_utils_ipv6_addr_set_stable_privacy (stable_type, + &lladdr, + nm_device_get_iface (self), + stable_id, + priv->linklocal6_dad_counter++, + &error)) { _LOGW (LOGD_IP6, "linklocal6: failed to generate an address: %s", error->message); g_clear_error (&error); linklocal6_failed (self); @@ -5690,13 +6119,13 @@ check_and_add_ipv6ll_addr (NMDevice *self) return; } - if (!nm_device_get_ip_iface_identifier (self, &iid)) { + if (!nm_device_get_ip_iface_identifier (self, &iid, TRUE)) { _LOGW (LOGD_IP6, "linklocal6: failed to get interface identifier; IPv6 cannot continue"); return; } _LOGD (LOGD_IP6, "linklocal6: using EUI-64 identifier to generate IPv6LL address"); - nm_utils_ipv6_addr_set_interface_identfier (&lladdr, iid); + nm_utils_ipv6_addr_set_interface_identifier (&lladdr, iid); } _LOGD (LOGD_IP6, "linklocal6: adding IPv6LL address %s", nm_utils_inet6_ntop (&lladdr, NULL)); @@ -5799,8 +6228,9 @@ nm_device_ipv6_set_mtu (NMDevice *self, guint32 mtu) } static void -rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) +rdisc_config_changed (NMRDisc *rdisc, const NMRDiscData *rdata, guint changed_int, NMDevice *self) { + NMRDiscConfigMap changed = changed_int; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int i; int system_support; @@ -5830,11 +6260,9 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) if (changed & NM_RDISC_CONFIG_GATEWAYS) { /* Use the first gateway as ordered in router discovery cache. */ - if (rdisc->gateways->len) { - NMRDiscGateway *gateway = &g_array_index (rdisc->gateways, NMRDiscGateway, 0); - - nm_ip6_config_set_gateway (priv->ac_ip6_config, &gateway->address); - } else + if (rdata->gateways_n) + nm_ip6_config_set_gateway (priv->ac_ip6_config, &rdata->gateways[0].address); + else nm_ip6_config_set_gateway (priv->ac_ip6_config, NULL); } @@ -5847,8 +6275,8 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) * also counts static and temporary addresses when checking * max_addresses. **/ - for (i = 0; i < rdisc->addresses->len; i++) { - NMRDiscAddress *discovered_address = &g_array_index (rdisc->addresses, NMRDiscAddress, i); + for (i = 0; i < rdata->addresses_n; i++) { + const NMRDiscAddress *discovered_address = &rdata->addresses[i]; NMPlatformIP6Address address; memset (&address, 0, sizeof (address)); @@ -5859,7 +6287,7 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) address.preferred = discovered_address->preferred; if (address.preferred > address.lifetime) address.preferred = address.lifetime; - address.source = NM_IP_CONFIG_SOURCE_RDISC; + address.addr_source = NM_IP_CONFIG_SOURCE_RDISC; address.n_ifa_flags = ifa_flags; nm_ip6_config_add_address (priv->ac_ip6_config, &address); @@ -5870,25 +6298,17 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) /* Rebuild route list from router discovery cache. */ nm_ip6_config_reset_routes (priv->ac_ip6_config); - for (i = 0; i < rdisc->routes->len; i++) { - NMRDiscRoute *discovered_route = &g_array_index (rdisc->routes, NMRDiscRoute, i); - NMPlatformIP6Route route; - - /* Only accept non-default routes. The router has no idea what the - * local configuration or user preferences are, so sending routes - * with a prefix length of 0 is quite rude and thus ignored. - */ - if (discovered_route->plen > 0) { - memset (&route, 0, sizeof (route)); - route.network = discovered_route->network; - nm_assert (discovered_route->plen <= 128); - route.plen = discovered_route->plen; - route.gateway = discovered_route->gateway; - 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); - } + for (i = 0; i < rdata->routes_n; i++) { + const NMRDiscRoute *discovered_route = &rdata->routes[i]; + const NMPlatformIP6Route route = { + .network = discovered_route->network, + .plen = discovered_route->plen, + .gateway = discovered_route->gateway, + .rt_source = NM_IP_CONFIG_SOURCE_RDISC, + .metric = nm_device_get_ip6_route_metric (self), + }; + + nm_ip6_config_add_route (priv->ac_ip6_config, &route); } } @@ -5896,36 +6316,30 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) /* Rebuild DNS server list from router discovery cache. */ nm_ip6_config_reset_nameservers (priv->ac_ip6_config); - for (i = 0; i < rdisc->dns_servers->len; i++) { - NMRDiscDNSServer *discovered_server = &g_array_index (rdisc->dns_servers, NMRDiscDNSServer, i); - - nm_ip6_config_add_nameserver (priv->ac_ip6_config, &discovered_server->address); - } + for (i = 0; i < rdata->dns_servers_n; i++) + nm_ip6_config_add_nameserver (priv->ac_ip6_config, &rdata->dns_servers[i].address); } if (changed & NM_RDISC_CONFIG_DNS_DOMAINS) { /* Rebuild domain list from router discovery cache. */ nm_ip6_config_reset_domains (priv->ac_ip6_config); - for (i = 0; i < rdisc->dns_domains->len; i++) { - NMRDiscDNSDomain *discovered_domain = &g_array_index (rdisc->dns_domains, NMRDiscDNSDomain, i); - - nm_ip6_config_add_domain (priv->ac_ip6_config, discovered_domain->domain); - } + for (i = 0; i < rdata->dns_domains_n; i++) + nm_ip6_config_add_domain (priv->ac_ip6_config, rdata->dns_domains[i].domain); } if (changed & NM_RDISC_CONFIG_DHCP_LEVEL) { dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, TRUE); - priv->dhcp6_mode = rdisc->dhcp_level; - if (priv->dhcp6_mode != NM_RDISC_DHCP_LEVEL_NONE) { + priv->dhcp6.mode = rdata->dhcp_level; + if (priv->dhcp6.mode != NM_RDISC_DHCP_LEVEL_NONE) { NMDeviceStateReason reason; _LOGD (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) { + if (priv->dhcp6.mode == NM_RDISC_DHCP_LEVEL_MANAGED) { nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); return; } @@ -5934,10 +6348,10 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) } if (changed & NM_RDISC_CONFIG_HOP_LIMIT) - nm_platform_sysctl_set_ip6_hop_limit_safe (NM_PLATFORM_GET, nm_device_get_ip_iface (self), rdisc->hop_limit); + nm_platform_sysctl_set_ip6_hop_limit_safe (NM_PLATFORM_GET, nm_device_get_ip_iface (self), rdata->hop_limit); if (changed & NM_RDISC_CONFIG_MTU) - priv->ip6_mtu = rdisc->mtu; + priv->ip6_mtu = rdata->mtu; nm_device_activate_schedule_ip6_config_result (self); } @@ -5975,10 +6389,7 @@ addrconf6_start_with_link_ready (NMDevice *self) g_assert (priv->rdisc); - if (nm_platform_link_get_ipv6_token (NM_PLATFORM_GET, priv->ifindex, &iid)) { - _LOGD (LOGD_IP6, "addrconf6: IPv6 tokenized identifier present"); - nm_rdisc_set_iid (priv->rdisc, iid); - } else if (nm_device_get_ip_iface_identifier (self, &iid)) { + if (nm_device_get_ip_iface_identifier (self, &iid, FALSE)) { _LOGD (LOGD_IP6, "addrconf6: using the device EUI-64 identifier"); nm_rdisc_set_iid (priv->rdisc, iid); } else { @@ -6017,6 +6428,8 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) NMActStageReturn ret; NMSettingIP6Config *s_ip6 = NULL; GError *error = NULL; + NMUtilsStableType stable_type; + const char *stable_id; connection = nm_device_get_applied_connection (self); g_assert (connection); @@ -6030,12 +6443,16 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection)); g_assert (s_ip6); - priv->rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET, - nm_device_get_ip_ifindex (self), - nm_device_get_ip_iface (self), - nm_connection_get_uuid (connection), - nm_setting_ip6_config_get_addr_gen_mode (s_ip6), - &error); + stable_id = _get_stable_id (connection, &stable_type); + if (stable_id) { + priv->rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET, + nm_device_get_ip_ifindex (self), + nm_device_get_ip_iface (self), + stable_type, + stable_id, + nm_setting_ip6_config_get_addr_gen_mode (s_ip6), + &error); + } if (!priv->rdisc) { _LOGE (LOGD_IP6, "addrconf6: failed to start router discovery: %s", error->message); g_error_free (error); @@ -6147,11 +6564,18 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) priv->nm_ipv6ll = enable; if (ifindex > 0) { + NMPlatformError plerr; const char *detail = enable ? "enable" : "disable"; _LOGD (LOGD_IP6, "will %s userland IPv6LL", detail); - if (!nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, enable)) - _LOGW (LOGD_IP6, "failed to %s userspace IPv6LL address handling", detail); + plerr = nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, enable); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + _NMLOG (plerr == NM_PLATFORM_ERROR_NOT_FOUND ? LOGL_DEBUG : LOGL_WARN, + LOGD_IP6, + "failed to %s userspace IPv6LL address handling (%s)", + detail, + nm_platform_error_to_string (plerr)); + } if (enable) { /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */ @@ -6286,7 +6710,8 @@ act_stage3_ip6_config_start (NMDevice *self, } } - priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_NONE; + priv->dhcp6.mode = NM_RDISC_DHCP_LEVEL_NONE; + priv->dhcp6.num_tries_left = DHCP_NUM_TRIES_MAX; method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); @@ -6334,7 +6759,7 @@ act_stage3_ip6_config_start (NMDevice *self, } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) { ret = linklocal6_start (self); } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) { - priv->dhcp6_mode = NM_RDISC_DHCP_LEVEL_MANAGED; + priv->dhcp6.mode = NM_RDISC_DHCP_LEVEL_MANAGED; if (!dhcp6_start (self, TRUE, reason)) { /* IPv6 might be disabled; allow IPv4 to proceed */ ret = NM_ACT_STAGE_RETURN_STOP; @@ -6611,7 +7036,6 @@ act_stage4_ip4_config_timeout (NMDevice *self, NMDeviceStateReason *reason) return NM_ACT_STAGE_RETURN_SUCCESS; } - /* * nm_device_activate_stage4_ip4_config_timeout * @@ -6639,7 +7063,6 @@ activate_stage4_ip4_config_timeout (NMDevice *self) check_ip_failed (self, FALSE); } - /* * nm_device_activate_schedule_ip4_config_timeout * @@ -6659,7 +7082,6 @@ nm_device_activate_schedule_ip4_config_timeout (NMDevice *self) activation_source_schedule (self, activate_stage4_ip4_config_timeout, AF_INET); } - static NMActStageReturn act_stage4_ip6_config_timeout (NMDevice *self, NMDeviceStateReason *reason) { @@ -6671,7 +7093,6 @@ act_stage4_ip6_config_timeout (NMDevice *self, NMDeviceStateReason *reason) return NM_ACT_STAGE_RETURN_SUCCESS; } - /* * activate_stage4_ip6_config_timeout * @@ -6699,7 +7120,6 @@ activate_stage4_ip6_config_timeout (NMDevice *self) check_ip_failed (self, FALSE); } - /* * nm_device_activate_schedule_ip6_config_timeout * @@ -6747,19 +7167,12 @@ share_init (void) return TRUE; } -static void -add_share_rule (NMActRequest *req, const char *table, const char *fmt, ...) -{ - va_list args; - char *cmd; - - va_start (args, fmt); - cmd = g_strdup_vprintf (fmt, args); - va_end (args); - - nm_act_request_add_share_rule (req, table, cmd); - g_free (cmd); -} +#define add_share_rule(req, table, ...) \ + G_STMT_START { \ + char *_cmd = g_strdup_printf (__VA_ARGS__); \ + nm_act_request_add_share_rule (req, table, _cmd); \ + g_free (_cmd); \ + } G_STMT_END static gboolean start_sharing (NMDevice *self, NMIP4Config *config) @@ -6906,7 +7319,7 @@ activate_stage5_ip4_config_commit (NMDevice *self) /* NULL to use the existing priv->dev_ip4_config */ if (!ip4_config_merge_and_apply (self, NULL, TRUE, &reason)) { _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Commit) failed"); - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + nm_device_ip_method_failed (self, AF_INET, reason); return; } @@ -6924,7 +7337,7 @@ activate_stage5_ip4_config_commit (NMDevice *self) /* If IPv4 wasn't the first to complete, and DHCP was used, then ensure * dispatcher scripts get the DHCP lease information. */ - if ( priv->dhcp4_client + if ( priv->dhcp4.client && nm_device_activate_ip4_state_in_conf (self) && (nm_device_get_state (self) > NM_DEVICE_STATE_IP_CONFIG)) { /* Notify dispatcher scripts of new DHCP4 config */ @@ -6975,6 +7388,64 @@ nm_device_activate_ip4_state_in_wait (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_WAIT; } +gboolean +nm_device_activate_ip4_state_done (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, FALSE); + return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_DONE; +} + +/* + * Returns a NMIP6Config containing NM-configured addresses which + * have the tentative flag, or NULL if none is present. + */ +static NMIP6Config * +dad6_get_pending_addresses (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMIP6Config *confs[] = { priv->ac_ip6_config, + priv->dhcp6.ip6_config, + priv->con_ip6_config, + priv->wwan_ip6_config }; + const NMPlatformIP6Address *addr, *pl_addr; + NMIP6Config *dad6_config = NULL; + guint i, j, num; + int ifindex; + + ifindex = nm_device_get_ip_ifindex (self); + g_return_val_if_fail (ifindex > 0, NULL); + + /* We are interested only in addresses that we have explicitly configured, + * not in externally added ones. + */ + for (i = 0; i < G_N_ELEMENTS (confs); i++) { + if (confs[i]) { + num = nm_ip6_config_get_num_addresses (confs[i]); + for (j = 0; j < num; j++) { + addr = nm_ip6_config_get_address (confs[i], j); + pl_addr = nm_platform_ip6_address_get (NM_PLATFORM_GET, + ifindex, + addr->address, + addr->plen); + if ( pl_addr + && NM_FLAGS_HAS (pl_addr->n_ifa_flags, IFA_F_TENTATIVE) + && !NM_FLAGS_HAS (pl_addr->n_ifa_flags, IFA_F_DADFAILED) + && !NM_FLAGS_HAS (pl_addr->n_ifa_flags, IFA_F_OPTIMISTIC)) { + _LOGt (LOGD_DEVICE, "IPv6 DAD: pending address %s", + nm_platform_ip6_address_to_string (pl_addr, NULL, 0)); + + if (!dad6_config) + dad6_config = nm_ip6_config_new (ifindex); + + nm_ip6_config_add_address (dad6_config, pl_addr); + } + } + } + } + + return dad6_config; +} + static void activate_stage5_ip6_config_commit (NMDevice *self) { @@ -6998,9 +7469,9 @@ activate_stage5_ip6_config_commit (NMDevice *self) } if (ip6_config_merge_and_apply (self, TRUE, &reason)) { - if ( priv->dhcp6_mode != NM_RDISC_DHCP_LEVEL_NONE + if ( priv->dhcp6.mode != NM_RDISC_DHCP_LEVEL_NONE && priv->ip6_state == IP_CONF) { - if (priv->dhcp6_ip6_config) { + if (priv->dhcp6.ip6_config) { /* If IPv6 wasn't the first IP to complete, and DHCP was used, * then ensure dispatcher scripts get the DHCP lease information. */ @@ -7016,16 +7487,23 @@ activate_stage5_ip6_config_commit (NMDevice *self) return; } } - nm_device_remove_pending_action (self, PENDING_ACTION_DHCP6, FALSE); nm_device_remove_pending_action (self, PENDING_ACTION_AUTOCONF6, FALSE); - /* Enter the IP_CHECK state if this is the first method to complete */ - priv->ip6_state = IP_DONE; - check_ip_done (self); + /* Check if we have to wait for DAD */ + if (priv->ip6_state == IP_CONF && !priv->dad6_ip6_config) { + priv->dad6_ip6_config = dad6_get_pending_addresses (self); + if (priv->dad6_ip6_config) { + _LOGD (LOGD_DEVICE | LOGD_IP6, "IPv6 DAD: waiting termination"); + } else { + /* No tentative addresses, proceed right away */ + priv->ip6_state = IP_DONE; + check_ip_done (self); + } + } } else { _LOGW (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 5 of 5 (IPv6 Commit) failed"); - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + nm_device_ip_method_failed (self, AF_INET6, reason); } } @@ -7059,6 +7537,13 @@ nm_device_activate_ip6_state_in_wait (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->ip6_state == IP_WAIT; } +gboolean +nm_device_activate_ip6_state_done (NMDevice *self) +{ + g_return_val_if_fail (self != NULL, FALSE); + return NM_DEVICE_GET_PRIVATE (self)->ip6_state == IP_DONE; +} + static void clear_act_request (NMDevice *self) { @@ -7223,6 +7708,7 @@ _cleanup_ip6_pre (NMDevice *self, CleanupType cleanup_type) if (nm_clear_g_source (&priv->queued_ip6_config_id)) _LOGD (LOGD_DEVICE, "clearing queued IP6 config change"); + g_clear_object (&priv->dad6_ip6_config); dhcp6_cleanup (self, cleanup_type, FALSE); linklocal6_cleanup (self); addrconf6_cleanup (self); @@ -7891,7 +8377,7 @@ nm_device_steal_connection (NMDevice *self, NMSettingsConnection *connection) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - _LOGW (LOGD_DEVICE, "disconnecting connection '%s' for new activation request.", + _LOGI (LOGD_DEVICE, "disconnecting connection '%s' for new activation request", nm_settings_connection_get_id (connection)); if ( priv->queued_act_request @@ -7970,7 +8456,7 @@ nm_device_get_dhcp4_config (NMDevice *self) { g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - return NM_DEVICE_GET_PRIVATE (self)->dhcp4_config; + return NM_DEVICE_GET_PRIVATE (self)->dhcp4.config; } NMIP4Config * @@ -8287,7 +8773,7 @@ nm_device_get_dhcp6_config (NMDevice *self) { g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - return NM_DEVICE_GET_PRIVATE (self)->dhcp6_config; + return NM_DEVICE_GET_PRIVATE (self)->dhcp6.config; } NMIP6Config * @@ -8595,6 +9081,7 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean device_is_up = FALSE; + NMDeviceCapabilities capabilities; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); @@ -8605,6 +9092,10 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) return FALSE; } + /* Store carrier immediately. */ + if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) + check_carrier (self); + device_is_up = nm_device_is_up (self); if (block && !device_is_up) { int ifindex = nm_device_get_ip_ifindex (self); @@ -8626,6 +9117,13 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) return FALSE; } + /* some ethernet devices fail to report capabilities unless the device + * is up. Re-read the capabilities. */ + capabilities = 0; + if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities) + capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self); + _add_capabilities (self, capabilities); + /* Devices that support carrier detect must be IFF_UP to report carrier * changes; so after setting the device IFF_UP we must suppress startup * complete (via a pending action) until either the carrier turns on, or @@ -8669,10 +9167,6 @@ bring_up (NMDevice *self, gboolean *no_firmware) result = nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, no_firmware); - /* Store carrier immediately. */ - if (result && nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) - check_carrier (self); - return result; } @@ -8808,7 +9302,7 @@ capture_lease_config (NMDevice *self, NMIP6Config **out_ip6_config) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const GSList *connections, *citer; + NMSettingsConnection *const*connections; guint i; gboolean dhcp_used = FALSE; @@ -8841,9 +9335,9 @@ capture_lease_config (NMDevice *self, if (!dhcp_used) return; - connections = nm_connection_provider_get_connections (priv->con_provider); - for (citer = connections; citer; citer = citer->next) { - NMConnection *candidate = citer->data; + connections = nm_settings_get_connections (priv->settings, NULL); + for (i = 0; connections[i]; i++) { + NMConnection *candidate = (NMConnection *) connections[i]; const char *method; if (!nm_device_check_connection_compatible (self, candidate)) @@ -9016,8 +9510,8 @@ update_ip6_config (NMDevice *self, gboolean initial) nm_ip6_config_intersect (priv->con_ip6_config, priv->ext_ip6_config); if (priv->ac_ip6_config) nm_ip6_config_intersect (priv->ac_ip6_config, priv->ext_ip6_config); - if (priv->dhcp6_ip6_config) - nm_ip6_config_intersect (priv->dhcp6_ip6_config, priv->ext_ip6_config); + if (priv->dhcp6.ip6_config) + nm_ip6_config_intersect (priv->dhcp6.ip6_config, priv->ext_ip6_config); if (priv->wwan_ip6_config) nm_ip6_config_intersect (priv->wwan_ip6_config, priv->ext_ip6_config); g_slist_foreach (priv->vpn6_configs, _ip6_config_intersect, priv->ext_ip6_config); @@ -9029,8 +9523,8 @@ update_ip6_config (NMDevice *self, gboolean initial) nm_ip6_config_subtract (priv->ext_ip6_config, priv->con_ip6_config); 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->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); g_slist_foreach (priv->vpn6_configs, _ip6_config_subtract, priv->ext_ip6_config); @@ -9070,9 +9564,7 @@ queued_ip4_config_change (gpointer user_data) return TRUE; priv->queued_ip4_config_id = 0; - g_object_ref (self); update_ip4_config (self, FALSE); - g_object_unref (self); set_unmanaged_external_down (self, TRUE); @@ -9096,16 +9588,15 @@ queued_ip6_config_change (gpointer user_data) return TRUE; priv->queued_ip6_config_id = 0; - g_object_ref (self); update_ip6_config (self, FALSE); - if ( priv->state < NM_DEVICE_STATE_DEACTIVATING + if (priv->state < NM_DEVICE_STATE_DEACTIVATING && nm_platform_link_get (NM_PLATFORM_GET, priv->ifindex)) { /* Handle DAD failures */ for (iter = priv->dad6_failed_addrs; iter; iter = g_slist_next (iter)) { NMPlatformIP6Address *addr = iter->data; - if (addr->source >= NM_IP_CONFIG_SOURCE_USER) + if (addr->addr_source >= NM_IP_CONFIG_SOURCE_USER) continue; _LOGI (LOGD_IP6, "ipv6: duplicate address check failed for the %s address", @@ -9132,7 +9623,18 @@ queued_ip6_config_change (gpointer user_data) g_slist_free_full (priv->dad6_failed_addrs, g_free); priv->dad6_failed_addrs = NULL; - g_object_unref (self); + /* Check if DAD is still pending */ + if ( priv->ip6_state == IP_CONF + && priv->dad6_ip6_config + && priv->ext_ip6_config_captured) { + if (!nm_ip6_config_has_any_dad_pending (priv->ext_ip6_config_captured, + priv->dad6_ip6_config)) { + _LOGD (LOGD_DEVICE | LOGD_IP6, "IPv6 DAD terminated"); + g_clear_object (&priv->dad6_ip6_config); + priv->ip6_state = IP_DONE; + check_ip_done (self); + } + } set_unmanaged_external_down (self, TRUE); @@ -9256,7 +9758,7 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo * Some flags are authoritative, meaning they always cause * the device to be unmanaged (e.g. @NM_UNMANAGED_PLATFORM_INIT). * - * OTOH, some flags can be overwritten. For example NM_UNMANAGED_USER_SETTINGS + * OTOH, some flags can be overwritten. For example NM_UNMANAGED_USER_UDEV * is ignored once NM_UNMANAGED_USER_EXPLICIT is set. The idea is that * the flag from the configuration has no effect once the user explicitly * touches the unmanaged flags. */ @@ -9290,6 +9792,9 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo /* configuration from udev or nm-config overwrites the by-default flag * which is based on the device type. */ flags &= ~NM_UNMANAGED_BY_DEFAULT; + + /* configuration from udev overwrites external-down */ + flags &= ~NM_UNMANAGED_EXTERNAL_DOWN; } if ( NM_FLAGS_HAS (mask, NM_UNMANAGED_IS_SLAVE) @@ -9528,7 +10033,7 @@ nm_device_set_unmanaged_by_flags_queue (NMDevice *self, } void -nm_device_set_unmanaged_by_user_config (NMDevice *self, const GSList *unmanaged_specs) +nm_device_set_unmanaged_by_user_settings (NMDevice *self, const GSList *unmanaged_specs) { NMDevicePrivate *priv; gboolean unmanaged; @@ -9880,50 +10385,45 @@ void nm_device_recheck_available_connections (NMDevice *self) { NMDevicePrivate *priv; - const GSList *connections, *iter; + NMSettingsConnection *const*connections; gboolean changed = FALSE; GHashTableIter h_iter; NMConnection *connection; + guint i; + gs_unref_hashtable GHashTable *prune_list = NULL; g_return_if_fail (NM_IS_DEVICE (self)); priv = NM_DEVICE_GET_PRIVATE(self); - if (priv->con_provider) { - gs_unref_hashtable GHashTable *prune_list = NULL; - - if (g_hash_table_size (priv->available_connections) > 0) { - prune_list = g_hash_table_new (g_direct_hash, g_direct_equal); - g_hash_table_iter_init (&h_iter, priv->available_connections); - while (g_hash_table_iter_next (&h_iter, (gpointer *) &connection, NULL)) - g_hash_table_add (prune_list, connection); - } - - connections = nm_connection_provider_get_connections (priv->con_provider); - for (iter = connections; iter; iter = g_slist_next (iter)) { - connection = NM_CONNECTION (iter->data); - - if (nm_device_check_connection_available (self, - connection, - NM_DEVICE_CHECK_CON_AVAILABLE_NONE, - NULL)) { - if (available_connections_add (self, connection)) - changed = TRUE; - if (prune_list) - g_hash_table_remove (prune_list, connection); - } + if (g_hash_table_size (priv->available_connections) > 0) { + prune_list = g_hash_table_new (g_direct_hash, g_direct_equal); + g_hash_table_iter_init (&h_iter, priv->available_connections); + while (g_hash_table_iter_next (&h_iter, (gpointer *) &connection, NULL)) + g_hash_table_add (prune_list, connection); + } + + connections = nm_settings_get_connections (priv->settings, NULL); + for (i = 0; connections[i]; i++) { + connection = (NMConnection *) connections[i]; + + if (nm_device_check_connection_available (self, + connection, + NM_DEVICE_CHECK_CON_AVAILABLE_NONE, + NULL)) { + if (available_connections_add (self, connection)) + changed = TRUE; + if (prune_list) + g_hash_table_remove (prune_list, connection); } + } - if (prune_list) { - g_hash_table_iter_init (&h_iter, prune_list); - while (g_hash_table_iter_next (&h_iter, (gpointer *) &connection, NULL)) { - if (available_connections_del (self, connection)) - changed = TRUE; - } + if (prune_list) { + g_hash_table_iter_init (&h_iter, prune_list); + while (g_hash_table_iter_next (&h_iter, (gpointer *) &connection, NULL)) { + if (available_connections_del (self, connection)) + changed = TRUE; } - } else { - if (available_connections_del_all (self)) - changed = TRUE; } if (changed) @@ -9984,10 +10484,9 @@ nm_device_get_best_connection (NMDevice *self, } static void -cp_connection_added_or_updated (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) +cp_connection_added_or_updated (NMDevice *self, NMConnection *connection) { gboolean changed; - NMDevice *self = user_data; g_return_if_fail (NM_IS_DEVICE (self)); g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection)); @@ -10007,6 +10506,18 @@ cp_connection_added_or_updated (NMConnectionProvider *cp, NMConnection *connecti } static void +cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) +{ + cp_connection_added_or_updated (user_data, connection); +} + +static void +cp_connection_updated (NMConnectionProvider *cp, NMConnection *connection, gboolean by_user, gpointer user_data) +{ + cp_connection_added_or_updated (user_data, connection); +} + +static void cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) { NMDevice *self = user_data; @@ -10213,6 +10724,7 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) g_clear_object (&priv->ext_ip6_config_captured); g_clear_object (&priv->wwan_ip6_config); g_clear_object (&priv->ip6_config); + g_clear_object (&priv->dad6_ip6_config); g_slist_free_full (priv->vpn4_configs, g_object_unref); priv->vpn4_configs = NULL; @@ -10276,44 +10788,48 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean if (NM_DEVICE_GET_CLASS (self)->deactivate) NM_DEVICE_GET_CLASS (self)->deactivate (self); - /* master: release slaves */ - nm_device_master_release_slaves (self); + if (cleanup_type != CLEANUP_TYPE_KEEP) { + /* master: release slaves */ + nm_device_master_release_slaves (self); - /* slave: mark no longer enslaved */ - if ( priv->master - && nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex) <= 0) - nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + /* slave: mark no longer enslaved */ + if ( priv->master + && nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex) <= 0) + nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); - /* Take out any entries in the routing table and any IP address the device had. */ - ifindex = nm_device_get_ip_ifindex (self); - if (ifindex > 0) { - nm_route_manager_route_flush (nm_route_manager_get (), ifindex); - nm_platform_address_flush (NM_PLATFORM_GET, ifindex); + /* Take out any entries in the routing table and any IP address the device had. */ + ifindex = nm_device_get_ip_ifindex (self); + if (ifindex > 0) { + nm_route_manager_route_flush (nm_route_manager_get (), ifindex); + nm_platform_address_flush (NM_PLATFORM_GET, ifindex); + } } if (priv->lldp_listener) nm_lldp_listener_stop (priv->lldp_listener); nm_device_update_metered (self); + + /* during device cleanup, we want to reset the MAC address of the device + * to the initial state. + * + * We certainly want to do that when reaching the UNMANAGED state... */ + if (nm_device_get_state (self) <= NM_DEVICE_STATE_UNMANAGED) + nm_device_hw_addr_reset (self, "unmanage"); + else { + /* for other device states (UNAVAILABLE, DISCONNECTED), allow the + * device to overwrite the reset behavior, so that Wi-Fi can set + * a randomized MAC address used during scanning. */ + NM_DEVICE_GET_CLASS (self)->deactivate_reset_hw_addr (self); + } + _cleanup_generic_post (self, cleanup_type); } -static char * -bin2hexstr (const char *bytes, gsize len) +static void +deactivate_reset_hw_addr (NMDevice *self) { - 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); + nm_device_hw_addr_reset (self, "deactivate"); } static char * @@ -10329,7 +10845,7 @@ find_dhcp4_address (NMDevice *self) 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) + if (a->addr_source == NM_IP_CONFIG_SOURCE_DHCP) return g_strdup (nm_utils_inet4_ntop (a->address, NULL)); } return NULL; @@ -10346,6 +10862,8 @@ nm_device_spawn_iface_helper (NMDevice *self) GPtrArray *argv; gs_free char *dhcp4_address = NULL; char *logging_backend; + NMUtilsStableType stable_type; + const char *stable_id; if (priv->state != NM_DEVICE_STATE_ACTIVATED) return; @@ -10364,6 +10882,12 @@ nm_device_spawn_iface_helper (NMDevice *self) g_ptr_array_add (argv, g_strdup ("--uuid")); g_ptr_array_add (argv, g_strdup (nm_connection_get_uuid (connection))); + stable_id = _get_stable_id (connection, &stable_type); + if (stable_id && stable_type != NM_UTILS_STABLE_TYPE_UUID) { + g_ptr_array_add (argv, g_strdup ("--stable-id")); + g_ptr_array_add (argv, g_strdup_printf ("%d %s", (int) stable_type, stable_id)); + } + logging_backend = nm_config_get_is_debug (nm_config_get ()) ? g_strdup ("debug") : nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG, @@ -10380,7 +10904,6 @@ nm_device_spawn_iface_helper (NMDevice *self) method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) { NMSettingIPConfig *s_ip4; - char *hex_client_id; s_ip4 = nm_connection_get_setting_ip4_config (connection); g_assert (s_ip4); @@ -10393,25 +10916,26 @@ nm_device_spawn_iface_helper (NMDevice *self) if (nm_setting_ip_config_get_may_fail (s_ip4) == FALSE) g_ptr_array_add (argv, g_strdup ("--dhcp4-required")); - if (priv->dhcp4_client) { + if (priv->dhcp4.client) { const char *hostname, *fqdn; GBytes *client_id; - client_id = nm_dhcp_client_get_client_id (priv->dhcp4_client); + 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); + g_ptr_array_add (argv, + _nm_utils_bin2str (g_bytes_get_data (client_id, NULL), + g_bytes_get_size (client_id), + FALSE)); } - hostname = nm_dhcp_client_get_hostname (priv->dhcp4_client); + hostname = nm_dhcp_client_get_hostname (priv->dhcp4.client); if (hostname) { g_ptr_array_add (argv, g_strdup ("--dhcp4-hostname")); g_ptr_array_add (argv, g_strdup (hostname)); } - fqdn = nm_dhcp_client_get_fqdn (priv->dhcp4_client); + fqdn = nm_dhcp_client_get_fqdn (priv->dhcp4.client); if (fqdn) { g_ptr_array_add (argv, g_strdup ("--dhcp4-fqdn")); g_ptr_array_add (argv, g_strdup (fqdn)); @@ -10424,7 +10948,6 @@ nm_device_spawn_iface_helper (NMDevice *self) method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); if (g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) { NMSettingIPConfig *s_ip6; - char *hex_iid; NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT; s_ip6 = nm_connection_get_setting_ip6_config (connection); @@ -10441,10 +10964,12 @@ nm_device_spawn_iface_helper (NMDevice *self) 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)) { + if (nm_device_get_ip_iface_identifier (self, &iid, FALSE)) { 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); + g_ptr_array_add (argv, + _nm_utils_bin2str (iid.id_u8, + sizeof (NMUtilsIPv6IfaceId), + FALSE)); } g_ptr_array_add (argv, g_strdup ("--addr-gen-mode")); @@ -10654,11 +11179,14 @@ _set_state_full (NMDevice *self, if (old_state > NM_DEVICE_STATE_UNMANAGED) { if (reason == NM_DEVICE_STATE_REASON_REMOVED) { nm_device_cleanup (self, reason, CLEANUP_TYPE_REMOVED); + } else if (reason == NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) { + nm_device_cleanup (self, reason, CLEANUP_TYPE_KEEP); } else { /* Clean up if the device is now unmanaged but was activated */ if (nm_device_get_act_request (self)) nm_device_cleanup (self, reason, CLEANUP_TYPE_DECONFIGURE); nm_device_take_down (self, TRUE); + nm_device_hw_addr_reset (self, "unmanage"); set_nm_ipv6ll (self, FALSE); restore_ip6_properties (self); } @@ -10706,6 +11234,9 @@ _set_state_full (NMDevice *self, } } break; + case NM_DEVICE_STATE_PREPARE: + nm_device_update_initial_hw_address (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 @@ -10726,7 +11257,7 @@ _set_state_full (NMDevice *self, _notify (self, PROP_STATE); _notify (self, PROP_STATE_REASON); - g_signal_emit_by_name (self, NM_DEVICE_STATE_CHANGED, state, old_state, reason); + g_signal_emit (self, signals[STATE_CHANGED], 0, state, old_state, reason); /* Post-process the event after internal notification */ @@ -10749,11 +11280,9 @@ _set_state_full (NMDevice *self, case NM_DEVICE_STATE_DEACTIVATING: _cancel_activation (self); - if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { - /* We cache the ignore_carrier state to not react on config-reloads while the connection - * is active. But on deactivating, reset the ignore-carrier flag to the current state. */ - priv->ignore_carrier = nm_config_data_get_ignore_carrier (NM_CONFIG_GET_DATA, self); - } + /* We cache the ignore_carrier state to not react on config-reloads while the connection + * is active. But on deactivating, reset the ignore-carrier flag to the current state. */ + priv->ignore_carrier = nm_config_data_get_ignore_carrier (NM_CONFIG_GET_DATA, self); if (quitting) { nm_dispatcher_call_sync (DISPATCHER_ACTION_PRE_DOWN, @@ -11026,25 +11555,27 @@ nm_device_get_hw_address (NMDevice *self) g_return_val_if_fail (NM_IS_DEVICE (self), NULL); priv = NM_DEVICE_GET_PRIVATE (self); - return priv->hw_addr_len ? priv->hw_addr : NULL; + nm_assert ((!priv->hw_addr) ^ (priv->hw_addr_len > 0)); + + return priv->hw_addr; } void nm_device_update_hw_address (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ifindex = nm_device_get_ifindex (self); + NMDevicePrivate *priv; const guint8 *hwaddr; gsize hwaddrlen = 0; - static const guint8 zero_hwaddr[ETH_ALEN]; - if (ifindex <= 0) + priv = NM_DEVICE_GET_PRIVATE (self); + if (priv->ifindex <= 0) return; - hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &hwaddrlen); + hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &hwaddrlen); if ( priv->type == NM_DEVICE_TYPE_ETHERNET - && nm_utils_hwaddr_matches (hwaddr, hwaddrlen, zero_hwaddr, sizeof (zero_hwaddr))) + && hwaddr + && nm_utils_hwaddr_matches (hwaddr, hwaddrlen, nm_ip_addr_zero.addr_eth, sizeof (nm_ip_addr_zero.addr_eth))) hwaddrlen = 0; if (hwaddrlen) { @@ -11053,17 +11584,28 @@ nm_device_update_hw_address (NMDevice *self) g_free (priv->hw_addr); priv->hw_addr = nm_utils_hwaddr_ntoa (hwaddr, hwaddrlen); - _LOGD (LOGD_HW | LOGD_DEVICE, "hardware address now %s", priv->hw_addr); + _LOGD (LOGD_HW | LOGD_DEVICE, "hw-addr: hardware address now %s", priv->hw_addr); _notify (self, PROP_HW_ADDRESS); + + if ( !priv->hw_addr_initial + || ( priv->hw_addr_type == HW_ADDR_TYPE_UNSET + && priv->state < NM_DEVICE_STATE_PREPARE + && !nm_device_is_activating (self))) { + /* when we get a hw_addr the first time or while the device + * is not activated (with no explict hw address set), always + * update our inital hw-address as well. */ + nm_device_update_initial_hw_address (self); + } } } else { /* Invalid or no hardware address */ if (priv->hw_addr_len != 0) { - g_clear_pointer (&priv->hw_addr, g_free); - priv->hw_addr_len = 0; _LOGD (LOGD_HW | LOGD_DEVICE, - "previous hardware address is no longer valid"); - _notify (self, PROP_HW_ADDRESS); + "hw-addr: failed reading current MAC address (stay with %s)", + priv->hw_addr); + } else { + _LOGD (LOGD_HW | LOGD_DEVICE, + "hw-addr: failed reading current MAC address"); } } } @@ -11073,84 +11615,365 @@ nm_device_update_initial_hw_address (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->hw_addr_len) { - priv->initial_hw_addr = g_strdup (priv->hw_addr); - _LOGD (LOGD_DEVICE | LOGD_HW, "read initial MAC address %s", priv->initial_hw_addr); + if ( priv->hw_addr + && !nm_streq0 (priv->hw_addr_initial, priv->hw_addr)) { + if ( priv->hw_addr_initial + && priv->hw_addr_type != HW_ADDR_TYPE_UNSET) { + /* once we have the initial hw address set, we only allow + * update if the currenty type is "unset". */ + return; + } + g_free (priv->hw_addr_initial); + priv->hw_addr_initial = g_strdup (priv->hw_addr); + _LOGD (LOGD_DEVICE, "hw-addr: update initial MAC address %s", + priv->hw_addr_initial); + } +} - if (priv->ifindex > 0) { - guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; - size_t len = 0; +void +nm_device_update_permanent_hw_address (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; + size_t len = 0; + gboolean success_read; - if (nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len)) { - g_warn_if_fail (len == priv->hw_addr_len); - priv->perm_hw_addr = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len); - _LOGD (LOGD_DEVICE | LOGD_HW, "read permanent MAC address %s", - priv->perm_hw_addr); - } else { - /* Fall back to current address */ - _LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address"); - priv->perm_hw_addr = g_strdup (priv->hw_addr); + if (priv->hw_addr_perm) { + /* the permanent hardware address is only read once and not + * re-read later. + * + * Except during unrealize/realize cycles, where we clear the permanent + * hardware address during unrealization. */ + return; + } + + if (priv->ifindex <= 0) + return; + + if (!priv->hw_addr_len) { + nm_device_update_hw_address (self); + if (!priv->hw_addr_len) + return; + } + + success_read = nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len); + if (!success_read || len != priv->hw_addr_len) { + /* Fall back to current address. We use the fake address and keep it + * until the device unrealizes. + * + * In some cases it might be necessary to know whether this is a "real" or + * a temporary address (fake). */ + _LOGD (LOGD_HW | LOGD_ETHER, "hw-addr: %s (use current: %s)", + success_read + ? "read HW addr length of permanent MAC address differs" + : "unable to read permanent MAC address", + priv->hw_addr); + priv->hw_addr_perm_fake = TRUE; + priv->hw_addr_perm = g_strdup (priv->hw_addr); + } else { + priv->hw_addr_perm_fake = FALSE; + priv->hw_addr_perm = nm_utils_hwaddr_ntoa (buf, len); + _LOGD (LOGD_DEVICE, "hw-addr: read permanent MAC address '%s'", + priv->hw_addr_perm); + } + _notify (self, PROP_PERM_HW_ADDRESS); +} + +static const char * +_get_cloned_mac_address_setting (NMDevice *self, NMConnection *connection, gboolean is_wifi, char **out_addr) +{ + NMSetting *setting; + const char *addr = NULL; + + nm_assert (out_addr && !*out_addr); + + setting = nm_connection_get_setting (connection, + is_wifi ? NM_TYPE_SETTING_WIRELESS : NM_TYPE_SETTING_WIRED); + if (setting) { + addr = is_wifi + ? nm_setting_wireless_get_cloned_mac_address ((NMSettingWireless *) setting) + : nm_setting_wired_get_cloned_mac_address ((NMSettingWired *) setting); + } + + if (!addr) { + gs_free char *a = NULL; + + a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + is_wifi ? "wifi.cloned-mac-address" : "ethernet.cloned-mac-address", + self); + /* default is permanent. */ + addr = NM_CLONED_MAC_PERMANENT; + + if (!a) { + if (is_wifi) { + NMSettingMacRandomization v; + + /* for backward compatibility, read the deprecated wifi.mac-address-randomization setting. */ + a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "wifi." NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION, + self); + v = _nm_utils_ascii_str_to_int64 (a, 10, + NM_SETTING_MAC_RANDOMIZATION_DEFAULT, + NM_SETTING_MAC_RANDOMIZATION_ALWAYS, + NM_SETTING_MAC_RANDOMIZATION_DEFAULT); + if (v == NM_SETTING_MAC_RANDOMIZATION_ALWAYS) + addr = NM_CLONED_MAC_RANDOM; } - } + } else if ( NM_CLONED_MAC_IS_SPECIAL (a) + || nm_utils_hwaddr_valid (a, ETH_ALEN)) + addr = *out_addr = g_steal_pointer (&a); } + + return addr; +} + +static const char * +_get_generate_mac_address_mask_setting (NMDevice *self, NMConnection *connection, gboolean is_wifi, char **out_value) +{ + NMSetting *setting; + const char *value = NULL; + char *a; + + nm_assert (out_value && !*out_value); + + setting = nm_connection_get_setting (connection, + is_wifi ? NM_TYPE_SETTING_WIRELESS : NM_TYPE_SETTING_WIRED); + if (setting) { + value = is_wifi + ? nm_setting_wireless_get_generate_mac_address_mask ((NMSettingWireless *) setting) + : nm_setting_wired_get_generate_mac_address_mask ((NMSettingWired *) setting); + if (value) + return value; + } + + a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + is_wifi ? "wifi.generate-mac-address-mask" : "ethernet.generate-mac-mac-address-mask", + self); + if (!a) + return NULL; + *out_value = a; + return a; } gboolean -nm_device_set_hw_addr (NMDevice *self, const char *addr, - const char *detail, guint64 hw_log_domain) +nm_device_hw_addr_is_explict (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + return !NM_IN_SET (priv->hw_addr_type, HW_ADDR_TYPE_PERMANENT, HW_ADDR_TYPE_UNSET); +} + +static gboolean +_hw_addr_set (NMDevice *self, + const char *addr, + const char *operation, + const char *detail) +{ + NMDevicePrivate *priv; gboolean success = FALSE; - const char *cur_addr = nm_device_get_hw_address (self); + NMPlatformError plerr; + const char *cur_addr; guint8 addr_bytes[NM_UTILS_HWADDR_LEN_MAX]; + guint hw_addr_len; + gboolean was_up; - /* Fall back to the permanent address */ - if (!addr) - addr = priv->perm_hw_addr; - if (!addr) - return FALSE; + nm_assert (NM_IS_DEVICE (self)); + nm_assert (addr); + nm_assert (operation); + + priv = NM_DEVICE_GET_PRIVATE (self); + + cur_addr = nm_device_get_hw_address (self); /* Do nothing if current MAC is same */ if (cur_addr && nm_utils_hwaddr_matches (cur_addr, -1, addr, -1)) { - _LOGD (LOGD_DEVICE | hw_log_domain, "no MAC address change needed"); + _LOGT (LOGD_DEVICE, "set-hw-addr: no MAC address change needed (%s)", cur_addr); return TRUE; } - 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 (self, FALSE); + hw_addr_len = priv->hw_addr_len; + if (!hw_addr_len) + hw_addr_len = _nm_utils_hwaddr_length (addr); + if ( !hw_addr_len + || !nm_utils_hwaddr_aton (addr, addr_bytes, hw_addr_len)) + g_return_val_if_reached (FALSE); - success = nm_platform_link_set_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), addr_bytes, priv->hw_addr_len); + _LOGT (LOGD_DEVICE, "set-hw-addr: setting MAC address to '%s' (%s, %s)...", addr, operation, detail); + + was_up = nm_device_is_up (self); + if (was_up) { + /* Can't change MAC address while device is up */ + nm_device_take_down (self, FALSE); + } + + plerr = nm_platform_link_set_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), addr_bytes, hw_addr_len); + success = (plerr == NM_PLATFORM_ERROR_SUCCESS); if (success) { /* MAC address succesfully changed; update the current MAC to match */ 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); + _LOGI (LOGD_DEVICE, "set-hw-addr: %s MAC address to %s (%s)", + operation, addr, detail); } else { - _LOGW (LOGD_DEVICE | hw_log_domain, - "new MAC address %s not successfully set", addr); + _LOGW (LOGD_DEVICE, + "set-hw-addr: new MAC address %s not successfully set to %s (%s)", + addr, operation, detail); success = FALSE; } } else { - _LOGW (LOGD_DEVICE | hw_log_domain, "failed to %s MAC address to %s", - detail, addr); + _NMLOG (plerr == NM_PLATFORM_ERROR_NOT_FOUND ? LOGL_DEBUG : LOGL_WARN, + LOGD_DEVICE, "set-hw-addr: failed to %s MAC address to %s (%s) (%s)", + operation, addr, detail, + nm_platform_error_to_string (plerr)); + } + + if (was_up) { + if (!nm_device_bring_up (self, TRUE, NULL)) + return FALSE; } - nm_device_bring_up (self, TRUE, NULL); return success; } +gboolean +nm_device_hw_addr_set (NMDevice *self, const char *addr, const char *detail) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (!addr) + g_return_val_if_reached (FALSE); + + /* this is called by NMDeviceVlan to take the MAC address from the parent + * and by NMDeviceWifi to set a random MAC address during scanning. + * In this case, it's like setting it to PERMANENT. */ + priv->hw_addr_type = HW_ADDR_TYPE_PERMANENT; + + return _hw_addr_set (self, addr, "set", detail); +} + +gboolean +nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean is_wifi) +{ + NMDevicePrivate *priv; + gs_free char *hw_addr_tmp = NULL; + gs_free char *hw_addr_generated = NULL; + gs_free char *generate_mac_address_mask_tmp = NULL; + const char *addr, *addr_setting; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (!connection) + g_return_val_if_reached (FALSE); + + addr = addr_setting = _get_cloned_mac_address_setting (self, connection, is_wifi, &hw_addr_tmp); + + if (nm_streq (addr, NM_CLONED_MAC_PRESERVE)) { + /* "preserve" means to reset the initial MAC address. */ + return nm_device_hw_addr_reset (self, addr_setting); + } + + if (nm_streq (addr, NM_CLONED_MAC_PERMANENT)) { + addr = nm_device_get_permanent_hw_address (self, TRUE); + if (!addr) + return FALSE; + priv->hw_addr_type = HW_ADDR_TYPE_PERMANENT; + } else if (NM_IN_STRSET (addr, NM_CLONED_MAC_RANDOM)) { + if (priv->hw_addr_type == HW_ADDR_TYPE_GENERATED) { + /* hm, we already use a generate MAC address. Most certainly, that is from the same + * activation request, so we should not create a new random address, instead keep + * the current. */ + return TRUE; + } + hw_addr_generated = nm_utils_hw_addr_gen_random_eth (nm_device_get_initial_hw_address (self), + _get_generate_mac_address_mask_setting (self, connection, is_wifi, &generate_mac_address_mask_tmp)); + if (!hw_addr_generated) { + _LOGW (LOGD_DEVICE, "set-hw-addr: failed to generate %s MAC address", "random"); + return FALSE; + } + priv->hw_addr_type = HW_ADDR_TYPE_GENERATED; + addr = hw_addr_generated; + } else if (NM_IN_STRSET (addr, NM_CLONED_MAC_STABLE)) { + NMUtilsStableType stable_type; + const char *stable_id; + + if (priv->hw_addr_type == HW_ADDR_TYPE_GENERATED) { + /* hm, we already use a generate MAC address. Most certainly, that is from the same + * activation request, so let's skip creating the stable address anew. */ + return TRUE; + } + + stable_id = _get_stable_id (connection, &stable_type); + if (stable_id) { + hw_addr_generated = nm_utils_hw_addr_gen_stable_eth (stable_type, stable_id, + nm_device_get_ip_iface (self), + nm_device_get_initial_hw_address (self), + _get_generate_mac_address_mask_setting (self, connection, is_wifi, &generate_mac_address_mask_tmp)); + } + if (!hw_addr_generated) { + _LOGW (LOGD_DEVICE, "set-hw-addr: failed to generate %s MAC address", "stable"); + return FALSE; + } + priv->hw_addr_type = HW_ADDR_TYPE_GENERATED; + addr = hw_addr_generated; + } else { + /* this must be a valid address. Otherwise, we shouldn't come here. */ + if (_nm_utils_hwaddr_length (addr) <= 0) { + g_return_val_if_reached (FALSE); + } + priv->hw_addr_type = HW_ADDR_TYPE_EXPLICIT; + } + + return _hw_addr_set (self, addr, "set-cloned", addr_setting); +} + +gboolean +nm_device_hw_addr_reset (NMDevice *self, const char *detail) +{ + NMDevicePrivate *priv; + const char *addr; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->hw_addr_type == HW_ADDR_TYPE_UNSET) + return TRUE; + + priv->hw_addr_type = HW_ADDR_TYPE_UNSET; + addr = nm_device_get_initial_hw_address (self); + if (!addr) { + /* as hw_addr_type is not UNSET, we expect that we can get an + * initial address to which to reset. */ + g_return_val_if_reached (FALSE); + } + + return _hw_addr_set (self, addr, "reset", detail); +} + const char * -nm_device_get_permanent_hw_address (NMDevice *self) +nm_device_get_permanent_hw_address (NMDevice *self, gboolean fallback_fake) { + NMDevicePrivate *priv; + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - return NM_DEVICE_GET_PRIVATE (self)->perm_hw_addr; + priv = NM_DEVICE_GET_PRIVATE (self); + if (!priv->hw_addr_perm) + return NULL; + if ( priv->hw_addr_perm_fake + && !fallback_fake) + return NULL; + return priv->hw_addr_perm; } const char * @@ -11158,7 +11981,7 @@ nm_device_get_initial_hw_address (NMDevice *self) { g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - return NM_DEVICE_GET_PRIVATE (self)->initial_hw_addr; + return NM_DEVICE_GET_PRIVATE (self)->hw_addr_initial; } /** @@ -11196,9 +12019,9 @@ nm_device_spec_match_list (NMDevice *self, const GSList *specs) static NMMatchSpecMatchType spec_match_list (NMDevice *self, const GSList *specs) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMMatchSpecMatchType matched = NM_MATCH_SPEC_NO_MATCH, m; const GSList *iter; + const char *hw_addr_perm; for (iter = specs; iter; iter = g_slist_next (iter)) { if (!strcmp ((const char *) iter->data, "*")) { @@ -11206,8 +12029,10 @@ spec_match_list (NMDevice *self, const GSList *specs) break; } } - if (priv->hw_addr_len && priv->hw_addr) { - m = nm_match_spec_hwaddr (specs, priv->hw_addr); + + hw_addr_perm = nm_device_get_permanent_hw_address (self, FALSE); + if (hw_addr_perm) { + m = nm_match_spec_hwaddr (specs, hw_addr_perm); matched = MAX (matched, m); } if (matched != NM_MATCH_SPEC_NEG_MATCH) { @@ -11290,7 +12115,8 @@ constructor (GType type, self = NM_DEVICE (object); priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->iface) { + if ( priv->iface + && G_LIKELY (!nm_utils_get_testing ())) { pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, priv->iface); if (pllink && link_type_compatible (self, pllink->type, NULL, NULL)) { @@ -11299,6 +12125,17 @@ constructor (GType type, } } + if (priv->hw_addr_perm) { + priv->hw_addr_len = _nm_utils_hwaddr_length (priv->hw_addr_perm); + if (!priv->hw_addr_len) { + g_clear_pointer (&priv->hw_addr_perm, g_free); + g_return_val_if_reached (object); + } + + priv->hw_addr = g_strdup (priv->hw_addr_perm); + _LOGT (LOGD_DEVICE, "hw-addr: has permanent hw-address '%s'", priv->hw_addr_perm); + } + return object; } @@ -11309,33 +12146,31 @@ constructed (GObject *object) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMPlatform *platform; - platform = nm_platform_get (); - if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities) priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self); /* Watch for external IP config changes */ + platform = NM_PLATFORM_GET; g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ipx_changed), self); g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ipx_changed), self); g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self); g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self); g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self); - 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_or_updated), - self); + priv->settings = g_object_ref (NM_SETTINGS_GET); + g_assert (priv->settings); - g_signal_connect (priv->con_provider, - NM_CP_SIGNAL_CONNECTION_REMOVED, - G_CALLBACK (cp_connection_removed), + g_signal_connect (priv->settings, + NM_SETTINGS_SIGNAL_CONNECTION_ADDED, + G_CALLBACK (cp_connection_added), self); - - g_signal_connect (priv->con_provider, - NM_CP_SIGNAL_CONNECTION_UPDATED, - G_CALLBACK (cp_connection_added_or_updated), + g_signal_connect (priv->settings, + NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, + G_CALLBACK (cp_connection_updated), + self); + g_signal_connect (priv->settings, + NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, + G_CALLBACK (cp_connection_removed), self); G_OBJECT_CLASS (nm_device_parent_class)->constructed (object); @@ -11352,7 +12187,7 @@ dispose (GObject *object) _LOGD (LOGD_DEVICE, "disposing"); - platform = nm_platform_get (); + platform = NM_PLATFORM_GET; g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ipx_changed), self); g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self); @@ -11361,7 +12196,7 @@ dispose (GObject *object) arp_cleanup (self); - g_signal_handlers_disconnect_by_func (nm_config_get (), config_changed_update_ignore_carrier, self); + nm_clear_g_signal_handler (nm_config_get (), &priv->ignore_carrier_id); dispatcher_cleanup (self); @@ -11382,12 +12217,14 @@ dispose (GObject *object) nm_clear_g_source (&priv->check_delete_unrealized_id); + nm_clear_g_source (&priv->stats.timeout_id); + link_disconnect_action_cancel (self); - if (priv->con_provider) { - g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_added_or_updated, self); - g_signal_handlers_disconnect_by_func (priv->con_provider, cp_connection_removed, self); - priv->con_provider = NULL; + if (priv->settings) { + g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_added, self); + g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_updated, self); + g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_removed, self); } available_connections_del_all (self); @@ -11426,8 +12263,8 @@ finalize (GObject *object) _LOGD (LOGD_DEVICE, "finalize(): %s", G_OBJECT_TYPE_NAME (self)); g_free (priv->hw_addr); - g_free (priv->perm_hw_addr); - g_free (priv->initial_hw_addr); + g_free (priv->hw_addr_perm); + g_free (priv->hw_addr_initial); g_slist_free_full (priv->pending_actions, g_free); g_slist_free_full (priv->dad6_failed_addrs, g_free); g_clear_pointer (&priv->physical_port_id, g_free); @@ -11445,16 +12282,19 @@ finalize (GObject *object) g_hash_table_unref (priv->available_connections); G_OBJECT_CLASS (nm_device_parent_class)->finalize (object); + + /* for testing, NMDeviceTest does not invoke NMDevice::constructed, + * and thus @settings might be unset. */ + if (priv->settings) + g_object_unref (priv->settings); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - NMDevice *self = NM_DEVICE (object); + NMDevice *self = (NMDevice *) object; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *hw_addr, *p; - guint count; switch (prop_id) { case PROP_UDI: @@ -11476,11 +12316,11 @@ set_property (GObject *object, guint prop_id, break; case PROP_DRIVER_VERSION: g_free (priv->driver_version); - priv->driver_version = g_strdup (g_value_get_string (value)); + priv->driver_version = g_value_dup_string (value); break; case PROP_FIRMWARE_VERSION: g_free (priv->firmware_version); - priv->firmware_version = g_strdup (g_value_get_string (value)); + priv->firmware_version = g_value_dup_string (value); break; case PROP_MTU: priv->mtu = g_value_get_uint (value); @@ -11532,32 +12372,12 @@ set_property (GObject *object, guint prop_id, case PROP_IS_MASTER: priv->is_master = g_value_get_boolean (value); break; - case PROP_HW_ADDRESS: + case PROP_PERM_HW_ADDRESS: /* 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; - } - - 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; - } + priv->hw_addr_perm = g_value_dup_string (value); + break; + case PROP_REFRESH_RATE_MS: + _stats_set_refresh_rate (self, g_value_get_uint (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -11617,13 +12437,13 @@ get_property (GObject *object, guint prop_id, nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip4_config : NULL); break; case PROP_DHCP4_CONFIG: - nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp4_config : NULL); + nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp4.config : NULL); break; case PROP_IP6_CONFIG: nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->ip6_config : NULL); break; case PROP_DHCP6_CONFIG: - nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp6_config : NULL); + nm_utils_g_value_set_object_path (value, ip_config_valid (priv->state) ? priv->dhcp6.config : NULL); break; case PROP_STATE: g_value_set_uint (value, priv->state); @@ -11680,6 +12500,10 @@ get_property (GObject *object, guint prop_id, case PROP_HW_ADDRESS: g_value_set_string (value, priv->hw_addr); break; + case PROP_PERM_HW_ADDRESS: + /* this property is exposed on D-Bus for NMDeviceEthernet and NMDeviceWifi. */ + g_value_set_string (value, nm_device_get_permanent_hw_address (self, FALSE)); + break; case PROP_HAS_PENDING_ACTION: g_value_set_boolean (value, nm_device_has_pending_action (self)); break; @@ -11717,6 +12541,15 @@ get_property (GObject *object, guint prop_id, g_value_take_boxed (value, slave_list); break; } + case PROP_REFRESH_RATE_MS: + g_value_set_uint (value, priv->stats.refresh_rate_ms); + break; + case PROP_TX_BYTES: + g_value_set_uint64 (value, priv->stats.tx_bytes); + break; + case PROP_RX_BYTES: + g_value_set_uint64 (value, priv->stats.rx_bytes); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -11765,6 +12598,8 @@ nm_device_class_init (NMDeviceClass *klass) klass->take_down = take_down; klass->carrier_changed = carrier_changed; klass->get_ip_iface_identifier = get_ip_iface_identifier; + klass->unmanaged_on_quit = unmanaged_on_quit; + klass->deactivate_reset_hw_addr = deactivate_reset_hw_addr; /* Properties */ obj_properties[PROP_UDI] = @@ -11923,6 +12758,11 @@ nm_device_class_init (NMDeviceClass *klass) obj_properties[PROP_HW_ADDRESS] = g_param_spec_string (NM_DEVICE_HW_ADDRESS, "", "", NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_PERM_HW_ADDRESS] = + g_param_spec_string (NM_DEVICE_PERM_HW_ADDRESS, "", "", + NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_properties[PROP_HAS_PENDING_ACTION] = @@ -11960,6 +12800,23 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + /* Statistics */ + obj_properties[PROP_REFRESH_RATE_MS] = + g_param_spec_uint (NM_DEVICE_STATISTICS_REFRESH_RATE_MS, "", "", + 0, UINT32_MAX, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_TX_BYTES] = + g_param_spec_uint64 (NM_DEVICE_STATISTICS_TX_BYTES, "", "", + 0, UINT64_MAX, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_RX_BYTES] = + g_param_spec_uint64 (NM_DEVICE_STATISTICS_RX_BYTES, "", "", + 0, UINT64_MAX, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); /* Signals */ @@ -12030,4 +12887,8 @@ nm_device_class_init (NMDeviceClass *klass) "Disconnect", impl_device_disconnect, "Delete", impl_device_delete, NULL); + + nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), + NMDBUS_TYPE_DEVICE_STATISTICS_SKELETON, + NULL); } |