diff options
| author | Michael Biebl <biebl@debian.org> | 2017-11-07 00:14:39 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2017-11-07 00:14:39 +0100 |
| commit | 90e8691111889a7b5f3c812f5a41f15a8a058913 (patch) | |
| tree | f101a879eca27c34a9bfa5f3da52266b22539a36 /src/devices/nm-device.c | |
| parent | bdb6eeb0670658255c2a4c3c501c0a27fa8cfe55 (diff) | |
New upstream version 1.9.90 upstream/1.9.90
Diffstat (limited to 'src/devices/nm-device.c')
| -rw-r--r-- | src/devices/nm-device.c | 3107 |
1 files changed, 1722 insertions, 1385 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index bacbfb33..e979b875 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -34,12 +34,16 @@ #include <arpa/inet.h> #include <fcntl.h> #include <linux/if_addr.h> +#include <linux/rtnetlink.h> + +#include "nm-utils/nm-dedup-multi.h" #include "nm-common-macros.h" #include "nm-device-private.h" #include "NetworkManagerUtils.h" #include "nm-manager.h" #include "platform/nm-platform.h" +#include "platform/nmp-object.h" #include "ndisc/nm-ndisc.h" #include "ndisc/nm-lndp-ndisc.h" #include "dhcp/nm-dhcp-manager.h" @@ -59,16 +63,16 @@ #include "nm-netns.h" #include "nm-dispatcher.h" #include "nm-config.h" +#include "nm-utils/c-list.h" #include "dns/nm-dns-manager.h" #include "nm-core-internal.h" -#include "nm-default-route-manager.h" -#include "nm-route-manager.h" #include "systemd/nm-sd.h" #include "nm-lldp-listener.h" #include "nm-audit-manager.h" #include "nm-arping-manager.h" #include "nm-connectivity.h" #include "nm-dbus-interface.h" +#include "nm-device-vlan.h" #include "nm-device-logging.h" _LOG_DECLARE_SELF (NMDevice); @@ -82,6 +86,13 @@ _LOG_DECLARE_SELF (NMDevice); #define DHCP_NUM_TRIES_MAX 3 #define DEFAULT_AUTOCONNECT TRUE +#define CARRIER_WAIT_TIME_MS 5000 +#define CARRIER_WAIT_TIME_AFTER_MTU_MS 10000 + +#define NM_DEVICE_AUTH_RETRIES_UNSET -1 +#define NM_DEVICE_AUTH_RETRIES_INFINITY -2 +#define NM_DEVICE_AUTH_RETRIES_DEFAULT 3 + /*****************************************************************************/ typedef void (*ActivationHandleFunc) (NMDevice *self); @@ -106,6 +117,7 @@ typedef enum { } IpState; typedef struct { + CList lst_slave; NMDevice *slave; gulong watch_id; bool slave_is_enslaved; @@ -186,7 +198,6 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice, PROP_IFINDEX, PROP_AVAILABLE_CONNECTIONS, PROP_PHYSICAL_PORT_ID, - PROP_IS_MASTER, PROP_MASTER, PROP_PARENT, PROP_HW_ADDRESS, @@ -231,6 +242,8 @@ typedef struct _NMDevicePrivate { int parent_ifindex; + int auth_retries; + union { const guint8 hw_addr_len; /* read-only */ guint8 hw_addr_len_; @@ -312,6 +325,17 @@ typedef struct _NMDevicePrivate { guint32 mtu_initial; guint32 ip6_mtu_initial; + guint32 v4_route_table; + guint32 v6_route_table; + + /* when carrier goes away, we give a grace period of CARRIER_WAIT_TIME_MS + * until taking action. + * + * When changing MTU, the device might take longer then that. So, whenever + * NM changes the MTU it sets @carrier_wait_until_ms to CARRIER_WAIT_TIME_AFTER_MTU_MS + * in the future. This is used to extend the grace period in this particular case. */ + gint64 carrier_wait_until_ms; + bool carrier:1; bool ignore_carrier:1; @@ -322,10 +346,15 @@ typedef struct _NMDevicePrivate { bool v4_commit_first_time:1; bool v6_commit_first_time:1; + bool default_route_metric_penalty_ip4_has:1; + bool default_route_metric_penalty_ip6_has:1; + NMDeviceSysIfaceState sys_iface_state:2; + bool v4_route_table_initalized:1; + bool v6_route_table_initalized:1; + /* Generic DHCP stuff */ - guint32 dhcp_timeout; char * dhcp_anycast_address; char * current_stable_id; @@ -346,14 +375,7 @@ typedef struct _NMDevicePrivate { NMIP4Config * ext_ip4_config; /* Stuff added outside NM */ NMIP4Config * wwan_ip4_config; /* WWAN configuration */ GSList * vpn4_configs; /* VPNs which use this device */ - struct { - bool v4_has; - bool v4_is_assumed; - bool v6_has; - bool v6_is_assumed; - NMPlatformIP4Route v4; - NMPlatformIP6Route v6; - } default_route; + bool v4_has_shadowed_routes; const char *ip4_rp_filter; @@ -390,6 +412,7 @@ typedef struct _NMDevicePrivate { /* IPv4LL stuff */ sd_ipv4ll * ipv4ll; guint ipv4ll_timeout; + guint rt6_temporary_not_available_id; /* IPv4 DAD stuff */ struct { @@ -411,6 +434,8 @@ typedef struct _NMDevicePrivate { bool nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */ NMIP6Config * dad6_ip6_config; + GHashTable * rt6_temporary_not_available; + NMNDisc * ndisc; gulong ndisc_changed_id; gulong ndisc_timeout_id; @@ -452,8 +477,7 @@ typedef struct _NMDevicePrivate { gulong master_ready_id; /* slave management */ - bool is_master; - GSList * slaves; /* list of SlaveInfo */ + CList slaves; /* list of SlaveInfo */ NMMetered metered; @@ -485,19 +509,18 @@ G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, NM_TYPE_EXPORTED_OBJECT) static void nm_device_set_proxy_config (NMDevice *self, const char *pac_url); +static gboolean update_ext_ip_config (NMDevice *self, int addr_family, gboolean initial, gboolean intersect_configs); + static gboolean nm_device_set_ip4_config (NMDevice *self, NMIP4Config *config, - guint32 default_route_metric, gboolean commit, - gboolean routes_full_sync); + GPtrArray *ip4_dev_route_blacklist); static gboolean ip4_config_merge_and_apply (NMDevice *self, - NMIP4Config *config, gboolean commit); static gboolean nm_device_set_ip6_config (NMDevice *self, NMIP6Config *config, - gboolean commit, - gboolean routes_full_sync); + gboolean commit); static gboolean ip6_config_merge_and_apply (NMDevice *self, gboolean commit); @@ -514,7 +537,7 @@ static void nm_device_set_autoconnect_both (NMDevice *self, gboolean autoconnect static void nm_device_set_autoconnect_full (NMDevice *self, int autoconnect_intern, int autoconnect_user); static const char *_activation_func_to_string (ActivationHandleFunc func); -static void activation_source_handle_cb (NMDevice *self, int family); +static void activation_source_handle_cb (NMDevice *self, int addr_family); static void _set_state_full (NMDevice *self, NMDeviceState state, @@ -525,7 +548,7 @@ static gboolean queued_ip4_config_change (gpointer user_data); static gboolean queued_ip6_config_change (gpointer user_data); static void ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data); static gboolean ip_config_valid (NMDeviceState state); -static NMActStageReturn dhcp4_start (NMDevice *self, NMConnection *connection); +static NMActStageReturn dhcp4_start (NMDevice *self); static gboolean dhcp6_start (NMDevice *self, gboolean wait_for_ll); static void nm_device_start_ip_check (NMDevice *self); static void realize_start_setup (NMDevice *self, @@ -534,8 +557,9 @@ static void realize_start_setup (NMDevice *self, const char *assume_state_connection_uuid, gboolean set_nm_owned, NMUnmanFlagOp unmanaged_user_explicit); +static void _set_mtu (NMDevice *self, guint32 mtu); static void _commit_mtu (NMDevice *self, const NMIP4Config *config); -static void dhcp_schedule_restart (NMDevice *self, int family, const char *reason); +static void dhcp_schedule_restart (NMDevice *self, int addr_family, const char *reason); static void _cancel_activation (NMDevice *self); /*****************************************************************************/ @@ -628,6 +652,7 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_reason_to_string, NMDeviceStateReason, NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_NEW_ACTIVATION, "new-activation"), NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_PARENT_CHANGED, "parent-changed"), NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED, "parent-managed-changed"), + NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_OVSDB_FAILED, "ovsdb-failed"), ); #define reason_to_string(reason) \ @@ -647,12 +672,32 @@ nm_device_get_netns (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->netns; } +NMDedupMultiIndex * +nm_device_get_multi_index (NMDevice *self) +{ + return nm_netns_get_multi_idx (nm_device_get_netns (self)); +} + NMPlatform * nm_device_get_platform (NMDevice *self) { return nm_netns_get_platform (nm_device_get_netns (self)); } +static NMIP4Config * +_ip4_config_new (NMDevice *self) +{ + return nm_ip4_config_new (nm_device_get_multi_index (self), + nm_device_get_ip_ifindex (self)); +} + +static NMIP6Config * +_ip6_config_new (NMDevice *self) +{ + return nm_ip6_config_new (nm_device_get_multi_index (self), + nm_device_get_ip_ifindex (self)); +} + /*****************************************************************************/ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_sys_iface_state_to_str, NMDeviceSysIfaceState, @@ -714,6 +759,25 @@ nm_device_sys_iface_state_set (NMDevice *self, nm_assert (priv->sys_iface_state == sys_iface_state); } +static void +_active_connection_set_state_flags_full (NMDevice *self, + NMActivationStateFlags flags, + NMActivationStateFlags mask) +{ + NMActiveConnection *ac; + + ac = NM_ACTIVE_CONNECTION (nm_device_get_act_request (self)); + if (ac) + nm_active_connection_set_state_flags_full (ac, flags, mask); +} + +static void +_active_connection_set_state_flags (NMDevice *self, + NMActivationStateFlags flags) +{ + _active_connection_set_state_flags_full (self, flags, flags); +} + /*****************************************************************************/ void @@ -796,26 +860,35 @@ nm_device_ipv4_sysctl_set (NMDevice *self, const char *property, const char *val NMPlatform *platform = nm_device_get_platform (self); gs_free char *value_to_free = NULL; const char *value_to_set; + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + + if (!nm_device_get_ip_ifindex (self)) + return FALSE; if (value) { value_to_set = value; } else { /* Set to a default value when we've got a NULL @value. */ value_to_free = nm_platform_sysctl_get (platform, - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip4_property_path ("default", property))); + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, buf, "default", property))); value_to_set = value_to_free; } return nm_platform_sysctl_set (platform, - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip4_property_path (nm_device_get_ip_iface (self), property)), + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, buf, nm_device_get_ip_iface (self), property)), value_to_set); } static guint32 nm_device_ipv4_sysctl_get_uint32 (NMDevice *self, const char *property, guint32 fallback) { + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + + if (!nm_device_get_ip_ifindex (self)) + return fallback; + return nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip4_property_path (nm_device_get_ip_iface (self), property)), + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, buf, nm_device_get_ip_iface (self), property)), 10, 0, G_MAXUINT32, @@ -825,14 +898,24 @@ nm_device_ipv4_sysctl_get_uint32 (NMDevice *self, const char *property, guint32 gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value) { - return nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property)), value); + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + + if (!nm_device_get_ip_ifindex (self)) + return FALSE; + + return nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, nm_device_get_ip_iface (self), property)), value); } static guint32 nm_device_ipv6_sysctl_get_uint32 (NMDevice *self, const char *property, guint32 fallback) { + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + + if (!nm_device_get_ip_ifindex (self)) + return fallback; + return nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property)), + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, nm_device_get_ip_iface (self), property)), 10, 0, G_MAXUINT32, @@ -949,14 +1032,29 @@ _set_ip_state (NMDevice *self, int addr_family, IpState new_state) IpState *p; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + nm_assert_addr_family (addr_family); - p = addr_family == AF_INET ? &priv->ip4_state_ : &priv->ip6_state_; + p = (addr_family == AF_INET) + ? &priv->ip4_state_ + : &priv->ip6_state_; if (*p != new_state) { - _LOGT (LOGD_DEVICE, "ip%c-state: set to %d (%s)", addr_family == AF_INET ? '4' : '6', - (int) new_state, _ip_state_to_string (new_state)); + _LOGT (LOGD_DEVICE, "ip%c-state: set to %d (%s)", + nm_utils_addr_family_to_char (addr_family), + (int) new_state, + _ip_state_to_string (new_state)); *p = new_state; + + if (new_state == IP_DONE) { + /* we only set the IPx_READY flag once we reach IP_DONE state. We don't + * ever clear it, even if we later enter IP_FAIL state. + * + * This is not documented/guaranteed behavior, but seems to make sense for now. */ + _active_connection_set_state_flags (self, + addr_family == AF_INET + ? NM_ACTIVATION_STATE_FLAG_IP4_READY + : NM_ACTIVATION_STATE_FLAG_IP6_READY); + } } } @@ -978,6 +1076,48 @@ nm_device_get_iface (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->iface; } +gboolean +nm_device_take_over_link (NMDevice *self, const char *ifname, gboolean *renamed) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const NMPlatformLink *plink; + NMPlatform *platform; + gboolean up, success = TRUE; + int ifindex; + + g_return_val_if_fail (priv->ifindex <= 0, FALSE); + g_return_val_if_fail (ifname, FALSE); + + NM_SET_OUT (renamed, FALSE); + + platform = nm_device_get_platform (self); + plink = nm_platform_link_get_by_ifname (platform, ifname); + if (!plink) + return FALSE; + + ifindex = plink->ifindex; + + if (!nm_streq (ifname, nm_device_get_iface (self))) { + up = NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP); + + /* Rename the link to the device ifname */ + if (up) + nm_platform_link_set_down (platform, ifindex); + success = nm_platform_link_set_name (platform, ifindex, nm_device_get_iface (self)); + if (up) + nm_platform_link_set_up (platform, ifindex, NULL); + + NM_SET_OUT (renamed, success); + } + + if (success) { + priv->ifindex = ifindex; + _notify (self, PROP_IFINDEX); + } + + return success; +} + int nm_device_get_ifindex (NMDevice *self) { @@ -1091,7 +1231,8 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface) } if (priv->ip_ifindex > 0) { - if (nm_platform_check_support_user_ipv6ll (nm_device_get_platform (self))) + if (nm_platform_check_kernel_support (nm_device_get_platform (self), + NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) nm_platform_link_set_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ip_ifindex, TRUE); if (!nm_platform_link_is_up (nm_device_get_platform (self), priv->ip_ifindex)) @@ -1478,23 +1619,14 @@ nm_device_get_metered (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->metered; } -/** - * nm_device_get_priority(): - * @self: the #NMDevice - * - * Returns: the device's routing priority. Lower numbers means a "better" - * device, eg higher priority. - */ -int -nm_device_get_priority (NMDevice *self) +static guint32 +_get_route_metric_default (NMDevice *self) { - g_return_val_if_fail (NM_IS_DEVICE (self), 1000); - /* Device 'priority' is used for the default route-metric and is based on * the device type. The settings ipv4.route-metric and ipv6.route-metric * can overwrite this default. * - * Currently for both IPv4 and IPv6 we use the same default values. + * For both IPv4 and IPv6 we use the same default values. * * The route-metric is used for the metric of the routes of device. * This also applies to the default route. Therefore it affects also @@ -1532,6 +1664,8 @@ nm_device_get_priority (NMDevice *self) return 425; case NM_DEVICE_TYPE_TUN: return 450; + case NM_DEVICE_TYPE_PPP: + return 460; case NM_DEVICE_TYPE_VXLAN: return 500; case NM_DEVICE_TYPE_DUMMY: @@ -1546,6 +1680,10 @@ nm_device_get_priority (NMDevice *self) return 700; case NM_DEVICE_TYPE_BT: return 750; + case NM_DEVICE_TYPE_OVS_BRIDGE: + case NM_DEVICE_TYPE_OVS_INTERFACE: + case NM_DEVICE_TYPE_OVS_PORT: + return 800; case NM_DEVICE_TYPE_GENERIC: return 950; case NM_DEVICE_TYPE_UNKNOWN: @@ -1558,29 +1696,40 @@ nm_device_get_priority (NMDevice *self) return 11000; } -static guint32 -route_metric_with_penalty (NMDevice *self, guint32 metric) +static gboolean +default_route_metric_penalty_detect (NMDevice *self) { #if WITH_CONCHECK NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const guint32 PENALTY = 20000; - - /* Beware: for IPv6, a metric of 0 effectively means 1024. - * Only pass a normalized IPv6 metric (nm_utils_ip6_route_metric_normalize). */ + /* currently we don't differentiate between IPv4 and IPv6 when detecting + * connectivity. */ if ( priv->connectivity_state != NM_CONNECTIVITY_FULL - && nm_connectivity_check_enabled (nm_connectivity_get ())) { - if (metric >= G_MAXUINT32 - PENALTY) - return G_MAXUINT32; - return metric + PENALTY; + && nm_connectivity_check_enabled (nm_connectivity_get ())) { + return TRUE; } #endif - return metric; + + return FALSE; } static guint32 -_get_ipx_route_metric (NMDevice *self, - gboolean is_v4) +default_route_metric_penalty_get (NMDevice *self, int addr_family) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + nm_assert_addr_family (addr_family); + + if ( addr_family == AF_INET + ? priv->default_route_metric_penalty_ip4_has + : priv->default_route_metric_penalty_ip6_has) + return 20000; + return 0; +} + +guint32 +nm_device_get_route_metric (NMDevice *self, + int addr_family) { char *value; gint64 route_metric; @@ -1588,10 +1737,11 @@ _get_ipx_route_metric (NMDevice *self, NMConnection *connection; g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32); + g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), G_MAXUINT32); connection = nm_device_get_applied_connection (self); if (connection) { - s_ip = is_v4 + s_ip = addr_family == AF_INET ? nm_connection_get_setting_ip4_config (connection) : nm_connection_get_setting_ip6_config (connection); @@ -1610,7 +1760,7 @@ _get_ipx_route_metric (NMDevice *self, * Note that that means that the route-metric might change between SIGHUP. * You must cache the returned value if that is a problem. */ value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - is_v4 ? "ipv4.route-metric" : "ipv6.route-metric", self); + addr_family == AF_INET ? "ipv4.route-metric" : "ipv6.route-metric", self); if (value) { route_metric = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1); g_free (value); @@ -1618,81 +1768,96 @@ _get_ipx_route_metric (NMDevice *self, if (route_metric >= 0) goto out; } - route_metric = nm_device_get_priority (self); + route_metric = _get_route_metric_default (self); out: - if (!is_v4) - route_metric = nm_utils_ip6_route_metric_normalize (route_metric); - return route_metric; + return nm_utils_ip_route_metric_normalize (addr_family, route_metric); } guint32 -nm_device_get_ip4_route_metric (NMDevice *self) +nm_device_get_route_table (NMDevice *self, + int addr_family, + gboolean fallback_main) { - return _get_ipx_route_metric (self, TRUE); -} + NMDevicePrivate *priv; + NMConnection *connection; + NMSettingIPConfig *s_ip; + guint32 route_table = 0; -guint32 -nm_device_get_ip6_route_metric (NMDevice *self) -{ - return _get_ipx_route_metric (self, FALSE); -} + nm_assert_addr_family (addr_family); -static void -_update_default_route (NMDevice *self, int addr_family, gboolean has, gboolean is_assumed) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - bool *p_has, *p_is_assumed; + g_return_val_if_fail (NM_IS_DEVICE (self), RT_TABLE_MAIN); - nm_assert (NM_IN_SET (addr_family, 0, AF_INET, AF_INET6)); + priv = NM_DEVICE_GET_PRIVATE (self); + /* the route table setting affects how we sync routes. We shall + * not change it while the device is active, hence, cache it. */ if (addr_family == AF_INET) { - p_has = &priv->default_route.v4_has; - p_is_assumed = &priv->default_route.v4_is_assumed; + if (priv->v4_route_table_initalized) + return priv->v4_route_table ?: (fallback_main ? RT_TABLE_MAIN : 0); } else { - p_has = &priv->default_route.v6_has; - p_is_assumed = &priv->default_route.v6_is_assumed; + if (priv->v6_route_table_initalized) + return priv->v6_route_table ?: (fallback_main ? RT_TABLE_MAIN : 0); } - if (*p_has == has && *p_is_assumed == is_assumed) - return; + connection = nm_device_get_applied_connection (self); + if (connection) { + if (addr_family == AF_INET) + s_ip = nm_connection_get_setting_ip4_config (connection); + else + s_ip = nm_connection_get_setting_ip6_config (connection); - *p_has = has; - *p_is_assumed = is_assumed; + if (s_ip) + route_table = nm_setting_ip_config_get_route_table (s_ip); - if (addr_family == AF_INET) - nm_default_route_manager_ip4_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); - else - nm_default_route_manager_ip6_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); -} - -const NMPlatformIP4Route * -nm_device_get_ip4_default_route (NMDevice *self, gboolean *out_is_assumed) -{ - NMDevicePrivate *priv; + /* we only lookup the global default if we also have an applied + * connection. Otherwise, the connection is not active, and the + * connection default doesn't matter. */ + if (route_table == 0) { + gs_free char *value = NULL; - g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + addr_family == AF_INET + ? "ipv4.route-table" + : "ipv6.route-table", + self); + route_table = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, 0); + } + } - priv = NM_DEVICE_GET_PRIVATE (self); + if (addr_family == AF_INET) { + priv->v4_route_table_initalized = TRUE; + priv->v4_route_table = route_table; + } else { + priv->v6_route_table_initalized = TRUE; + priv->v6_route_table = route_table; + } - if (out_is_assumed) - *out_is_assumed = priv->default_route.v4_is_assumed; + _LOGT (LOGD_DEVICE, + "ipv%c.route-table = %u%s", + addr_family == AF_INET ? '4' : '6', + (guint) (route_table ?: RT_TABLE_MAIN), + route_table ? "" : " (policy routing not enabled)"); - return priv->default_route.v4_has ? &priv->default_route.v4 : NULL; + return route_table ?: (fallback_main ? RT_TABLE_MAIN : 0); } -const NMPlatformIP6Route * -nm_device_get_ip6_default_route (NMDevice *self, gboolean *out_is_assumed) +const NMPObject * +nm_device_get_best_default_route (NMDevice *self, + int addr_family) { - NMDevicePrivate *priv; - - g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - - priv = NM_DEVICE_GET_PRIVATE (self); - - if (out_is_assumed) - *out_is_assumed = priv->default_route.v6_is_assumed; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - return priv->default_route.v6_has ? &priv->default_route.v6 : NULL; + switch (addr_family) { + case AF_INET: + return priv->ip4_config ? nm_ip4_config_best_default_route_get (priv->ip4_config) : NULL; + case AF_INET6: + return priv->ip6_config ? nm_ip6_config_best_default_route_get (priv->ip6_config) : NULL; + case AF_UNSPEC: + return (priv->ip4_config ? nm_ip4_config_best_default_route_get (priv->ip4_config) : NULL) + ?: (priv->ip6_config ? nm_ip6_config_best_default_route_get (priv->ip6_config) : NULL); + default: + g_return_val_if_reached (NULL); + } } const char * @@ -1809,7 +1974,7 @@ update_connectivity_state (NMDevice *self, NMConnectivityState state) /* If the connectivity check is disabled, make an optimistic guess. */ if (state == NM_CONNECTIVITY_UNKNOWN) { if (priv->state == NM_DEVICE_STATE_ACTIVATED) { - if (priv->default_route.v4_has || priv->default_route.v6_has) + if (nm_device_get_best_default_route (self, AF_UNSPEC)) state = NM_CONNECTIVITY_FULL; else state = NM_CONNECTIVITY_LIMITED; @@ -1829,12 +1994,12 @@ update_connectivity_state (NMDevice *self, NMConnectivityState state) if ( priv->state == NM_DEVICE_STATE_ACTIVATED && !nm_device_sys_iface_state_is_external (self)) { - if ( priv->default_route.v4_has - && !ip4_config_merge_and_apply (self, NULL, TRUE)) - _LOGW (LOGD_IP4, "Failed to update IPv4 default route metric"); - if ( priv->default_route.v6_has + if ( nm_device_get_best_default_route (self, AF_INET) + && !ip4_config_merge_and_apply (self, TRUE)) + _LOGW (LOGD_IP4, "Failed to update IPv4 route metric"); + if ( nm_device_get_best_default_route (self, AF_INET6) && !ip6_config_merge_and_apply (self, TRUE)) - _LOGW (LOGD_IP6, "Failed to update IPv6 default route metric"); + _LOGW (LOGD_IP6, "Failed to update IPv6 route metric"); } } } @@ -1948,7 +2113,7 @@ concheck_periodic_update (NMDevice *self) gboolean check_enable; check_enable = (priv->state == NM_DEVICE_STATE_ACTIVATED) - && (priv->default_route.v4_has || priv->default_route.v6_has); + && nm_device_get_best_default_route (self, AF_UNSPEC); if (check_enable && !priv->concheck_periodic_id) { /* We just gained a default route. Enable periodic checking. */ @@ -1976,11 +2141,11 @@ static SlaveInfo * find_slave_info (NMDevice *self, NMDevice *slave) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + CList *iter; SlaveInfo *info; - GSList *iter; - for (iter = priv->slaves; iter; iter = g_slist_next (iter)) { - info = iter->data; + c_list_for_each (iter, &priv->slaves) { + info = c_list_entry (iter, SlaveInfo, lst_slave); if (info->slave == slave) return info; } @@ -2100,13 +2265,19 @@ nm_device_master_release_one_slave (NMDevice *self, NMDevice *slave, gboolean co * Transfers ownership from slave_priv->master. */ self_free = self; - priv->slaves = g_slist_remove (priv->slaves, info); + c_list_unlink_init (&info->lst_slave); slave_priv->master = NULL; g_signal_handler_disconnect (slave, info->watch_id); g_object_unref (slave); g_slice_free (SlaveInfo, info); + if (c_list_is_empty (&priv->slaves)) { + _active_connection_set_state_flags_full (self, + 0, + NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES); + } + /* Ensure the device's hardware address is up-to-date; it often changes * when slaves change. */ @@ -2140,7 +2311,8 @@ is_unmanaged_external_down (NMDevice *self, gboolean consider_can) /* Manage externally-created software interfaces only when they are IFF_UP */ if ( priv->ifindex <= 0 || !priv->up - || !(priv->slaves || nm_platform_link_can_assume (nm_device_get_platform (self), priv->ifindex))) + || !( !c_list_is_empty (&priv->slaves) + || nm_platform_link_can_assume (nm_device_get_platform (self), priv->ifindex))) return NM_UNMAN_FLAG_OP_SET_UNMANAGED; return NM_UNMAN_FLAG_OP_SET_MANAGED; @@ -2235,8 +2407,6 @@ carrier_changed (NMDevice *self, gboolean carrier) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NM_DEVICE_GET_CLASS (self)->carrier_changed_notify (self, carrier); - if (priv->state <= NM_DEVICE_STATE_UNMANAGED) return; @@ -2246,21 +2416,22 @@ carrier_changed (NMDevice *self, gboolean carrier) if (priv->ignore_carrier && !carrier) return; - if (priv->is_master) { - /* Bridge/bond/team carrier does not affect its own activation, - * but when carrier comes on, if there are slaves waiting, - * it will restart them. - */ - if (!carrier) + if (nm_device_is_master (self)) { + if (carrier) { + /* Force master to retry getting ip addresses when carrier + * is restored. */ + if (priv->state == NM_DEVICE_STATE_ACTIVATED) + nm_device_update_dynamic_ip_setup (self); + else { + if (nm_device_activate_ip4_state_in_wait (self)) + nm_device_activate_stage3_ip4_start (self); + if (nm_device_activate_ip6_state_in_wait (self)) + nm_device_activate_stage3_ip6_start (self); + } return; - - if (nm_device_activate_ip4_state_in_wait (self)) - nm_device_activate_stage3_ip4_start (self); - if (nm_device_activate_ip6_state_in_wait (self)) - nm_device_activate_stage3_ip6_start (self); - - return; - } else if (nm_device_get_enslaved (self) && !carrier) { + } + /* fall-through and change state of device */ + } else if (priv->is_enslaved && !carrier) { /* Slaves don't deactivate when they lose carrier; for * bonds/teams in particular that would be actively * counterproductive. @@ -2306,7 +2477,7 @@ carrier_disconnected_action_cb (gpointer user_data) NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - _LOGD (LOGD_DEVICE, "link disconnected (calling deferred action) (id=%u)", priv->carrier_defer_id); + _LOGD (LOGD_DEVICE, "carrier: link disconnected (calling deferred action) (id=%u)", priv->carrier_defer_id); priv->carrier_defer_id = 0; carrier_changed (self, FALSE); @@ -2320,7 +2491,7 @@ carrier_disconnected_action_cancel (NMDevice *self) guint id = priv->carrier_defer_id; if (nm_clear_g_source (&priv->carrier_defer_id)) { - _LOGD (LOGD_DEVICE, "link disconnected (canceling deferred action) (id=%u)", + _LOGD (LOGD_DEVICE, "carrier: link disconnected (canceling deferred action) (id=%u)", id); } } @@ -2338,8 +2509,9 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier) _notify (self, PROP_CARRIER); if (priv->carrier) { - _LOGI (LOGD_DEVICE, "link connected"); + _LOGI (LOGD_DEVICE, "carrier: link connected"); carrier_disconnected_action_cancel (self); + NM_DEVICE_GET_CLASS (self)->carrier_changed_notify (self, carrier); carrier_changed (self, TRUE); if (priv->carrier_wait_id) { @@ -2349,14 +2521,15 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier) } else { if (priv->carrier_wait_id) nm_device_add_pending_action (self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); + NM_DEVICE_GET_CLASS (self)->carrier_changed_notify (self, carrier); if ( state <= NM_DEVICE_STATE_DISCONNECTED && !priv->queued_act_request) { - _LOGD (LOGD_DEVICE, "link disconnected"); + _LOGD (LOGD_DEVICE, "carrier: link disconnected"); carrier_changed (self, FALSE); } else { priv->carrier_defer_id = g_timeout_add_seconds (LINK_DISCONNECT_DELAY, carrier_disconnected_action_cb, self); - _LOGD (LOGD_DEVICE, "link disconnected (deferring action for %d seconds) (id=%u)", + _LOGD (LOGD_DEVICE, "carrier: link disconnected (deferring action for %d seconds) (id=%u)", LINK_DISCONNECT_DELAY, priv->carrier_defer_id); } } @@ -2388,12 +2561,27 @@ static void device_recheck_slave_status (NMDevice *self, const NMPlatformLink *plink) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevice *master; + nm_auto_nmpobj const NMPObject *plink_master_keep_alive = NULL; + const NMPlatformLink *plink_master; g_return_if_fail (plink); if (plink->master <= 0) return; + master = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->master); + plink_master = nm_platform_link_get (nm_device_get_platform (self), plink->master); + plink_master_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (plink_master)); + + if ( master == NULL + && plink_master + && g_strcmp0 (plink_master->name, "ovs-system") == 0 + && plink_master->type == NM_LINK_TYPE_OPENVSWITCH) { + _LOGD (LOGD_DEVICE, "the device claimed by openvswitch"); + return; + } + if (priv->master) { if ( plink->master > 0 && plink->master == nm_device_get_ifindex (priv->master)) { @@ -2405,20 +2593,16 @@ device_recheck_slave_status (NMDevice *self, const NMPlatformLink *plink) nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); } - if (plink->master > 0) { - NMDevice *master; - master = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->master); - if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) - nm_device_master_add_slave (master, self, FALSE); - else if (master) { - _LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring", - nm_device_get_iface (master)); - } else { - _LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s", - plink->master, - nm_platform_link_get_name (nm_device_get_platform (self), plink->master)); - } + if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) + nm_device_master_add_slave (master, self, FALSE); + else if (master) { + _LOGI (LOGD_DEVICE, "enslaved to non-master-type device %s; ignoring", + nm_device_get_iface (master)); + } else { + _LOGW (LOGD_DEVICE, "enslaved to unknown device %d (%s%s%s)", + plink->master, + NM_PRINT_FMT_QUOTED (plink_master, "\"", plink_master->name, "\"", "??")); } } @@ -2429,16 +2613,19 @@ ndisc_set_router_config (NMNDisc *ndisc, NMDevice *self) gint32 now; GArray *addresses, *dns_servers, *dns_domains; guint len, i; + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter ipconf_iter; if (nm_ndisc_get_node_type (ndisc) != NM_NDISC_NODE_TYPE_ROUTER) return; now = nm_utils_get_monotonic_timestamp_s (); - len = nm_ip6_config_get_num_addresses (priv->ip6_config); - addresses = g_array_sized_new (FALSE, TRUE, sizeof (NMNDiscAddress), len); - for (i = 0; i < len; i++) { - const NMPlatformIP6Address *addr = nm_ip6_config_get_address (priv->ip6_config, i); + head_entry = nm_ip6_config_lookup_addresses (priv->ip6_config); + addresses = g_array_sized_new (FALSE, TRUE, sizeof (NMNDiscAddress), + head_entry ? head_entry->len : 0); + nm_dedup_multi_iter_for_each (&ipconf_iter, head_entry) { + const NMPlatformIP6Address *addr = NMP_OBJECT_CAST_IP6_ADDRESS (ipconf_iter.current->obj); NMNDiscAddress *ndisc_addr; if (IN6_IS_ADDR_LINKLOCAL (&addr->address)) @@ -2497,8 +2684,7 @@ device_link_changed (NMDevice *self) NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean ip_ifname_changed = FALSE; - const char *udi; - NMPlatformLink info; + nm_auto_nmpobj const NMPObject *pllink_keep_alive = NULL; const NMPlatformLink *pllink; int ifindex; gboolean was_up; @@ -2512,40 +2698,20 @@ device_link_changed (NMDevice *self) if (!pllink) return G_SOURCE_REMOVE; - info = *pllink; + pllink_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (pllink)); - udi = nm_platform_link_get_udi (nm_device_get_platform (self), info.ifindex); - if (udi && !nm_streq0 (udi, priv->udi)) { - /* Update UDI to what udev gives us */ - g_free (priv->udi); - priv->udi = g_strdup (udi); - _notify (self, PROP_UDI); - } - - if (!nm_streq0 (info.driver, priv->driver)) { - g_free (priv->driver); - priv->driver = g_strdup (info.driver); - _notify (self, PROP_DRIVER); - } - - if (priv->mtu != info.mtu) { - priv->mtu = info.mtu; - _notify (self, PROP_MTU); - } - - if (ifindex == nm_device_get_ip_ifindex (self)) - _stats_update_counters_from_pllink (self, &info); + nm_device_update_from_platform_link (self, pllink); 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, FALSE); - if (info.name[0] && strcmp (priv->iface, info.name) != 0) { + if (pllink->name[0] && strcmp (priv->iface, pllink->name) != 0) { _LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'", - priv->ifindex, priv->iface, info.name); + priv->ifindex, priv->iface, pllink->name); g_free (priv->iface); - priv->iface = g_strdup (info.name); + priv->iface = g_strdup (pllink->name); /* If the device has no explicit ip_iface, then changing iface changes ip_iface too. */ ip_ifname_changed = !priv->ip_iface; @@ -2568,8 +2734,8 @@ device_link_changed (NMDevice *self) nm_device_emit_recheck_auto_activate (self); } - if (priv->ndisc && info.inet6_token.id) { - if (nm_ndisc_set_iid (priv->ndisc, info.inet6_token)) + if (priv->ndisc && pllink->inet6_token.id) { + if (nm_ndisc_set_iid (priv->ndisc, pllink->inet6_token)) _LOGD (LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface); } @@ -2578,20 +2744,21 @@ device_link_changed (NMDevice *self) && !nm_device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) nm_device_set_carrier (self, pllink->connected); - klass->link_changed (self, &info); + klass->link_changed (self, pllink); /* Update DHCP, etc, if needed */ if (ip_ifname_changed) nm_device_update_dynamic_ip_setup (self); was_up = priv->up; - priv->up = NM_FLAGS_HAS (info.n_ifi_flags, IFF_UP); + priv->up = NM_FLAGS_HAS (pllink->n_ifi_flags, IFF_UP); - if ( info.initialized + if ( pllink->initialized && nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) { NMDeviceStateReason reason; nm_device_set_unmanaged_by_user_udev (self); + nm_device_set_unmanaged_by_user_conf (self); reason = NM_DEVICE_STATE_REASON_NOW_MANAGED; @@ -2616,13 +2783,13 @@ device_link_changed (NMDevice *self) set_unmanaged_external_down (self, FALSE); - device_recheck_slave_status (self, &info); + device_recheck_slave_status (self, pllink); if (priv->up && !was_up) { /* the link was down and just came up. That happens for example, while changing MTU. * We must restore IP configuration. */ if (priv->ip4_state == IP_DONE) { - if (!ip4_config_merge_and_apply (self, NULL, TRUE)) + if (!ip4_config_merge_and_apply (self, TRUE)) _LOGW (LOGD_IP4, "failed applying IP4 config after link comes up again"); } if (priv->ip6_state == IP_DONE) { @@ -2703,6 +2870,103 @@ link_changed_cb (NMPlatform *platform, } } +/*****************************************************************************/ + +typedef struct { + in_addr_t network; + guint8 plen; +} IP4RPFilterData; + +static guint +_v4_has_shadowed_routes_detect_hash (const IP4RPFilterData *d) +{ + NMHashState h; + + nm_hash_init (&h, 1105201169u); + nm_hash_update_vals (&h, + d->network, + d->plen); + return nm_hash_complete (&h); +} + +static gboolean +_v4_has_shadowed_routes_detect_equal (const IP4RPFilterData *d1, const IP4RPFilterData *d2) +{ + return d1->network == d2->network && d1->plen == d2->plen; +} + +static gboolean +_v4_has_shadowed_routes_detect (NMDevice *self) +{ + NMPlatform *platform; + int ifindex; + NMPLookup lookup; + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *o; + guint data_len; + gs_unref_hashtable GHashTable *data_hash = NULL; + gs_free IP4RPFilterData *data_arr = NULL; + + ifindex = nm_device_get_ip_ifindex (self); + if (ifindex <= 0) + return FALSE; + + platform = nm_device_get_platform (self); + + head_entry = nm_platform_lookup (platform, + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP4_ROUTE, + ifindex)); + if (!head_entry) + return FALSE; + + /* first, create a lookup index @data_hash for all network/plen pairs. */ + data_len = 0; + data_arr = g_new (IP4RPFilterData, head_entry->len); + data_hash = g_hash_table_new ((GHashFunc) _v4_has_shadowed_routes_detect_hash, + (GEqualFunc) _v4_has_shadowed_routes_detect_equal); + + nmp_cache_iter_for_each (&iter, head_entry, &o) { + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (o); + IP4RPFilterData *d; + + nm_assert (r->ifindex == ifindex); + + if ( NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + || r->table_coerced) + continue; + + d = &data_arr[data_len++]; + d->network = nm_utils_ip4_address_clear_host_address (r->network, r->plen); + d->plen = r->plen; + g_hash_table_add (data_hash, d); + } + + /* then, search if there is any route on another interface with the same + * network/plen destination. If yes, we consider this a multihoming + * setup. */ + head_entry = nm_platform_lookup (platform, + nmp_lookup_init_obj_type (&lookup, + NMP_OBJECT_TYPE_IP4_ROUTE)); + nmp_cache_iter_for_each (&iter, head_entry, &o) { + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (o); + IP4RPFilterData d; + + if ( r->ifindex == ifindex + || NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + || r->table_coerced) + continue; + + d.network = nm_utils_ip4_address_clear_host_address (r->network, r->plen); + d.plen = r->plen; + if (g_hash_table_contains (data_hash, &d)) + return TRUE; + } + + return FALSE; +} + static void ip4_rp_filter_update (NMDevice *self) { @@ -2710,7 +2974,7 @@ ip4_rp_filter_update (NMDevice *self) const char *ip4_rp_filter; if ( priv->v4_has_shadowed_routes - || priv->default_route.v4_has) { + || nm_device_get_best_default_route (self, AF_INET)) { if (nm_device_ipv4_sysctl_get_uint32 (self, "rp_filter", 0) != 1) { /* Don't touch the rp_filter if it's not strict. */ return; @@ -2729,20 +2993,6 @@ ip4_rp_filter_update (NMDevice *self) } static void -ip4_routes_changed_changed_cb (NMRouteManager *route_manager, NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ifindex = nm_device_get_ip_ifindex (self); - - if (nm_device_sys_iface_state_is_external_or_assume (self)) - return; - - priv->v4_has_shadowed_routes = nm_route_manager_ip4_routes_shadowed (route_manager, - ifindex); - ip4_rp_filter_update (self); -} - -static void link_changed (NMDevice *self, const NMPlatformLink *pllink) { /* stub implementation of virtual function to allow subclasses to chain up. */ @@ -2822,7 +3072,9 @@ nm_device_realize_start (NMDevice *self, gboolean *out_compatible, GError **error) { - NMPlatformLink plink_copy; + nm_auto_nmpobj const NMPObject *plink_keep_alive = NULL; + + nm_assert (!plink || NMP_OBJECT_GET_TYPE (NMP_OBJECT_UP_CAST (plink)) == NMP_OBJECT_TYPE_LINK); NM_SET_OUT (out_compatible, TRUE); @@ -2836,13 +3088,12 @@ nm_device_realize_start (NMDevice *self, if (!link_type_compatible (self, plink->type, out_compatible, error)) return FALSE; - } - if (plink) { - plink_copy = *plink; - plink = &plink_copy; + plink_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (plink)); } - realize_start_setup (self, plink, + + realize_start_setup (self, + plink, assume_state_guess_assume, assume_state_connection_uuid, set_nm_owned, @@ -2868,8 +3119,8 @@ nm_device_create_and_realize (NMDevice *self, NMDevice *parent, GError **error) { + nm_auto_nmpobj const NMPObject *plink_keep_alive = NULL; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMPlatformLink plink_copy; const NMPlatformLink *plink = NULL; /* Must be set before device is realized */ @@ -2881,11 +3132,14 @@ nm_device_create_and_realize (NMDevice *self, if (NM_DEVICE_GET_CLASS (self)->create_and_realize) { if (!NM_DEVICE_GET_CLASS (self)->create_and_realize (self, connection, parent, &plink, error)) return FALSE; - plink_copy = *plink; - plink = &plink_copy; + if (plink) { + nm_assert (NMP_OBJECT_GET_TYPE (NMP_OBJECT_UP_CAST (plink)) == NMP_OBJECT_TYPE_LINK); + plink_keep_alive = nmp_object_ref (NMP_OBJECT_UP_CAST (plink)); + } } - realize_start_setup (self, plink, + realize_start_setup (self, + plink, FALSE, /* assume_state_guess_assume */ NULL, /* assume_state_connection_uuid */ FALSE, NM_UNMAN_FLAG_OP_FORGET); @@ -2899,38 +3153,54 @@ nm_device_create_and_realize (NMDevice *self, return TRUE; } -static void -update_device_from_platform_link (NMDevice *self, const NMPlatformLink *plink) +void +nm_device_update_from_platform_link (NMDevice *self, const NMPlatformLink *plink) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *udi; + const char *str; + int ifindex; + guint32 mtu; - g_return_if_fail (plink != NULL); + g_return_if_fail (plink == NULL || link_type_compatible (self, plink->type, NULL, NULL)); - udi = nm_platform_link_get_udi (nm_device_get_platform (self), plink->ifindex); - if (udi && !nm_streq0 (udi, priv->udi)) { + str = plink ? nm_platform_link_get_udi (nm_device_get_platform (self), plink->ifindex) : NULL; + if (g_strcmp0 (str, priv->udi)) { g_free (priv->udi); - priv->udi = g_strdup (udi); + priv->udi = g_strdup (str); _notify (self, PROP_UDI); } - if (!g_strcmp0 (plink->name, priv->iface)) { + str = plink ? plink->name : NULL; + if (str && g_strcmp0 (str, priv->iface)) { g_free (priv->iface); - priv->iface = g_strdup (plink->name); + priv->iface = g_strdup (str); _notify (self, PROP_IFACE); } - if (priv->ifindex != plink->ifindex) { - priv->ifindex = plink->ifindex; - _notify (self, PROP_IFINDEX); - } - - priv->up = NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP); - if (plink->driver && g_strcmp0 (plink->driver, priv->driver) != 0) { + str = plink ? plink->driver : NULL; + if (g_strcmp0 (str, priv->driver) != 0) { g_free (priv->driver); - priv->driver = g_strdup (plink->driver); + priv->driver = g_strdup (str); _notify (self, PROP_DRIVER); } + + if (plink) { + priv->up = NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP); + if (plink->ifindex == nm_device_get_ip_ifindex (self)) + _stats_update_counters_from_pllink (self, plink); + } else { + priv->up = FALSE; + } + + mtu = plink ? plink->mtu : 0; + _set_mtu (self, mtu); + + ifindex = plink ? plink->ifindex : 0; + if (priv->ifindex != ifindex) { + priv->ifindex = ifindex; + _notify (self, PROP_IFINDEX); + NM_DEVICE_GET_CLASS (self)->link_changed (self, plink); + } } static void @@ -2943,7 +3213,7 @@ device_init_sriov_num_vfs (NMDevice *self) if ( priv->ifindex > 0 && nm_device_has_capability (self, NM_DEVICE_CAP_SRIOV)) { value = nm_config_data_get_device_config (NM_CONFIG_GET_DATA, - "sriov-num-vfs", + NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS, self, NULL); num_vfs = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXINT32, -1); @@ -3010,7 +3280,10 @@ realize_start_setup (NMDevice *self, NMDeviceCapabilities capabilities = 0; NMConfig *config; guint real_rate; - guint32 mtu; + + /* plink is a NMPlatformLink type, however, we require it to come from the platform + * cache (where else would it come from?). */ + nm_assert (!plink || NMP_OBJECT_GET_TYPE (NMP_OBJECT_UP_CAST (plink)) == NMP_OBJECT_TYPE_LINK); g_return_if_fail (NM_IS_DEVICE (self)); @@ -3035,20 +3308,14 @@ realize_start_setup (NMDevice *self, priv->mtu_initial = 0; priv->ip6_mtu_initial = 0; priv->ip6_mtu = 0; - if (priv->mtu) { - priv->mtu = 0; - _notify (self, PROP_MTU); - } + _set_mtu (self, 0); _assume_state_set (self, assume_state_guess_assume, assume_state_connection_uuid); nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_EXTERNAL); - 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 (plink) + nm_device_update_from_platform_link (self, plink); if (priv->ifindex > 0) { priv->physical_port_id = nm_platform_link_get_physical_port_id (nm_device_get_platform (self), priv->ifindex); @@ -3059,11 +3326,9 @@ realize_start_setup (NMDevice *self, if (nm_platform_link_is_software (nm_device_get_platform (self), priv->ifindex)) capabilities |= NM_DEVICE_CAP_IS_SOFTWARE; - mtu = nm_platform_link_get_mtu (nm_device_get_platform (self), priv->ifindex); - if (priv->mtu != mtu) { - priv->mtu = mtu; - _notify (self, PROP_MTU); - } + _set_mtu (self, + nm_platform_link_get_mtu (nm_device_get_platform (self), + priv->ifindex)); nm_platform_link_get_driver_info (nm_device_get_platform (self), priv->ifindex, @@ -3075,7 +3340,8 @@ realize_start_setup (NMDevice *self, if (priv->firmware_version) _notify (self, PROP_FIRMWARE_VERSION); - if (nm_platform_check_support_user_ipv6ll (nm_device_get_platform (self))) + if (nm_platform_check_kernel_support (nm_device_get_platform (self), + NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ifindex); if (nm_platform_link_supports_sriov (nm_device_get_platform (self), priv->ifindex)) @@ -3147,6 +3413,7 @@ realize_start_setup (NMDevice *self, nm_device_set_unmanaged_flags (self, NM_UNMANAGED_LOOPBACK, priv->ifindex == 1); nm_device_set_unmanaged_by_user_udev (self); + nm_device_set_unmanaged_by_user_conf (self); nm_device_set_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT, plink && !plink->initialized); @@ -3257,7 +3524,6 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) g_return_val_if_fail (priv->iface != NULL, FALSE); g_return_val_if_fail (priv->real, FALSE); - g_object_freeze_notify (G_OBJECT (self)); ifindex = nm_device_get_ifindex (self); @@ -3274,6 +3540,7 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) } } + g_object_freeze_notify (G_OBJECT (self)); NM_DEVICE_GET_CLASS (self)->unrealize_notify (self); _parent_set_ifindex (self, 0, FALSE); @@ -3286,10 +3553,7 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) if (nm_clear_g_free (&priv->ip_iface)) _notify (self, PROP_IP_IFACE); - if (priv->mtu != 0) { - priv->mtu = 0; - _notify (self, PROP_MTU); - } + _set_mtu (self, 0); if (priv->driver_version) { g_clear_pointer (&priv->driver_version, g_free); @@ -3373,13 +3637,25 @@ gboolean nm_device_notify_component_added (NMDevice *self, GObject *component) { NMDeviceClass *klass; + NMDevicePrivate *priv; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - g_return_val_if_fail (G_IS_OBJECT (component), FALSE); + priv = NM_DEVICE_GET_PRIVATE (self); klass = NM_DEVICE_GET_CLASS (self); + + if (priv->state == NM_DEVICE_STATE_DISCONNECTED) { + /* A device could have stayed disconnected because it would + * want to register with a network server that now become + * available. */ + nm_device_recheck_available_connections (self); + if (g_hash_table_size (priv->available_connections) > 0) + nm_device_emit_recheck_auto_activate (self); + } + if (klass->component_added) return klass->component_added (self, component); + return FALSE; } @@ -3453,7 +3729,8 @@ slave_state_changed (NMDevice *slave, configure, reason); /* Bridge/bond/team interfaces are left up until manually deactivated */ - if (priv->slaves == NULL && priv->state == NM_DEVICE_STATE_ACTIVATED) + if ( c_list_is_empty (&priv->slaves) + && priv->state == NM_DEVICE_STATE_ACTIVATED) _LOGD (LOGD_DEVICE, "last slave removed; remaining activated"); } } @@ -3504,9 +3781,12 @@ nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure) info->watch_id = g_signal_connect (slave, NM_DEVICE_STATE_CHANGED, G_CALLBACK (slave_state_changed), self); - priv->slaves = g_slist_append (priv->slaves, info); + c_list_link_tail (&priv->slaves, &info->lst_slave); slave_priv->master = g_object_ref (self); + _active_connection_set_state_flags (self, + NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES); + /* no need to emit * * _notify (slave, PROP_MASTER); @@ -3527,46 +3807,6 @@ nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure) } /** - * nm_device_master_get_slaves: - * @self: the master device - * - * Returns: any slaves of which @self is the master. Caller owns returned list. - */ -static GSList * -nm_device_master_get_slaves (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - GSList *slaves = NULL, *iter; - - for (iter = priv->slaves; iter; iter = g_slist_next (iter)) - slaves = g_slist_prepend (slaves, ((SlaveInfo *) iter->data)->slave); - - return slaves; -} - -/** - * nm_device_master_get_slave_by_ifindex: - * @self: the master device - * @ifindex: the slave's interface index - * - * Returns: the slave with the given @ifindex of which @self is the master, - * or %NULL if no device with @ifindex is a slave of @self. - */ -NMDevice * -nm_device_master_get_slave_by_ifindex (NMDevice *self, int ifindex) -{ - GSList *iter; - - for (iter = NM_DEVICE_GET_PRIVATE (self)->slaves; iter; iter = g_slist_next (iter)) { - SlaveInfo *info = iter->data; - - if (nm_device_get_ip_ifindex (info->slave) == ifindex) - return info->slave; - } - return NULL; -} - -/** * nm_device_master_check_slave_physical_port: * @self: the master device * @slave: a slave device @@ -3582,14 +3822,14 @@ nm_device_master_check_slave_physical_port (NMDevice *self, NMDevice *slave, NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const char *slave_physical_port_id, *existing_physical_port_id; SlaveInfo *info; - GSList *iter; + CList *iter; slave_physical_port_id = nm_device_get_physical_port_id (slave); if (!slave_physical_port_id) return; - for (iter = priv->slaves; iter; iter = iter->next) { - info = iter->data; + c_list_for_each (iter, &priv->slaves) { + info = c_list_entry (iter, SlaveInfo, lst_slave); if (info->slave == slave) continue; @@ -3615,6 +3855,7 @@ nm_device_master_release_slaves (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDeviceStateReason reason; gboolean configure = TRUE; + CList *iter, *safe; /* Don't release the slaves if this connection doesn't belong to NM. */ if (nm_device_sys_iface_state_is_external (self)) @@ -3627,8 +3868,8 @@ nm_device_master_release_slaves (NMDevice *self) if (!nm_platform_link_get (nm_device_get_platform (self), priv->ifindex)) configure = FALSE; - while (priv->slaves) { - SlaveInfo *info = priv->slaves->data; + c_list_for_each_safe (iter, safe, &priv->slaves) { + SlaveInfo *info = c_list_entry (iter, SlaveInfo, lst_slave); nm_device_master_release_one_slave (self, info->slave, configure, reason); } @@ -3643,7 +3884,9 @@ nm_device_master_release_slaves (NMDevice *self) gboolean nm_device_is_master (NMDevice *self) { - return NM_DEVICE_GET_PRIVATE (self)->is_master; + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + return NM_DEVICE_GET_CLASS (self)->is_master; } /** @@ -3669,6 +3912,113 @@ nm_device_get_master (NMDevice *self) return NULL; } +static gboolean +get_ip_config_may_fail (NMDevice *self, int addr_family) +{ + NMConnection *connection; + NMSettingIPConfig *s_ip = NULL; + + connection = nm_device_get_applied_connection (self); + + /* Fail the connection if the failed IP method is required to complete */ + switch (addr_family) { + case AF_INET: + s_ip = nm_connection_get_setting_ip4_config (connection); + break; + case AF_INET6: + s_ip = nm_connection_get_setting_ip6_config (connection); + break; + default: + nm_assert_not_reached (); + } + + return !s_ip || nm_setting_ip_config_get_may_fail (s_ip); +} + +/* + * check_ip_state + * + * Transition the device from IP_CONFIG to the next state according to the + * outcome of IPv4 and IPv6 configuration. @may_fail indicates that we are + * called just after the initial configuration and thus IPv4/IPv6 are allowed to + * fail if the ipvx.may-fail properties say so, because the IP methods couldn't + * even be started. + */ +static void +check_ip_state (NMDevice *self, gboolean may_fail) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean ip4_disabled = FALSE, ip6_ignore = FALSE; + NMSettingIPConfig *s_ip4, *s_ip6; + NMDeviceState state; + + if (nm_device_get_state (self) != NM_DEVICE_STATE_IP_CONFIG) + return; + + /* Don't progress into IP_CHECK or SECONDARIES if we're waiting for the + * master to enslave us. */ + if ( nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request)) + && !priv->is_enslaved) + return; + + s_ip4 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP4_CONFIG); + if (s_ip4 && nm_streq0 (nm_setting_ip_config_get_method (s_ip4), + NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) + ip4_disabled = TRUE; + + s_ip6 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); + if (s_ip6 && nm_streq0 (nm_setting_ip_config_get_method (s_ip6), + NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) + ip6_ignore = TRUE; + + if ( priv->ip4_state == IP_DONE + && priv->ip6_state == IP_DONE) { + /* Both method completed (or disabled), proceed with activation */ + nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); + return; + } + + if ( (priv->ip4_state == IP_FAIL || (ip4_disabled && priv->ip4_state == IP_DONE)) + && (priv->ip6_state == IP_FAIL || (ip6_ignore && priv->ip6_state == IP_DONE))) { + /* Either both methods failed, or only one failed and the other is + * disabled */ + if (nm_device_sys_iface_state_is_external_or_assume (self)) { + /* We have assumed configuration, but couldn't redo it. No problem, + * move to check state. */ + _set_ip_state (self, AF_INET, IP_DONE); + _set_ip_state (self, AF_INET6, IP_DONE); + state = NM_DEVICE_STATE_IP_CHECK; + } else if ( may_fail + && get_ip_config_may_fail (self, AF_INET) + && get_ip_config_may_fail (self, AF_INET6)) { + /* Couldn't start either IPv6 and IPv4 autoconfiguration, + * but both are allowed to fail. */ + state = NM_DEVICE_STATE_SECONDARIES; + } else { + /* Autoconfiguration attempted without success. */ + state = NM_DEVICE_STATE_FAILED; + } + + nm_device_state_changed (self, + state, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + return; + } + + /* If a method is still pending but required, wait */ + if (priv->ip4_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET)) + return; + if (priv->ip6_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET6)) + return; + + /* If at least a method has completed, proceed with activation */ + if ( (priv->ip4_state == IP_DONE && !ip4_disabled) + || (priv->ip6_state == IP_DONE && !ip6_ignore)) { + nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); + return; + } +} + /** * nm_device_slave_notify_enslave: * @self: the slave device @@ -3705,10 +4055,8 @@ nm_device_slave_notify_enslave (NMDevice *self, gboolean success) } if (activating) { - _set_ip_state (self, AF_INET, IP_DONE); - _set_ip_state (self, AF_INET6, IP_DONE); if (success) - nm_device_queue_state (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE); + check_ip_state (self, FALSE); else nm_device_queue_state (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_UNKNOWN); } else @@ -3767,19 +4115,6 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason) } /** - * nm_device_get_enslaved: - * @self: the #NMDevice - * - * Returns: %TRUE if the device is enslaved to a master device (eg bridge or - * bond or team), %FALSE if not - */ -gboolean -nm_device_get_enslaved (NMDevice *self) -{ - return NM_DEVICE_GET_PRIVATE (self)->is_enslaved; -} - -/** * nm_device_removed: * @self: the #NMDevice * @unconfigure_ip_config: whether to clear the IP config objects @@ -3805,17 +4140,8 @@ nm_device_removed (NMDevice *self, gboolean unconfigure_ip_config) if (!unconfigure_ip_config) return; - /* Clean up IP configs; this does not actually deconfigure the - * interface, it just clears the configuration to which policy - * is reacting via NM_DEVICE_IP4_CONFIG_CHANGED/NM_DEVICE_IP6_CONFIG_CHANGED - * signal. As NMPolicy registered the NMIPxConfig instances in NMDnsManager, - * these would be leaked otherwise. */ - _update_default_route (self, AF_INET, priv->default_route.v4_has, TRUE); - _update_default_route (self, AF_INET6, priv->default_route.v6_has, TRUE); - _update_default_route (self, AF_INET, FALSE, TRUE); - _update_default_route (self, AF_INET6, FALSE, TRUE); - nm_device_set_ip4_config (self, NULL, 0, FALSE, FALSE); - nm_device_set_ip6_config (self, NULL, FALSE, FALSE); + nm_device_set_ip4_config (self, NULL, FALSE, NULL); + nm_device_set_ip6_config (self, NULL, FALSE); } static gboolean @@ -3823,12 +4149,17 @@ is_available (NMDevice *self, NMDeviceCheckDevAvailableFlags flags) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->carrier || priv->ignore_carrier) + if ( priv->carrier + || priv->ignore_carrier) return TRUE; if (NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER)) return TRUE; + /* master types are always available even without carrier. */ + if (nm_device_is_master (self)) + return TRUE; + return FALSE; } @@ -3863,6 +4194,13 @@ nm_device_is_available (NMDevice *self, NMDeviceCheckDevAvailableFlags flags) } gboolean +nm_device_ignore_carrier_by_default (NMDevice *self) +{ + /* master types ignore-carrier by default. */ + return nm_device_is_master (self); +} + +gboolean nm_device_get_enabled (NMDevice *self) { g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); @@ -4057,7 +4395,8 @@ device_has_config (NMDevice *self) return TRUE; /* Master-slave relationship is also a configuration */ - if (priv->slaves || nm_platform_link_get_master (nm_device_get_platform (self), priv->ifindex) > 0) + if ( !c_list_is_empty (&priv->slaves) + || nm_platform_link_get_master (nm_device_get_platform (self), priv->ifindex) > 0) return TRUE; return FALSE; @@ -4179,6 +4518,8 @@ nm_device_generate_connection (NMDevice *self, s_ip6 = nm_ip6_config_create_setting (priv->ip6_config); nm_connection_add_setting (connection, s_ip6); + nm_connection_add_setting (connection, nm_setting_proxy_new ()); + pllink = nm_platform_link_get (nm_device_get_platform (self), priv->ifindex); if (pllink && pllink->inet6_token.id) { g_object_set (s_ip6, @@ -4206,7 +4547,7 @@ nm_device_generate_connection (NMDevice *self, if ( g_strcmp0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0 && g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0 && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con)) - && !priv->slaves) { + && c_list_is_empty (&priv->slaves)) { NM_SET_OUT (out_maybe_later, TRUE); g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "ignoring generated connection (no IP and not in master-slave relationship)"); @@ -4219,7 +4560,7 @@ nm_device_generate_connection (NMDevice *self, if ( g_strcmp0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0 && g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0 && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con)) - && !priv->slaves + && c_list_is_empty (&priv->slaves) && !nm_config_data_get_assume_ipv6ll_only (NM_CONFIG_GET_DATA, self)) { _LOGD (LOGD_DEVICE, "ignoring generated connection (IPv6LL-only and not in master-slave relationship)"); NM_SET_OUT (out_maybe_later, TRUE); @@ -4261,6 +4602,65 @@ nm_device_complete_connection (NMDevice *self, return success; } +gboolean +nm_device_match_parent (NMDevice *self, const char *parent) +{ + NMDevice *parent_device; + + g_return_val_if_fail (parent, FALSE); + + parent_device = nm_device_parent_get_device (self); + if (!parent_device) + return FALSE; + + if (nm_utils_is_uuid (parent)) { + NMConnection *connection; + + /* If the parent is a UUID, the connection matches when there is + * no connection active on the device or when a connection with + * that UUID is active. + */ + connection = nm_device_get_applied_connection (self); + if (!connection) + return TRUE; + + if (!nm_streq0 (parent, nm_connection_get_uuid (connection))) + return FALSE; + } else { + /* Interface name */ + if (!nm_streq0 (parent, nm_device_get_ip_iface (parent_device))) + return FALSE; + } + + return TRUE; +} + +gboolean +nm_device_match_hwaddr (NMDevice *device, + NMConnection *connection, + gboolean fail_if_no_hwaddr) +{ + NMSettingWired *s_wired; + NMDevice *parent_device; + const char *setting_mac; + const char *parent_mac; + + s_wired = nm_connection_get_setting_wired (connection); + if (!s_wired) + return !fail_if_no_hwaddr; + + setting_mac = nm_setting_wired_get_mac_address (s_wired); + if (!setting_mac) + return !fail_if_no_hwaddr; + + parent_device = nm_device_parent_get_device (device); + if (!parent_device) + return !fail_if_no_hwaddr; + + parent_mac = nm_device_get_permanent_hw_address (parent_device); + return parent_mac && nm_utils_hwaddr_matches (setting_mac, -1, parent_mac, -1); +} + static gboolean check_connection_compatible (NMDevice *self, NMConnection *connection) { @@ -4317,7 +4717,7 @@ nm_device_check_slave_connection_compatible (NMDevice *self, NMConnection *slave priv = NM_DEVICE_GET_PRIVATE (self); - if (!priv->is_master) + if (!nm_device_is_master (self)) return FALSE; /* All masters should have connection type set */ @@ -4506,44 +4906,49 @@ activation_source_handle_cb6 (gpointer user_data) static ActivationHandleData * activation_source_get_by_family (NMDevice *self, - int family, + int addr_family, GSourceFunc *out_idle_func) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (family == AF_INET6) { + switch (addr_family) { + case AF_INET6: NM_SET_OUT (out_idle_func, activation_source_handle_cb6); return &priv->act_handle6; - } else { + case AF_INET: NM_SET_OUT (out_idle_func, activation_source_handle_cb4); - g_return_val_if_fail (family == AF_INET, &priv->act_handle4); return &priv->act_handle4; } + g_return_val_if_reached (NULL); } static void -activation_source_clear (NMDevice *self, int family) +activation_source_clear (NMDevice *self, + int addr_family) { ActivationHandleData *act_data; - act_data = activation_source_get_by_family (self, family, NULL); + act_data = activation_source_get_by_family (self, addr_family, NULL); if (act_data->id) { - _LOGD (LOGD_DEVICE, "activation-stage: clear %s,%d (id %u)", - _activation_func_to_string (act_data->func), family, act_data->id); + _LOGD (LOGD_DEVICE, "activation-stage: clear %s,v%c (id %u)", + _activation_func_to_string (act_data->func), + nm_utils_addr_family_to_char (addr_family), + act_data->id); nm_clear_g_source (&act_data->id); act_data->func = NULL; } } static void -activation_source_handle_cb (NMDevice *self, int family) +activation_source_handle_cb (NMDevice *self, + int addr_family) { ActivationHandleData *act_data, a; g_return_if_fail (NM_IS_DEVICE (self)); - act_data = activation_source_get_by_family (self, family, NULL); + act_data = activation_source_get_by_family (self, addr_family, NULL); g_return_if_fail (act_data->id); g_return_if_fail (act_data->func); @@ -4553,23 +4958,27 @@ activation_source_handle_cb (NMDevice *self, int family) act_data->func = NULL; act_data->id = 0; - _LOGD (LOGD_DEVICE, "activation-stage: invoke %s,%d (id %u)", - _activation_func_to_string (a.func), family, a.id); + _LOGD (LOGD_DEVICE, "activation-stage: invoke %s,v%c (id %u)", + _activation_func_to_string (a.func), + nm_utils_addr_family_to_char (addr_family), + a.id); a.func (self); - _LOGD (LOGD_DEVICE, "activation-stage: complete %s,%d (id %u)", - _activation_func_to_string (a.func), family, a.id); + _LOGD (LOGD_DEVICE, "activation-stage: complete %s,v%c (id %u)", + _activation_func_to_string (a.func), + nm_utils_addr_family_to_char (addr_family), + a.id); } static void -activation_source_schedule (NMDevice *self, ActivationHandleFunc func, int family) +activation_source_schedule (NMDevice *self, ActivationHandleFunc func, int addr_family) { ActivationHandleData *act_data; GSourceFunc source_func; guint new_id = 0; - act_data = activation_source_get_by_family (self, family, &source_func); + act_data = activation_source_get_by_family (self, addr_family, &source_func); if (act_data->id && act_data->func == func) { /* Don't bother rescheduling the same function that's about to @@ -4577,22 +4986,28 @@ activation_source_schedule (NMDevice *self, ActivationHandleFunc func, int famil * streams of associate events before NM has had a chance to process * the first one. */ - _LOGD (LOGD_DEVICE, "activation-stage: already scheduled %s,%d (id %u)", - _activation_func_to_string (func), family, act_data->id); + _LOGD (LOGD_DEVICE, "activation-stage: already scheduled %s,v%c (id %u)", + _activation_func_to_string (func), + nm_utils_addr_family_to_char (addr_family), + act_data->id); return; } new_id = g_idle_add (source_func, self); if (act_data->id) { - _LOGW (LOGD_DEVICE, "activation-stage: schedule %s,%d which replaces %s,%d (id %u -> %u)", - _activation_func_to_string (func), family, - _activation_func_to_string (act_data->func), family, + _LOGW (LOGD_DEVICE, "activation-stage: schedule %s,v%c which replaces %s,v%c (id %u -> %u)", + _activation_func_to_string (func), + nm_utils_addr_family_to_char (addr_family), + _activation_func_to_string (act_data->func), + nm_utils_addr_family_to_char (addr_family), act_data->id, new_id); nm_clear_g_source (&act_data->id); } else { - _LOGD (LOGD_DEVICE, "activation-stage: schedule %s,%d (id %u)", - _activation_func_to_string (func), family, new_id); + _LOGD (LOGD_DEVICE, "activation-stage: schedule %s,v%c (id %u)", + _activation_func_to_string (func), + nm_utils_addr_family_to_char (addr_family), + new_id); } act_data->func = func; @@ -4600,42 +5015,18 @@ activation_source_schedule (NMDevice *self, ActivationHandleFunc func, int famil } static gboolean -activation_source_is_scheduled (NMDevice *self, ActivationHandleFunc func, int family) +activation_source_is_scheduled (NMDevice *self, + ActivationHandleFunc func, + int addr_family) { ActivationHandleData *act_data; - act_data = activation_source_get_by_family (self, family, NULL); + act_data = activation_source_get_by_family (self, addr_family, NULL); return act_data->func == func; } /*****************************************************************************/ -static gboolean -get_ip_config_may_fail (NMDevice *self, int family) -{ - NMConnection *connection; - NMSettingIPConfig *s_ip = NULL; - - g_return_val_if_fail (self != NULL, TRUE); - - connection = nm_device_get_applied_connection (self); - g_assert (connection); - - /* Fail the connection if the failed IP method is required to complete */ - switch (family) { - case AF_INET: - s_ip = nm_connection_get_setting_ip4_config (connection); - break; - case AF_INET6: - s_ip = nm_connection_get_setting_ip6_config (connection); - break; - default: - g_assert_not_reached (); - } - - return !s_ip || nm_setting_ip_config_get_may_fail (s_ip); -} - static void master_ready (NMDevice *self, NMActiveConnection *active) @@ -4836,7 +5227,7 @@ activate_stage2_device_config (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActStageReturn ret; gboolean no_firmware = FALSE; - GSList *iter; + CList *iter; nm_device_state_changed (self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE); @@ -4863,8 +5254,8 @@ activate_stage2_device_config (NMDevice *self) } /* If we have slaves that aren't yet enslaved, do that now */ - for (iter = priv->slaves; iter; iter = g_slist_next (iter)) { - SlaveInfo *info = iter->data; + c_list_for_each (iter, &priv->slaves) { + SlaveInfo *info = c_list_entry (iter, SlaveInfo, lst_slave); NMDeviceState slave_state = nm_device_get_state (info->slave); if (slave_state == NM_DEVICE_STATE_IP_CONFIG) @@ -4930,97 +5321,21 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self) activation_source_schedule (self, activate_stage2_device_config, AF_INET); } -/* - * check_ip_state - * - * Transition the device from IP_CONFIG to the next state according to the - * outcome of IPv4 and IPv6 configuration. @may_fail indicates that we are - * called just after the initial configuration and thus IPv4/IPv6 are allowed to - * fail if the ipvx.may-fail properties say so, because the IP methods couldn't - * even be started. - */ -static void -check_ip_state (NMDevice *self, gboolean may_fail) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean ip4_disabled = FALSE, ip6_ignore = FALSE; - NMSettingIPConfig *s_ip4, *s_ip6; - NMDeviceState state; - - if (nm_device_get_state (self) != NM_DEVICE_STATE_IP_CONFIG) - return; - - s_ip4 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP4_CONFIG); - if (s_ip4 && nm_streq0 (nm_setting_ip_config_get_method (s_ip4), - NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) - ip4_disabled = TRUE; - - s_ip6 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); - if (s_ip6 && nm_streq0 (nm_setting_ip_config_get_method (s_ip6), - NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) - ip6_ignore = TRUE; - - if ( priv->ip4_state == IP_DONE - && priv->ip6_state == IP_DONE) { - /* Both method completed (or disabled), proceed with activation */ - nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); - return; - } - - if ( (priv->ip4_state == IP_FAIL || (ip4_disabled && priv->ip4_state == IP_DONE)) - && (priv->ip6_state == IP_FAIL || (ip6_ignore && priv->ip6_state == IP_DONE))) { - /* Either both methods failed, or only one failed and the other is - * disabled */ - if (nm_device_sys_iface_state_is_external_or_assume (self)) { - /* We have assumed configuration, but couldn't redo it. No problem, - * move to check state. */ - _set_ip_state (self, AF_INET, IP_DONE); - _set_ip_state (self, AF_INET6, IP_DONE); - state = NM_DEVICE_STATE_IP_CHECK; - } else if ( may_fail - && get_ip_config_may_fail (self, AF_INET) - && get_ip_config_may_fail (self, AF_INET6)) { - /* Couldn't start either IPv6 and IPv4 autoconfiguration, - * but both are allowed to fail. */ - state = NM_DEVICE_STATE_SECONDARIES; - } else { - /* Autoconfiguration attempted without success. */ - state = NM_DEVICE_STATE_FAILED; - } - - nm_device_state_changed (self, - state, - NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return; - } - - /* If a method is still pending but required, wait */ - if (priv->ip4_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET)) - return; - if (priv->ip6_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET6)) - return; - - /* If at least a method has completed, proceed with activation */ - if ( (priv->ip4_state == IP_DONE && !ip4_disabled) - || (priv->ip6_state == IP_DONE && !ip6_ignore)) { - nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); - return; - } -} - void -nm_device_ip_method_failed (NMDevice *self, int family, NMDeviceStateReason reason) +nm_device_ip_method_failed (NMDevice *self, + int addr_family, + NMDeviceStateReason reason) { NMDevicePrivate *priv; g_return_if_fail (NM_IS_DEVICE (self)); - g_return_if_fail (family == AF_INET || family == AF_INET6); + g_return_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6)); priv = NM_DEVICE_GET_PRIVATE (self); - _set_ip_state (self, family, IP_FAIL); + _set_ip_state (self, addr_family, IP_FAIL); - if (get_ip_config_may_fail (self, family)) + if (get_ip_config_may_fail (self, addr_family)) check_ip_state (self, FALSE); else nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); @@ -5077,7 +5392,7 @@ ipv4_manual_method_apply (NMDevice *self, NMIP4Config **configs, gboolean succes NMIP4Config *empty; if (success) { - empty = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + empty = _ip4_config_new (self); nm_device_activate_schedule_ip4_config_result (self, empty); g_object_unref (empty); } else { @@ -5091,17 +5406,17 @@ arping_manager_probe_terminated (NMArpingManager *arping_manager, ArpingData *da { NMDevice *self; NMDevicePrivate *priv; + NMDedupMultiIter ipconf_iter; const NMPlatformIP4Address *address; gboolean result, success = TRUE; - int i, j; + int i; g_assert (data); self = data->device; priv = NM_DEVICE_GET_PRIVATE (self); for (i = 0; data->configs && data->configs[i]; i++) { - for (j = 0; j < nm_ip4_config_get_num_addresses (data->configs[i]); j++) { - address = nm_ip4_config_get_address (data->configs[i], j); + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, data->configs[i], &address) { result = nm_arping_manager_check_address (arping_manager, address->address); success &= result; @@ -5135,13 +5450,14 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMArpingManager *arping_manager; const NMPlatformIP4Address *address; + NMDedupMultiIter ipconf_iter; ArpingData *data; guint timeout; gboolean ret, addr_found; const guint8 *hw_addr; size_t hw_addr_len = 0; GError *error = NULL; - guint i, j; + guint i; g_return_if_fail (NM_IS_DEVICE (self)); g_return_if_fail (configs); @@ -5187,10 +5503,8 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb) data->device = self; for (i = 0; configs[i]; i++) { - for (j = 0; j < nm_ip4_config_get_num_addresses (configs[i]); j++) { - address = nm_ip4_config_get_address (configs[i], j); + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, configs[i], &address) nm_arping_manager_add_address (arping_manager, address->address); - } } g_signal_connect_data (arping_manager, NM_ARPING_MANAGER_PROBE_TERMINATED, @@ -5234,7 +5548,7 @@ ipv4ll_get_ip4_config (NMDevice *self, guint32 lla) NMPlatformIP4Address address; NMPlatformIP4Route route; - config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + config = _ip4_config_new (self); g_assert (config); memset (&address, 0, sizeof (address)); @@ -5247,8 +5561,9 @@ ipv4ll_get_ip4_config (NMDevice *self, guint32 lla) route.network = htonl (0xE0000000L); route.plen = 4; route.rt_source = NM_IP_CONFIG_SOURCE_IP4LL; - route.metric = nm_device_get_ip4_route_metric (self); - nm_ip4_config_add_route (config, &route); + route.table_coerced = nm_platform_route_table_coerce (nm_device_get_route_table (self, AF_INET, TRUE)); + route.metric = nm_device_get_route_metric (self, AF_INET); + nm_ip4_config_add_route (config, &route, NULL); return config; } @@ -5304,7 +5619,9 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) nm_clear_g_source (&priv->ipv4ll_timeout); nm_device_activate_schedule_ip4_config_result (self, config); } else if (priv->ip4_state == IP_DONE) { - if (!ip4_config_merge_and_apply (self, config, TRUE)) { + g_clear_object (&priv->dev_ip4_config); + priv->dev_ip4_config = g_object_ref (config); + if (!ip4_config_merge_and_apply (self, TRUE)) { _LOGE (LOGD_AUTOIP4, "failed to update IP4 config for autoip change."); nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED); } @@ -5399,57 +5716,10 @@ ipv4ll_start (NMDevice *self) /*****************************************************************************/ -static gboolean -_device_get_default_route_from_platform (NMDevice *self, int addr_family, NMPlatformIPRoute *out_route) -{ - gboolean success = FALSE; - int ifindex = nm_device_get_ip_ifindex (self); - GArray *routes; - - if (addr_family == AF_INET) - routes = nm_platform_ip4_route_get_all (nm_device_get_platform (self), ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT); - else - routes = nm_platform_ip6_route_get_all (nm_device_get_platform (self), ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT); - - if (routes) { - guint route_metric = G_MAXUINT32, m; - const NMPlatformIPRoute *route = NULL, *r; - guint i; - - /* if there are several default routes, find the one with the best metric */ - for (i = 0; i < routes->len; i++) { - if (addr_family == AF_INET) { - r = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP4Route, i); - m = r->metric; - } else { - r = (const NMPlatformIPRoute *) &g_array_index (routes, NMPlatformIP6Route, i); - m = nm_utils_ip6_route_metric_normalize (r->metric); - } - if (!route || m < route_metric) { - route = r; - route_metric = m; - } - } - - if (route) { - if (addr_family == AF_INET) - *((NMPlatformIP4Route *) out_route) = *((NMPlatformIP4Route *) route); - else - *((NMPlatformIP6Route *) out_route) = *((NMPlatformIP6Route *) route); - success = TRUE; - } - g_array_free (routes, TRUE); - } - return success; -} - -/*****************************************************************************/ - static void ensure_con_ip4_config (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ip_ifindex = nm_device_get_ip_ifindex (self); NMConnection *connection; if (priv->con_ip4_config) @@ -5459,10 +5729,11 @@ ensure_con_ip4_config (NMDevice *self) if (!connection) return; - priv->con_ip4_config = nm_ip4_config_new (ip_ifindex); + priv->con_ip4_config = _ip4_config_new (self); nm_ip4_config_merge_setting (priv->con_ip4_config, nm_connection_get_setting_ip4_config (connection), - nm_device_get_ip4_route_metric (self)); + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET)); if (nm_device_sys_iface_state_is_external_or_assume (self)) { /* For assumed connections ignore all addresses and routes. */ @@ -5475,7 +5746,6 @@ static void ensure_con_ip6_config (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ip_ifindex = nm_device_get_ip_ifindex (self); NMConnection *connection; if (priv->con_ip6_config) @@ -5485,10 +5755,11 @@ ensure_con_ip6_config (NMDevice *self) if (!connection) return; - priv->con_ip6_config = nm_ip6_config_new (ip_ifindex); + priv->con_ip6_config = _ip6_config_new (self); nm_ip6_config_merge_setting (priv->con_ip6_config, nm_connection_get_setting_ip6_config (connection), - nm_device_get_ip6_route_metric (self)); + nm_device_get_route_table (self, AF_INET6, TRUE), + nm_device_get_route_metric (self, AF_INET6)); if (nm_device_sys_iface_state_is_external_or_assume (self)) { /* For assumed connections ignore all addresses and routes. */ @@ -5527,38 +5798,19 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) } } -static void -_ip4_config_merge_default (gpointer value, gpointer user_data) -{ - NMIP4Config *src = (NMIP4Config *) value; - NMIP4Config *dst = (NMIP4Config *) user_data; - - nm_ip4_config_merge (dst, src, NM_IP_CONFIG_MERGE_DEFAULT); -} - static gboolean ip4_config_merge_and_apply (NMDevice *self, - NMIP4Config *config, gboolean commit) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMConnection *connection; gboolean success; NMIP4Config *composite; - gboolean has_direct_route; - const guint32 default_route_metric = nm_device_get_ip4_route_metric (self); - guint32 gateway; - gboolean connection_has_default_route, connection_is_never_default; - gboolean routes_full_sync; gboolean ignore_auto_routes = FALSE; gboolean ignore_auto_dns = FALSE; - gboolean auto_method = FALSE; - - /* Merge all the configs into the composite config */ - if (config) { - g_clear_object (&priv->dev_ip4_config); - priv->dev_ip4_config = g_object_ref (config); - } + gboolean ignore_default_routes = FALSE; + GSList *iter; + gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL; /* Apply ignore-auto-routes and ignore-auto-dns settings */ connection = nm_device_get_applied_connection (self); @@ -5569,35 +5821,38 @@ ip4_config_merge_and_apply (NMDevice *self, ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes (s_ip4); ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns (s_ip4); - if (nm_streq0 (nm_setting_ip_config_get_method (s_ip4), - NM_SETTING_IP4_CONFIG_METHOD_AUTO)) - auto_method = TRUE; + /* if the connection has an explicit gateway, we also ignore + * the default routes from other sources. */ + ignore_default_routes = nm_setting_ip_config_get_never_default (s_ip4) + || nm_setting_ip_config_get_gateway (s_ip4); } } - composite = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + composite = _ip4_config_new (self); init_ip4_config_dns_priority (self, composite); if (commit) { + if (priv->queued_ip4_config_id) + update_ext_ip_config (self, AF_INET, FALSE, FALSE); ensure_con_ip4_config (self); - if (priv->queued_ip4_config_id) { - g_clear_object (&priv->ext_ip4_config); - priv->ext_ip4_config = nm_ip4_config_capture (nm_device_get_platform (self), - nm_device_get_ip_ifindex (self), - FALSE); - } } + if (commit) + priv->default_route_metric_penalty_ip4_has = default_route_metric_penalty_detect (self); + if (priv->dev_ip4_config) { nm_ip4_config_merge (composite, priv->dev_ip4_config, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); + | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) + | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), + default_route_metric_penalty_get (self, AF_INET)); } - g_slist_foreach (priv->vpn4_configs, _ip4_config_merge_default, composite); + for (iter = priv->vpn4_configs; iter; iter = iter->next) + nm_ip4_config_merge (composite, iter->data, NM_IP_CONFIG_MERGE_DEFAULT, 0); if (priv->ext_ip4_config) - nm_ip4_config_merge (composite, priv->ext_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT); + nm_ip4_config_merge (composite, priv->ext_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT, 0); /* Merge WWAN config *last* to ensure modem-given settings overwrite * any external stuff set by pppd or other scripts. @@ -5605,107 +5860,23 @@ ip4_config_merge_and_apply (NMDevice *self, if (priv->wwan_ip4_config) { nm_ip4_config_merge (composite, priv->wwan_ip4_config, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); + | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) + | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), + default_route_metric_penalty_get (self, AF_INET)); } /* Merge user overrides into the composite config. For assumed connections, * con_ip4_config is empty. */ - if (priv->con_ip4_config) - nm_ip4_config_merge (composite, priv->con_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT); - - /* Add the default route. - * - * We keep track of the default route of a device in a private field. - * NMDevice needs to know the default route at this point, because the gateway - * might require a direct route (see below). - * - * But also, we don't want to add the default route to priv->ip4_config, - * because the default route from the setting might not be the same that - * NMDefaultRouteManager eventually configures (because the it might - * tweak the effective metric). - */ - - /* unless we come to a different conclusion below, we have no default route and - * the route is assumed. */ - priv->default_route.v4_has = FALSE; - priv->default_route.v4_is_assumed = TRUE; - - if (!commit) { - /* during a non-commit event, we always pickup whatever is configured. */ - goto END_ADD_DEFAULT_ROUTE; - } - - /* a generated-assumed connection detects the default route from the platform, - * but if the IP method is automatic we need to update the default route to - * maintain connectivity. - */ - if (nm_device_sys_iface_state_is_external (self) && !auto_method) - goto END_ADD_DEFAULT_ROUTE; - - /* At this point, we treat assumed and non-assumed connections alike. - * For assumed connections we do that because we still manage RA and DHCP - * leases for them, so we must extend/update the default route on commits. - */ - - connection_has_default_route - = nm_default_route_manager_ip4_connection_has_default_route (nm_netns_get_default_route_manager (priv->netns), - connection, &connection_is_never_default); - - if ( !priv->v4_commit_first_time - && connection_is_never_default) { - /* If the connection is explicitly configured as never-default, we enforce the (absence of the) - * default-route only once. That allows the user to configure a connection as never-default, - * but he can add default routes externally (via a dispatcher script) and NM will not interfere. */ - goto END_ADD_DEFAULT_ROUTE; - } - - /* we are about to commit (for a non-assumed connection). Enforce whatever we have - * configured. */ - priv->default_route.v4_is_assumed = FALSE; - - if (!connection_has_default_route) - goto END_ADD_DEFAULT_ROUTE; - - if (!nm_ip4_config_get_num_addresses (composite)) { - /* without addresses we can have no default route. */ - goto END_ADD_DEFAULT_ROUTE; - } - - gateway = nm_ip4_config_get_gateway (composite); - if ( !nm_ip4_config_has_gateway (composite) - && nm_device_get_device_type (self) != NM_DEVICE_TYPE_MODEM) - goto END_ADD_DEFAULT_ROUTE; - - has_direct_route = ( gateway == 0 - || nm_ip4_config_destination_is_direct (composite, gateway, 32) - || nm_ip4_config_get_direct_route_for_host (composite, gateway)); - - priv->default_route.v4_has = TRUE; - memset (&priv->default_route.v4, 0, sizeof (priv->default_route.v4)); - priv->default_route.v4.rt_source = NM_IP_CONFIG_SOURCE_USER; - priv->default_route.v4.gateway = gateway; - priv->default_route.v4.metric = route_metric_with_penalty (self, default_route_metric); - priv->default_route.v4.mss = nm_ip4_config_get_mss (composite); - - if (!has_direct_route) { - NMPlatformIP4Route r = priv->default_route.v4; - - /* add a direct route to the gateway */ - r.network = gateway; - r.plen = 32; - r.gateway = 0; - nm_ip4_config_add_route (composite, &r); + if (priv->con_ip4_config) { + nm_ip4_config_merge (composite, priv->con_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT, + default_route_metric_penalty_get (self, AF_INET)); } -END_ADD_DEFAULT_ROUTE: - - if (priv->default_route.v4_is_assumed) { - /* If above does not explicitly assign a default route, we always pick up the - * default route based on what is currently configured. - * That means that even managed connections with never-default, can - * get a default route (if configured externally). - */ - priv->default_route.v4_has = _device_get_default_route_from_platform (self, AF_INET, (NMPlatformIPRoute *) &priv->default_route.v4); + if (commit) { + nm_ip4_config_add_dependent_routes (composite, + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET), + &ip4_dev_route_blacklist); } if (commit) { @@ -5713,11 +5884,7 @@ END_ADD_DEFAULT_ROUTE: NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, composite); } - routes_full_sync = commit - && priv->v4_commit_first_time - && !nm_device_sys_iface_state_is_external_or_assume (self); - - success = nm_device_set_ip4_config (self, composite, default_route_metric, commit, routes_full_sync); + success = nm_device_set_ip4_config (self, composite, commit, ip4_dev_route_blacklist); g_object_unref (composite); if (commit) @@ -5728,9 +5895,14 @@ END_ADD_DEFAULT_ROUTE: static gboolean dhcp4_lease_change (NMDevice *self, NMIP4Config *config) { + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + g_return_val_if_fail (config, FALSE); - if (!ip4_config_merge_and_apply (self, config, TRUE)) { + g_clear_object (&priv->dev_ip4_config); + priv->dev_ip4_config = g_object_ref (config); + + if (!ip4_config_merge_and_apply (self, TRUE)) { _LOGW (LOGD_DHCP4, "failed to update IPv4 config for DHCP change."); return FALSE; } @@ -5750,15 +5922,13 @@ dhcp4_restart_cb (gpointer user_data) { NMDevice *self = user_data; NMDevicePrivate *priv; - NMConnection *connection; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); priv = NM_DEVICE_GET_PRIVATE (self); priv->dhcp4.restart_id = 0; - connection = nm_device_get_applied_connection (self); - if (dhcp4_start (self, connection) == NM_ACT_STAGE_RETURN_FAILURE) + if (dhcp4_start (self) == NM_ACT_STAGE_RETURN_FAILURE) dhcp_schedule_restart (self, AF_INET, NULL); return FALSE; @@ -5825,7 +5995,7 @@ dhcp4_state_changed (NMDhcpClient *client, NMIP4Config *manual, **configs; NMConnection *connection; - g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == FALSE); + g_return_if_fail (nm_dhcp_client_get_addr_family (client) == AF_INET); g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config)); _LOGD (LOGD_DHCP4, "new DHCPv4 client state %d", state); @@ -5850,10 +6020,11 @@ dhcp4_state_changed (NMDhcpClient *client, connection = nm_device_get_applied_connection (self); g_assert (connection); - manual = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + manual = _ip4_config_new (self); nm_ip4_config_merge_setting (manual, nm_connection_get_setting_ip4_config (connection), - nm_device_get_ip4_route_metric (self)); + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET)); configs = g_new0 (NMIP4Config *, 3); configs[0] = manual; @@ -5885,36 +6056,60 @@ dhcp4_state_changed (NMDhcpClient *client, } static int -dhcp4_get_timeout (NMDevice *self, NMSettingIP4Config *s_ip4) +get_dhcp_timeout (NMDevice *self, int addr_family) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gs_free char *value = NULL; - int timeout; + NMDeviceClass *klass; + NMConnection *connection; + NMSettingIPConfig *s_ip; + guint32 timeout; - timeout = nm_setting_ip_config_get_dhcp_timeout (NM_SETTING_IP_CONFIG (s_ip4)); - if (timeout) - return timeout; + nm_assert (NM_IS_DEVICE (self)); + nm_assert_addr_family (addr_family); - value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - "ipv4.dhcp-timeout", - self); - timeout = _nm_utils_ascii_str_to_int64 (value, 10, - 0, G_MAXINT32, 0); + connection = nm_device_get_applied_connection (self); + + if (addr_family == AF_INET) + s_ip = nm_connection_get_setting_ip4_config (connection); + else + s_ip = nm_connection_get_setting_ip6_config (connection); + + timeout = nm_setting_ip_config_get_dhcp_timeout (s_ip); if (timeout) return timeout; - return priv->dhcp_timeout; + { + gs_free char *value = NULL; + + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + addr_family == AF_INET + ? "ipv4.dhcp-timeout" + : "ipv6.dhcp-timeout", + self); + timeout = _nm_utils_ascii_str_to_int64 (value, 10, + 0, G_MAXINT32, 0); + if (timeout) + return timeout; + } + + klass = NM_DEVICE_GET_CLASS (self); + if (klass->get_dhcp_timeout) + timeout = klass->get_dhcp_timeout (self, addr_family); + + return timeout ?: NM_DHCP_TIMEOUT_DEFAULT; } static NMActStageReturn -dhcp4_start (NMDevice *self, - NMConnection *connection) +dhcp4_start (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMSettingIPConfig *s_ip4; const guint8 *hw_addr; size_t hw_addr_len = 0; GByteArray *tmp = NULL; + NMConnection *connection; + + connection = nm_device_get_applied_connection (self); + g_return_val_if_fail (connection, FALSE); s_ip4 = nm_connection_get_setting_ip4_config (connection); @@ -5931,16 +6126,18 @@ 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 (), + nm_netns_get_multi_idx (nm_device_get_netns (self)), nm_device_get_ip_iface (self), nm_device_get_ip_ifindex (self), tmp, nm_connection_get_uuid (connection), - nm_device_get_ip4_route_metric (self), + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET), nm_setting_ip_config_get_dhcp_send_hostname (s_ip4), nm_setting_ip_config_get_dhcp_hostname (s_ip4), nm_setting_ip4_config_get_dhcp_fqdn (NM_SETTING_IP4_CONFIG (s_ip4)), nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4)), - dhcp4_get_timeout (self, NM_SETTING_IP4_CONFIG (s_ip4)), + get_dhcp_timeout (self, AF_INET), priv->dhcp_anycast_address, NULL); @@ -5968,7 +6165,6 @@ gboolean nm_device_dhcp4_renew (NMDevice *self, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; g_return_val_if_fail (priv->dhcp4.client != NULL, FALSE); @@ -5977,11 +6173,8 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release) /* Terminate old DHCP instance and release the old lease */ dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, release); - connection = nm_device_get_applied_connection (self); - g_return_val_if_fail (connection, FALSE); - /* Start DHCP again on the interface */ - return dhcp4_start (self, connection) != NM_ACT_STAGE_RETURN_FAILURE; + return dhcp4_start (self) != NM_ACT_STAGE_RETURN_FAILURE; } /*****************************************************************************/ @@ -5989,66 +6182,63 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release) static GHashTable *shared_ips = NULL; static void -release_shared_ip (gpointer data) +shared_ip_release (gpointer data) { g_hash_table_remove (shared_ips, data); + if (!g_hash_table_size (shared_ips)) + g_clear_pointer (&shared_ips, g_hash_table_unref); } -static gboolean -reserve_shared_ip (NMDevice *self, NMSettingIPConfig *s_ip4, NMPlatformIP4Address *address) +static NMIP4Config * +shared4_new_config (NMDevice *self, NMConnection *connection) { - if (G_UNLIKELY (shared_ips == NULL)) - shared_ips = g_hash_table_new (g_direct_hash, g_direct_equal); + NMIP4Config *config = NULL; + gboolean is_generated = FALSE; + NMSettingIPConfig *s_ip4; + NMPlatformIP4Address address = { + .addr_source = NM_IP_CONFIG_SOURCE_SHARED, + }; - memset (address, 0, sizeof (*address)); + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (connection, NULL); + s_ip4 = nm_connection_get_setting_ip4_config (connection); if (s_ip4 && nm_setting_ip_config_get_num_addresses (s_ip4)) { /* Use the first user-supplied address */ NMIPAddress *user = nm_setting_ip_config_get_address (s_ip4, 0); in_addr_t a; - g_assert (user); nm_ip_address_get_address_binary (user, &a); - nm_platform_ip4_address_set_addr (address, a, nm_ip_address_get_prefix (user)); + nm_platform_ip4_address_set_addr (&address, a, nm_ip_address_get_prefix (user)); } else { /* Find an unused address in the 10.42.x.x range */ guint32 start = (guint32) ntohl (0x0a2a0001); /* 10.42.0.1 */ guint32 count = 0; - while (g_hash_table_lookup (shared_ips, GUINT_TO_POINTER (start + count))) { - count += ntohl (0x100); - if (count > ntohl (0xFE00)) { - _LOGE (LOGD_SHARING, "ran out of shared IP addresses!"); - return FALSE; + if (G_UNLIKELY (!shared_ips)) + shared_ips = g_hash_table_new (g_direct_hash, g_direct_equal); + else { + while (g_hash_table_lookup (shared_ips, GUINT_TO_POINTER (start + count))) { + count += ntohl (0x100); + if (count > ntohl (0xFE00)) { + _LOGE (LOGD_SHARING, "ran out of shared IP addresses!"); + return FALSE; + } } } - nm_platform_ip4_address_set_addr (address, start + count, 24); - g_hash_table_add (shared_ips, GUINT_TO_POINTER (address->address)); + nm_platform_ip4_address_set_addr (&address, start + count, 24); + g_hash_table_add (shared_ips, GUINT_TO_POINTER (address.address)); + is_generated = TRUE; } - return TRUE; -} - -static NMIP4Config * -shared4_new_config (NMDevice *self, NMConnection *connection) -{ - NMIP4Config *config = NULL; - NMPlatformIP4Address address; - - g_return_val_if_fail (self != NULL, NULL); - - if (!reserve_shared_ip (self, nm_connection_get_setting_ip4_config (connection), &address)) - return NULL; - - config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); - address.addr_source = NM_IP_CONFIG_SOURCE_SHARED; + config = _ip4_config_new (self); nm_ip4_config_add_address (config, &address); - - /* Remove the address lock when the object gets disposed */ - g_object_set_qdata_full (G_OBJECT (config), NM_CACHED_QUARK ("shared-ip"), - GUINT_TO_POINTER (address.address), - release_shared_ip); - + if (is_generated) { + /* Remove the address lock when the object gets disposed */ + g_object_set_qdata_full (G_OBJECT (config), NM_CACHED_QUARK ("shared-ip"), + GUINT_TO_POINTER (address.address), + shared_ip_release); + } return config; } @@ -6092,9 +6282,16 @@ static gboolean connection_requires_carrier (NMConnection *connection) { NMSettingIPConfig *s_ip4, *s_ip6; + NMSettingConnection *s_con; gboolean ip4_carrier_wanted, ip6_carrier_wanted; gboolean ip4_used = FALSE, ip6_used = FALSE; + /* We can progress to IP_CONFIG now, so that we're enslaved. + * That may actually cause carrier to go up and thus continue acivation. */ + s_con = nm_connection_get_setting_connection (connection); + if (nm_setting_connection_get_master (s_con)) + return FALSE; + ip4_carrier_wanted = connection_ip4_method_requires_carrier (connection, &ip4_used); if (ip4_carrier_wanted) { /* If IPv4 wants a carrier and cannot fail, the whole connection @@ -6128,16 +6325,19 @@ connection_requires_carrier (NMConnection *connection) } static gboolean -have_any_ready_slaves (NMDevice *self, const GSList *slaves) +have_any_ready_slaves (NMDevice *self) { - const GSList *iter; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + SlaveInfo *info; + CList *iter; /* Any enslaved slave is "ready" in the generic case as it's * at least >= NM_DEVCIE_STATE_IP_CONFIG and has had Layer 2 * properties set up. */ - for (iter = slaves; iter; iter = g_slist_next (iter)) { - if (nm_device_get_enslaved (iter->data)) + c_list_for_each (iter, &priv->slaves) { + info = c_list_entry (iter, SlaveInfo, lst_slave); + if (NM_DEVICE_GET_PRIVATE (info->slave)->is_enslaved) return TRUE; } return FALSE; @@ -6161,29 +6361,23 @@ act_stage3_ip4_config_start (NMDevice *self, NMConnection *connection; NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; const char *method; - GSList *slaves; - gboolean ready_slaves; connection = nm_device_get_applied_connection (self); g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); if ( connection_ip4_method_requires_carrier (connection, NULL) - && priv->is_master + && nm_device_is_master (self) && !priv->carrier) { _LOGI (LOGD_IP4 | LOGD_DEVICE, "IPv4 config waiting until carrier is on"); return NM_ACT_STAGE_RETURN_IP_WAIT; } - if (priv->is_master && ip4_requires_slaves (connection)) { + if (nm_device_is_master (self) && ip4_requires_slaves (connection)) { /* If the master has no ready slaves, and depends on slaves for * a successful IPv4 attempt, then postpone IPv4 addressing. */ - slaves = nm_device_master_get_slaves (self); - ready_slaves = NM_DEVICE_GET_CLASS (self)->have_any_ready_slaves (self, slaves); - g_slist_free (slaves); - - if (ready_slaves == FALSE) { + if (!have_any_ready_slaves (self)) { _LOGI (LOGD_DEVICE | LOGD_IP4, "IPv4 config waiting until slaves are ready"); return NM_ACT_STAGE_RETURN_IP_WAIT; @@ -6195,7 +6389,7 @@ act_stage3_ip4_config_start (NMDevice *self, /* Start IPv4 addressing based on the method requested */ if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) { - ret = dhcp4_start (self, connection); + ret = dhcp4_start (self); if (ret == NM_ACT_STAGE_RETURN_FAILURE) NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_DHCP_START_FAILED); } else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) == 0) { @@ -6205,10 +6399,11 @@ act_stage3_ip4_config_start (NMDevice *self, } else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0) { NMIP4Config **configs, *config; - config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + config = _ip4_config_new (self); nm_ip4_config_merge_setting (config, nm_connection_get_setting_ip4_config (connection), - nm_device_get_ip4_route_metric (self)); + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET)); configs = g_new0 (NMIP4Config *, 2); configs[0] = config; @@ -6266,15 +6461,6 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) } } -static void -_ip6_config_merge_default (gpointer value, gpointer user_data) -{ - NMIP6Config *src = (NMIP6Config *) value; - NMIP6Config *dst = (NMIP6Config *) user_data; - - nm_ip6_config_merge (dst, src, NM_IP_CONFIG_MERGE_DEFAULT); -} - static gboolean ip6_config_merge_and_apply (NMDevice *self, gboolean commit) @@ -6283,14 +6469,11 @@ ip6_config_merge_and_apply (NMDevice *self, NMConnection *connection; gboolean success; NMIP6Config *composite; - gboolean has_direct_route; - const struct in6_addr *gateway; - gboolean connection_has_default_route, connection_is_never_default; - gboolean routes_full_sync; gboolean ignore_auto_routes = FALSE; gboolean ignore_auto_dns = FALSE; - gboolean auto_method = FALSE; + gboolean ignore_default_routes = FALSE; const char *token = NULL; + GSList *iter; /* Apply ignore-auto-routes and ignore-auto-dns settings */ connection = nm_device_get_applied_connection (self); @@ -6303,17 +6486,17 @@ ip6_config_merge_and_apply (NMDevice *self, 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 the connection has an explicit gateway, we also ignore + * the default routes from other sources. */ + ignore_default_routes = nm_setting_ip_config_get_never_default (s_ip6) + || nm_setting_ip_config_get_gateway (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)) - auto_method = TRUE; } } - composite = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + composite = _ip6_config_new (self); nm_ip6_config_set_privacy (composite, priv->ndisc ? priv->ndisc_use_tempaddr : @@ -6321,35 +6504,35 @@ ip6_config_merge_and_apply (NMDevice *self, init_ip6_config_dns_priority (self, composite); if (commit) { + if (priv->queued_ip6_config_id) + update_ext_ip_config (self, AF_INET6, FALSE, FALSE); ensure_con_ip6_config (self); - if (priv->queued_ip6_config_id) { - g_clear_object (&priv->ext_ip6_config); - g_clear_object (&priv->ext_ip6_config_captured); - priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_platform (self), - nm_device_get_ip_ifindex (self), - FALSE, - NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - if (priv->ext_ip6_config_captured) - priv->ext_ip6_config = nm_ip6_config_new_cloned (priv->ext_ip6_config_captured); - } } + if (commit) + priv->default_route_metric_penalty_ip6_has = default_route_metric_penalty_detect (self); + /* Merge all the IP configs into the composite config */ if (priv->ac_ip6_config) { nm_ip6_config_merge (composite, priv->ac_ip6_config, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); + | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) + | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), + default_route_metric_penalty_get (self, AF_INET6)); } 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)); + | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) + | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), + default_route_metric_penalty_get (self, AF_INET6)); } - g_slist_foreach (priv->vpn6_configs, _ip6_config_merge_default, composite); + for (iter = priv->vpn6_configs; iter; iter = iter->next) + nm_ip6_config_merge (composite, iter->data, NM_IP_CONFIG_MERGE_DEFAULT, 0); if (priv->ext_ip6_config) - nm_ip6_config_merge (composite, priv->ext_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT); + nm_ip6_config_merge (composite, priv->ext_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT, 0); /* Merge WWAN config *last* to ensure modem-given settings overwrite * any external stuff set by pppd or other scripts. @@ -6357,108 +6540,34 @@ ip6_config_merge_and_apply (NMDevice *self, if (priv->wwan_ip6_config) { nm_ip6_config_merge (composite, priv->wwan_ip6_config, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) - | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0)); - } - - /* Merge user overrides into the composite config. For assumed connections, - * con_ip6_config is empty. */ - if (priv->con_ip6_config) - nm_ip6_config_merge (composite, priv->con_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT); - - /* Add the default route. - * - * We keep track of the default route of a device in a private field. - * NMDevice needs to know the default route at this point, because the gateway - * might require a direct route (see below). - * - * But also, we don't want to add the default route to priv->ip6_config, - * because the default route from the setting might not be the same that - * NMDefaultRouteManager eventually configures (because the it might - * tweak the effective metric). - */ - - /* unless we come to a different conclusion below, we have no default route and - * the route is assumed. */ - priv->default_route.v6_has = FALSE; - priv->default_route.v6_is_assumed = TRUE; - - if (!commit) { - /* during a non-commit event, we always pickup whatever is configured. */ - goto END_ADD_DEFAULT_ROUTE; + | (ignore_default_routes ? NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES : 0) + | (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0), + default_route_metric_penalty_get (self, AF_INET6)); } - /* a generated-assumed connection detects the default route from the platform, - * but if the IP method is automatic we need to update the default route to - * maintain connectivity. - */ - if (nm_device_sys_iface_state_is_external (self) && !auto_method) - goto END_ADD_DEFAULT_ROUTE; - - /* At this point, we treat assumed and non-assumed connections alike. - * For assumed connections we do that because we still manage RA and DHCP - * leases for them, so we must extend/update the default route on commits. - */ - - connection_has_default_route - = nm_default_route_manager_ip6_connection_has_default_route (nm_netns_get_default_route_manager (priv->netns), - connection, &connection_is_never_default); - - if ( !priv->v6_commit_first_time - && connection_is_never_default) { - /* If the connection is explicitly configured as never-default, we enforce the (absence of the) - * default-route only once. That allows the user to configure a connection as never-default, - * but he can add default routes externally (via a dispatcher script) and NM will not interfere. */ - goto END_ADD_DEFAULT_ROUTE; - } + if (priv->rt6_temporary_not_available) { + const NMPObject *o; + GHashTableIter hiter; - /* we are about to commit (for a non-assumed connection). Enforce whatever we have - * configured. */ - priv->default_route.v6_is_assumed = FALSE; - - if (!connection_has_default_route) - goto END_ADD_DEFAULT_ROUTE; - - if (!nm_ip6_config_get_num_addresses (composite)) { - /* without addresses we can have no default route. */ - goto END_ADD_DEFAULT_ROUTE; + g_hash_table_iter_init (&hiter, priv->rt6_temporary_not_available); + while (g_hash_table_iter_next (&hiter, (gpointer *) &o, NULL)) { + nm_ip6_config_add_route (composite, + NMP_OBJECT_CAST_IP6_ROUTE (o), + NULL); + } } - gateway = nm_ip6_config_get_gateway (composite); - if (!gateway) - goto END_ADD_DEFAULT_ROUTE; - - - has_direct_route = nm_ip6_config_get_direct_route_for_host (composite, gateway) != NULL; - - - - priv->default_route.v6_has = TRUE; - memset (&priv->default_route.v6, 0, sizeof (priv->default_route.v6)); - priv->default_route.v6.rt_source = NM_IP_CONFIG_SOURCE_USER; - priv->default_route.v6.gateway = *gateway; - priv->default_route.v6.metric = route_metric_with_penalty (self, - nm_device_get_ip6_route_metric (self)); - priv->default_route.v6.mss = nm_ip6_config_get_mss (composite); - - if (!has_direct_route) { - NMPlatformIP6Route r = priv->default_route.v6; - - /* add a direct route to the gateway */ - r.network = *gateway; - r.plen = 128; - r.gateway = in6addr_any; - nm_ip6_config_add_route (composite, &r); + /* Merge user overrides into the composite config. For assumed connections, + * con_ip6_config is empty. */ + if (priv->con_ip6_config) { + nm_ip6_config_merge (composite, priv->con_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT, + default_route_metric_penalty_get (self, AF_INET6)); } -END_ADD_DEFAULT_ROUTE: - - if (priv->default_route.v6_is_assumed) { - /* If above does not explicitly assign a default route, we always pick up the - * default route based on what is currently configured. - * That means that even managed connections with never-default, can - * get a default route (if configured externally). - */ - priv->default_route.v6_has = _device_get_default_route_from_platform (self, AF_INET6, (NMPlatformIPRoute *) &priv->default_route.v6); + if (commit) { + nm_ip6_config_add_dependent_routes (composite, + nm_device_get_route_table (self, AF_INET6, TRUE), + nm_device_get_route_metric (self, AF_INET6)); } /* Allow setting MTU etc */ @@ -6472,11 +6581,7 @@ END_ADD_DEFAULT_ROUTE: } } - routes_full_sync = commit - && priv->v6_commit_first_time - && !nm_device_sys_iface_state_is_external_or_assume (self); - - success = nm_device_set_ip6_config (self, composite, commit, routes_full_sync); + success = nm_device_set_ip6_config (self, composite, commit); g_object_unref (composite); if (commit) priv->v6_commit_first_time = FALSE; @@ -6533,28 +6638,30 @@ dhcp6_restart_cb (gpointer user_data) } static void -dhcp_schedule_restart (NMDevice *self, int family, const char *reason) +dhcp_schedule_restart (NMDevice *self, + int addr_family, + const char *reason) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean inet4; guint tries_left; - gs_free char *tries_str = NULL; + char tries_str[255]; - g_return_if_fail (family == AF_INET || family == AF_INET6); - inet4 = family == AF_INET; + nm_assert_addr_family (addr_family); - 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); + tries_left = (addr_family == AF_INET) + ? priv->dhcp4.num_tries_left + : priv->dhcp6.num_tries_left; - _LOGI (inet4 ? LOGD_DHCP4 : LOGD_DHCP6, + _LOGI ((addr_family == AF_INET) ? LOGD_DHCP4 : LOGD_DHCP6, "scheduling DHCPv%c restart in %u seconds%s%s%s%s", - inet4 ? '4' : '6', + nm_utils_addr_family_to_char (addr_family), DHCP_RESTART_TIMEOUT, - tries_str ? tries_str : "", + (tries_left != DHCP_NUM_TRIES_MAX) + ? nm_sprintf_buf (tries_str, ", %u tries left", tries_left + 1) + : "", NM_PRINT_FMT_QUOTED (reason, " (reason: ", reason, ")", "")); - if (inet4) { + if (addr_family == AF_INET) { priv->dhcp4.restart_id = g_timeout_add_seconds (DHCP_RESTART_TIMEOUT, dhcp4_restart_cb, self); } else { @@ -6631,9 +6738,8 @@ dhcp6_state_changed (NMDhcpClient *client, { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - guint i; - g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE); + g_return_if_fail (nm_dhcp_client_get_addr_family (client) == AF_INET6); g_return_if_fail (!ip6_config || NM_IS_IP6_CONFIG (ip6_config)); _LOGD (LOGD_DHCP6, "new DHCPv6 client state %d", state); @@ -6648,10 +6754,11 @@ dhcp6_state_changed (NMDhcpClient *client, && 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_get_address (ip6_config, i)); - } + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Address *a; + + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ip6_config, &a) + nm_ip6_config_add_address (priv->dhcp6.ip6_config, a); } else { g_clear_object (&priv->dhcp6.ip6_config); g_clear_pointer (&priv->dhcp6.event_id, g_free); @@ -6741,15 +6848,17 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) } priv->dhcp6.client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (), + nm_device_get_multi_index (self), nm_device_get_ip_iface (self), nm_device_get_ip_ifindex (self), tmp, &ll_addr->address, nm_connection_get_uuid (connection), - nm_device_get_ip6_route_metric (self), + nm_device_get_route_table (self, AF_INET6, TRUE), + nm_device_get_route_metric (self, AF_INET6), nm_setting_ip_config_get_dhcp_send_hostname (s_ip6), nm_setting_ip_config_get_dhcp_hostname (s_ip6), - priv->dhcp_timeout, + get_dhcp_timeout (self, AF_INET6), priv->dhcp_anycast_address, (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE, nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6)), @@ -6769,7 +6878,7 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection) } if (nm_device_sys_iface_state_is_external_or_assume (self)) - priv->dhcp4.was_active = TRUE; + priv->dhcp6.was_active = TRUE; return !!priv->dhcp6.client; } @@ -6869,7 +6978,7 @@ nm_device_use_ip6_subnet (NMDevice *self, const NMPlatformIP6Address *subnet) NMPlatformIP6Address address = *subnet; if (!priv->ac_ip6_config) - priv->ac_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + priv->ac_ip6_config = _ip6_config_new (self); /* Assign a ::1 address in the subnet for us. */ address.address.s6_addr32[3] |= htonl (1); @@ -6899,7 +7008,7 @@ nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device) nm_ip6_config_reset_nameservers (priv->ac_ip6_config); nm_ip6_config_reset_searches (priv->ac_ip6_config); } else - priv->ac_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + priv->ac_ip6_config = _ip6_config_new (self); if (from_device) from_config = nm_device_get_ip6_config (from_device); @@ -6990,7 +7099,6 @@ check_and_add_ipv6ll_addr (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int ip_ifindex = nm_device_get_ip_ifindex (self); struct in6_addr lladdr; - guint i, n; NMConnection *connection; NMSettingIP6Config *s_ip6 = NULL; GError *error = NULL; @@ -6999,11 +7107,10 @@ check_and_add_ipv6ll_addr (NMDevice *self) return; if (priv->ip6_config) { - n = nm_ip6_config_get_num_addresses (priv->ip6_config); - for (i = 0; i < n; i++) { - const NMPlatformIP6Address *addr; + NMDedupMultiIter ipconf_iter; + const NMPlatformIP6Address *addr; - addr = nm_ip6_config_get_address (priv->ip6_config, i); + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, priv->ip6_config, &addr) { if ( IN6_IS_ADDR_LINKLOCAL (&addr->address) && !(addr->n_ifa_flags & IFA_F_DADFAILED)) { /* Already have an LL address, nothing to do */ @@ -7152,6 +7259,26 @@ nm_device_get_configured_mtu_for_wired (NMDevice *self, gboolean *out_is_user_co /*****************************************************************************/ static void +_set_mtu (NMDevice *self, guint32 mtu) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->mtu == mtu) + return; + + priv->mtu = mtu; + _notify (self, PROP_MTU); + + if (priv->master) { + /* changing the MTU of a slave, might require the master to reset + * it's MTU. Note that the master usually cannot set a MTU larger + * then the slave's. Hence, when the slave increases the MTU, + * master might want to retry setting the MTU. */ + nm_device_commit_mtu (priv->master); + } +} + +static void _commit_mtu (NMDevice *self, const NMIP4Config *config) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -7170,8 +7297,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) return; if (nm_device_sys_iface_state_is_external_or_assume (self)) { - /* for assumed connections we don't tamper with the MTU. This is - * a bug and supposed to be fixed by the unmanaged/assumed rework. */ + /* for assumed connections we don't tamper with the MTU. */ return; } @@ -7271,6 +7397,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) }) if ( (mtu_desired && mtu_desired != mtu_plat) || (ip6_mtu && ip6_mtu != _IP6_MTU_SYS ())) { + gboolean anticipated_failure = FALSE; if (!priv->mtu_initial && !priv->ip6_mtu_initial) { /* before touching any of the MTU paramters, record the @@ -7279,100 +7406,102 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) priv->ip6_mtu_initial = _IP6_MTU_SYS (); } - if (mtu_desired && mtu_desired != mtu_plat) - nm_platform_link_set_mtu (nm_device_get_platform (self), ifindex, mtu_desired); + if (mtu_desired && mtu_desired != mtu_plat) { + if (nm_platform_link_set_mtu (nm_device_get_platform (self), ifindex, mtu_desired) == NM_PLATFORM_ERROR_CANT_SET_MTU) { + anticipated_failure = TRUE; + _LOGW (LOGD_DEVICE, "mtu: failure to set MTU. %s", + NM_IS_DEVICE_VLAN (self) + ? "Is the parent's MTU size large enough?" + : (!c_list_is_empty (&priv->slaves) + ? "Are the MTU sizes of the slaves large enough?" + : "Did you configure the MTU correctly?")); + } + priv->carrier_wait_until_ms = nm_utils_get_monotonic_timestamp_ms () + CARRIER_WAIT_TIME_AFTER_MTU_MS; + } if (ip6_mtu && ip6_mtu != _IP6_MTU_SYS ()) { - nm_device_ipv6_sysctl_set (self, "mtu", - nm_sprintf_buf (sbuf, "%u", (unsigned) ip6_mtu)); + if (!nm_device_ipv6_sysctl_set (self, "mtu", + nm_sprintf_buf (sbuf, "%u", (unsigned) ip6_mtu))) { + int errsv = errno; + + _NMLOG (anticipated_failure && errsv == EINVAL ? LOGL_DEBUG : LOGL_WARN, + LOGD_DEVICE, + "mtu: failure to set IPv6 MTU%s", + anticipated_failure && errsv == EINVAL + ? ": Is the underlying MTU value successfully set?" + : ""); + } + priv->carrier_wait_until_ms = nm_utils_get_monotonic_timestamp_ms () + CARRIER_WAIT_TIME_AFTER_MTU_MS; } } #undef _IP6_MTU_SYS } +void +nm_device_commit_mtu (NMDevice *self) +{ + NMDeviceState state; + + g_return_if_fail (NM_IS_DEVICE (self)); + + state = nm_device_get_state (self); + if ( state >= NM_DEVICE_STATE_CONFIG + && state < NM_DEVICE_STATE_DEACTIVATING) { + _LOGT (LOGD_DEVICE, "mtu: commit-mtu..."); + _commit_mtu (self, NM_DEVICE_GET_PRIVATE (self)->ip4_config); + } else + _LOGT (LOGD_DEVICE, "mtu: commit-mtu... skip due to state %s", nm_device_state_to_str (state)); +} + static void ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_int, NMDevice *self) { NMNDiscConfigMap changed = changed_int; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int i; - int system_support; - guint32 ifa_flags = 0x00; - - /* - * Check, whether kernel is recent enough to help user space handling RA. - * If it's not supported, we have no ipv6-privacy and must add autoconf - * addresses as /128. The reason for the /128 is to prevent the kernel - * from adding a prefix route for this address. - **/ - system_support = nm_platform_check_support_kernel_extended_ifa_flags (nm_device_get_platform (self)); - - if (system_support) - ifa_flags = IFA_F_NOPREFIXROUTE; - if ( priv->ndisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR - || priv->ndisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) - { - /* without system_support, this flag will be ignored. Still set it, doesn't seem to do any harm. */ - ifa_flags |= IFA_F_MANAGETEMPADDR; - } + guint i; g_return_if_fail (priv->act_request); if (!priv->ac_ip6_config) - priv->ac_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); - - if (changed & NM_NDISC_CONFIG_GATEWAYS) { - /* Use the first gateway as ordered in neighbor discovery cache. */ - 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); - } + priv->ac_ip6_config = _ip6_config_new (self); if (changed & NM_NDISC_CONFIG_ADDRESSES) { - /* Rebuild address list from neighbor discovery cache. */ - nm_ip6_config_reset_addresses (priv->ac_ip6_config); - - /* ndisc->addresses contains at most max_addresses entries. - * This is different from what the kernel does, which - * also counts static and temporary addresses when checking - * max_addresses. - **/ - for (i = 0; i < rdata->addresses_n; i++) { - const NMNDiscAddress *discovered_address = &rdata->addresses[i]; - NMPlatformIP6Address address; - - memset (&address, 0, sizeof (address)); - address.address = discovered_address->address; - address.plen = system_support ? 64 : 128; - address.timestamp = discovered_address->timestamp; - address.lifetime = discovered_address->lifetime; - address.preferred = discovered_address->preferred; - if (address.preferred > address.lifetime) - address.preferred = address.lifetime; - address.addr_source = NM_IP_CONFIG_SOURCE_NDISC; - address.n_ifa_flags = ifa_flags; + guint8 plen; + guint32 ifa_flags; + + /* Check, whether kernel is recent enough to help user space handling RA. + * If it's not supported, we have no ipv6-privacy and must add autoconf + * addresses as /128. The reason for the /128 is to prevent the kernel + * from adding a prefix route for this address. */ + ifa_flags = 0; + if (nm_platform_check_kernel_support (nm_device_get_platform (self), + NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS)) { + ifa_flags |= IFA_F_NOPREFIXROUTE; + if (NM_IN_SET (priv->ndisc_use_tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)) + ifa_flags |= IFA_F_MANAGETEMPADDR; + plen = 64; + } else + plen = 128; - nm_ip6_config_add_address (priv->ac_ip6_config, &address); - } + nm_ip6_config_reset_addresses_ndisc (priv->ac_ip6_config, + rdata->addresses, + rdata->addresses_n, + plen, + ifa_flags); } - if (changed & NM_NDISC_CONFIG_ROUTES) { - /* Rebuild route list from neighbor discovery cache. */ - nm_ip6_config_reset_routes (priv->ac_ip6_config); - - for (i = 0; i < rdata->routes_n; i++) { - const NMNDiscRoute *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_NDISC, - .metric = nm_device_get_ip6_route_metric (self), - }; - - nm_ip6_config_add_route (priv->ac_ip6_config, &route); - } + if (NM_FLAGS_ANY (changed, NM_NDISC_CONFIG_ROUTES + | NM_NDISC_CONFIG_GATEWAYS)) { + nm_ip6_config_reset_routes_ndisc (priv->ac_ip6_config, + rdata->gateways, + rdata->gateways_n, + rdata->routes, + rdata->routes_n, + nm_device_get_route_table (self, AF_INET6, TRUE), + nm_device_get_route_metric (self, AF_INET6), + nm_platform_check_kernel_support (nm_device_get_platform (self), + NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF)); } if (changed & NM_NDISC_CONFIG_DNS_SERVERS) { @@ -7465,8 +7594,10 @@ addrconf6_start_with_link_ready (NMDevice *self) } /* Apply any manual configuration before starting RA */ - if (!ip6_config_merge_and_apply (self, TRUE)) + if (!ip6_config_merge_and_apply (self, TRUE)) { _LOGW (LOGD_IP6, "failed to apply manual IPv6 configuration"); + g_clear_object (&priv->con_ip6_config); + } /* XXX: These sysctls would probably be better set by the lndp ndisc itself. */ switch (nm_ndisc_get_node_type (priv->ndisc)) { @@ -7537,6 +7668,9 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) priv->ac_ip6_config = NULL; } + g_clear_pointer (&priv->rt6_temporary_not_available, g_hash_table_unref); + nm_clear_g_source (&priv->rt6_temporary_not_available_id); + s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection)); g_assert (s_ip6); @@ -7559,7 +7693,8 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) priv->ndisc_use_tempaddr = use_tempaddr; if ( NM_IN_SET (use_tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) - && !nm_platform_check_support_kernel_extended_ifa_flags (nm_device_get_platform (self))) { + && !nm_platform_check_kernel_support (nm_device_get_platform (self), + NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS)) { _LOGW (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for " "IPv6 private addresses. This feature is not available"); } @@ -7590,6 +7725,8 @@ addrconf6_cleanup (NMDevice *self) nm_device_remove_pending_action (self, NM_PENDING_ACTION_AUTOCONF6, FALSE); g_clear_object (&priv->ac_ip6_config); + g_clear_pointer (&priv->rt6_temporary_not_available, g_hash_table_unref); + nm_clear_g_source (&priv->rt6_temporary_not_available_id); g_clear_object (&priv->ndisc); } @@ -7616,8 +7753,13 @@ save_ip6_properties (NMDevice *self) g_hash_table_remove_all (priv->ip6_saved_properties); + if (!nm_device_get_ip_ifindex (self)) + return; + for (i = 0; i < G_N_ELEMENTS (ip6_properties_to_save); i++) { - value = nm_platform_sysctl_get (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (ifname, ip6_properties_to_save[i]))); + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + + value = nm_platform_sysctl_get (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, ifname, ip6_properties_to_save[i]))); if (value) { g_hash_table_insert (priv->ip6_saved_properties, (char *) ip6_properties_to_save[i], @@ -7657,7 +7799,8 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) int ifindex = nm_device_get_ip_ifindex (self); char *value; - if (!nm_platform_check_support_user_ipv6ll (nm_device_get_platform (self))) + if (!nm_platform_check_kernel_support (nm_device_get_platform (self), + NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) return; priv->nm_ipv6ll = enable; @@ -7672,13 +7815,15 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) LOGD_IP6, "failed to %s userspace IPv6LL address handling (%s)", detail, - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); } if (enable) { + char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */ value = nm_platform_sysctl_get (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), "disable_ipv6"))); + NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, nm_device_get_ip_iface (self), "disable_ipv6"))); if (g_strcmp0 (value, "0") == 0) nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); g_free (value); @@ -7739,6 +7884,9 @@ _ip6_privacy_get (NMDevice *self) if (ip6_privacy != NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN) return ip6_privacy; + if (!nm_device_get_ip_ifindex (self)) + return NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;; + /* 3.) No valid default-value configured. Fallback to reading sysctl. * * Instead of reading static config files in /etc, just read the current sysctl value. @@ -7775,29 +7923,23 @@ act_stage3_ip6_config_start (NMDevice *self, const char *method; NMSettingIP6ConfigPrivacy ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; const char *ip6_privacy_str = "0"; - GSList *slaves; - gboolean ready_slaves; connection = nm_device_get_applied_connection (self); g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); if ( connection_ip6_method_requires_carrier (connection, NULL) - && priv->is_master + && nm_device_is_master (self) && !priv->carrier) { _LOGI (LOGD_IP6 | LOGD_DEVICE, "IPv6 config waiting until carrier is on"); return NM_ACT_STAGE_RETURN_IP_WAIT; } - if (priv->is_master && ip6_requires_slaves (connection)) { + if (nm_device_is_master (self) && ip6_requires_slaves (connection)) { /* If the master has no ready slaves, and depends on slaves for * a successful IPv6 attempt, then postpone IPv6 addressing. */ - slaves = nm_device_master_get_slaves (self); - ready_slaves = NM_DEVICE_GET_CLASS (self)->have_any_ready_slaves (self, slaves); - g_slist_free (slaves); - - if (ready_slaves == FALSE) { + if (!have_any_ready_slaves (self)) { _LOGI (LOGD_DEVICE | LOGD_IP6, "IPv6 config waiting until slaves are ready"); return NM_ACT_STAGE_RETURN_IP_WAIT; @@ -7810,7 +7952,8 @@ act_stage3_ip6_config_start (NMDevice *self, method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) { - if (!priv->master) { + if ( !priv->master + && !nm_device_sys_iface_state_is_external (self)) { gboolean old_nm_ipv6ll = priv->nm_ipv6ll; /* When activating an IPv6 'ignore' connection we need to revert back @@ -7847,7 +7990,8 @@ act_stage3_ip6_config_start (NMDevice *self, */ nm_platform_process_events (nm_device_get_platform (self)); g_clear_object (&priv->ext_ip6_config_captured); - priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_platform (self), + priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_multi_index (self), + nm_device_get_platform (self), nm_device_get_ip_ifindex (self), FALSE, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); @@ -7911,17 +8055,11 @@ nm_device_activate_stage3_ip4_start (NMDevice *self) g_assert (priv->ip4_state == IP_WAIT); - /* Slaves stay in IP_CONFIG state until master is ready, and then - * they go directly to SECONDARIES without configuring IPv4. - */ - if (nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request))) - return TRUE; - _set_ip_state (self, AF_INET, IP_CONF); ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip4_config_start (self, &ip4_config, &failure_reason); if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { if (!ip4_config) - ip4_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + ip4_config = _ip4_config_new (self); nm_device_activate_schedule_ip4_config_result (self, ip4_config); g_object_unref (ip4_config); } else if (ret == NM_ACT_STAGE_RETURN_IP_DONE) { @@ -7958,17 +8096,11 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) g_assert (priv->ip6_state == IP_WAIT); - /* Slaves stay in IP_CONFIG state until master is ready, and then - * they go directly to SECONDARIES without configuring IPv6. - */ - if (nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request))) - return TRUE; - _set_ip_state (self, AF_INET6, IP_CONF); ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &ip6_config, &failure_reason); if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { if (!ip6_config) - ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + ip6_config = _ip6_config_new (self); /* Here we get a static IPv6 config, like for Shared where it's * autogenerated or from modems where it comes from ModemManager. */ @@ -8002,39 +8134,18 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) static void activate_stage3_ip_config_start (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActiveConnection *master; - NMDevice *master_device; - _set_ip_state (self, AF_INET, IP_WAIT); _set_ip_state (self, AF_INET6, IP_WAIT); + _active_connection_set_state_flags (self, + NM_ACTIVATION_STATE_FLAG_LAYER2_READY); + nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); /* Device should be up before we can do anything with it */ if (!nm_platform_link_is_up (nm_device_get_platform (self), nm_device_get_ip_ifindex (self))) _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); - /* If the device is a slave, then we don't do any IP configuration but we - * use the IP config stage to indicate to the master we're ready for - * enslavement. If the master is already activating, it will have tried to - * enslave us when we changed state to IP_CONFIG, causing us to queue a - * transition to SECONDARIES (or FAILED if the enslavement failed), with - * our IP states set to IP_DONE either way. If the master isn't yet - * activating, then they'll still be in IP_WAIT. Either way, we bail out - * of IP config here. - */ - master = nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request)); - if (master) { - master_device = nm_active_connection_get_device (master); - if (priv->ip4_state == IP_WAIT && priv->ip6_state == IP_WAIT) { - _LOGI (LOGD_DEVICE, "Activation: connection '%s' waiting on master '%s'", - nm_connection_get_id (nm_device_get_applied_connection (self)), - master_device ? nm_device_get_iface (master_device) : "(unknown)"); - } - return; - } - /* IPv4 */ if ( nm_device_activate_ip4_state_in_wait (self) && !nm_device_activate_stage3_ip4_start (self)) @@ -8309,18 +8420,20 @@ start_sharing (NMDevice *self, NMIP4Config *config) char str_addr[INET_ADDRSTRLEN + 1]; char str_mask[INET_ADDRSTRLEN + 1]; guint32 netmask, network; - const NMPlatformIP4Address *ip4_addr; + const NMPlatformIP4Address *ip4_addr = NULL; const char *ip_iface; g_return_val_if_fail (config != NULL, FALSE); ip_iface = nm_device_get_ip_iface (self); + if (!ip_iface) + return FALSE; - ip4_addr = nm_ip4_config_get_address (config, 0); + ip4_addr = nm_ip4_config_get_first_address (config); if (!ip4_addr || !ip4_addr->address) return FALSE; - netmask = nm_utils_ip4_prefix_to_netmask (ip4_addr->plen); + netmask = _nm_utils_ip4_prefix_to_netmask (ip4_addr->plen); if (!inet_ntop (AF_INET, &netmask, str_mask, sizeof (str_mask))) return FALSE; @@ -8420,7 +8533,7 @@ arp_announce (NMDevice *self) } static void -activate_stage5_ip4_config_commit (NMDevice *self) +activate_stage5_ip4_config_result (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActRequest *req; @@ -8442,7 +8555,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)) { + if (!ip4_config_merge_and_apply (self, TRUE)) { _LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Commit) failed"); nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_CONFIG_FAILED); return; @@ -8492,7 +8605,7 @@ nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *conf if (config) priv->dev_ip4_config = g_object_ref (config); - activation_source_schedule (self, activate_stage5_ip4_config_commit, AF_INET); + activation_source_schedule (self, activate_stage5_ip4_config_result, AF_INET); } gboolean @@ -8530,7 +8643,8 @@ dad6_get_pending_addresses (NMDevice *self) priv->wwan_ip6_config }; const NMPlatformIP6Address *addr, *pl_addr; NMIP6Config *dad6_config = NULL; - guint i, j, num; + NMDedupMultiIter ipconf_iter; + guint i; int ifindex; ifindex = nm_device_get_ip_ifindex (self); @@ -8541,13 +8655,11 @@ dad6_get_pending_addresses (NMDevice *self) */ 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); + + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, confs[i], &addr) { pl_addr = nm_platform_ip6_address_get (nm_device_get_platform (self), ifindex, - addr->address, - addr->plen); + addr->address); 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) @@ -8556,7 +8668,7 @@ dad6_get_pending_addresses (NMDevice *self) nm_platform_ip6_address_to_string (pl_addr, NULL, 0)); if (!dad6_config) - dad6_config = nm_ip6_config_new (ifindex); + dad6_config = _ip6_config_new (self); nm_ip6_config_add_address (dad6_config, pl_addr); } @@ -8584,6 +8696,8 @@ activate_stage5_ip6_config_commit (NMDevice *self) /* Interface must be IFF_UP before IP config can be applied */ ip_ifindex = nm_device_get_ip_ifindex (self); + g_return_if_fail (ip_ifindex); + if (!nm_platform_link_is_up (nm_device_get_platform (self), ip_ifindex) && !nm_device_sys_iface_state_is_external_or_assume (self)) { nm_platform_link_set_up (nm_device_get_platform (self), ip_ifindex, NULL); if (!nm_platform_link_is_up (nm_device_get_platform (self), ip_ifindex)) @@ -8756,16 +8870,15 @@ static void _update_ip4_address (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - guint32 addr; + const NMPlatformIP4Address *address; g_return_if_fail (NM_IS_DEVICE (self)); if ( priv->ip4_config && ip_config_valid (priv->state) - && nm_ip4_config_get_num_addresses (priv->ip4_config)) { - addr = nm_ip4_config_get_address (priv->ip4_config, 0)->address; - if (addr != priv->ip4_address) { - priv->ip4_address = addr; + && (address = nm_ip4_config_get_first_address (priv->ip4_config))) { + if (address->address != priv->ip4_address) { + priv->ip4_address = address->address; _notify (self, PROP_IP4_ADDRESS); } } @@ -8802,7 +8915,7 @@ delete_on_deactivate_link_delete (gpointer user_data) if (!nm_device_unrealize (data->device, TRUE, &error)) _LOGD (LOGD_DEVICE, "delete_on_deactivate: unrealizing %d failed (%s)", data->ifindex, error->message); - } else + } else if (data->ifindex > 0) nm_platform_link_delete (nm_device_get_platform (self), data->ifindex); g_free (data); @@ -8833,8 +8946,6 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); DeleteOnDeactivateData *data; - if (ifindex <= 0) - return; if (!priv->nm_owned) return; if (priv->queued_act_request) @@ -8905,7 +9016,7 @@ _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, #if NM_MORE_ASSERTS > 10 /* Assert that the keys are unique. */ { - gs_unref_hashtable GHashTable *check_dups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + gs_unref_hashtable GHashTable *check_dups = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL); for (i = 0; argv[i]; i++) { if (!nm_g_hash_table_add (check_dups, (char *) argv[i])) @@ -8971,10 +9082,11 @@ nm_device_reactivate_ip4_config (NMDevice *self, if (priv->ip4_state != IP_NONE) { g_clear_object (&priv->con_ip4_config); g_clear_object (&priv->ext_ip4_config); - priv->con_ip4_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + priv->con_ip4_config = _ip4_config_new (self); nm_ip4_config_merge_setting (priv->con_ip4_config, s_ip4_new, - nm_device_get_ip4_route_metric (self)); + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET)); if (!force_restart) { method_old = s_ip4_old @@ -8992,7 +9104,7 @@ nm_device_reactivate_ip4_config (NMDevice *self, if (!nm_device_activate_stage3_ip4_start (self)) _LOGW (LOGD_IP4, "Failed to apply IPv4 configuration"); } else { - if (!ip4_config_merge_and_apply (self, NULL, TRUE)) + if (!ip4_config_merge_and_apply (self, TRUE)) _LOGW (LOGD_IP4, "Failed to reapply IPv4 configuration"); } } @@ -9013,10 +9125,11 @@ nm_device_reactivate_ip6_config (NMDevice *self, if (priv->ip6_state != IP_NONE) { g_clear_object (&priv->con_ip6_config); g_clear_object (&priv->ext_ip6_config); - priv->con_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + priv->con_ip6_config = _ip6_config_new (self); nm_ip6_config_merge_setting (priv->con_ip6_config, s_ip6_new, - nm_device_get_ip6_route_metric (self)); + nm_device_get_route_table (self, AF_INET6, TRUE), + nm_device_get_route_metric (self, AF_INET6)); if (!force_restart) { method_old = s_ip6_old @@ -9096,7 +9209,27 @@ can_reapply_change (NMDevice *self, const char *setting_name, NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_PROXY_SETTING_NAME)) { - /* accept all */ + if (g_hash_table_contains (diffs, NM_SETTING_IP_CONFIG_ROUTE_TABLE)) { + /* changing the route-table setting is complicated, because it affects + * how we sync the routes. Don't support changing it without full + * re-activation. + * + * The problem is really that changing the setting also affects the sync + * mode. So, switching from NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN to + * NM_IP_ROUTE_TABLE_SYNC_MODE_FULL would somehow require us to get rid + * of additional routes, but we don't know which routes were added by NM + * and which should be removed. + * + * Note how nm_device_get_route_table() caches the value for the duration of the + * activation. */ + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + "Can't reapply changes to '%s.%s' setting", + setting_name, + NM_SETTING_IP_CONFIG_ROUTE_TABLE); + return FALSE; + } return TRUE; } else { g_set_error (error, @@ -9484,6 +9617,97 @@ impl_device_get_applied_connection (NMDevice *self, /*****************************************************************************/ +typedef struct { + gint64 timestamp_ms; + bool dirty; +} IP6RoutesTemporaryNotAvailableData; + +static gboolean +_rt6_temporary_not_available_timeout (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + priv->rt6_temporary_not_available_id = 0; + nm_device_activate_schedule_ip6_config_result (self); + + return G_SOURCE_REMOVE; +} + +static gboolean +_rt6_temporary_not_available_set (NMDevice *self, + GPtrArray *temporary_not_available) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + IP6RoutesTemporaryNotAvailableData *data; + GHashTableIter iter; + gint64 now_ms, oldest_ms; + const gint64 MAX_AGE_MS = 20000; + guint i; + gboolean success = TRUE; + + if ( !temporary_not_available + || !temporary_not_available->len) { + /* nothing outstanding. Clear tracking the routes. */ + g_clear_pointer (&priv->rt6_temporary_not_available, g_hash_table_unref); + nm_clear_g_source (&priv->rt6_temporary_not_available_id); + return success; + } + + if (priv->rt6_temporary_not_available) { + g_hash_table_iter_init (&iter, priv->rt6_temporary_not_available); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data)) + data->dirty = TRUE; + } else { + priv->rt6_temporary_not_available = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal, + (GDestroyNotify) nmp_object_unref, + nm_g_slice_free_fcn (IP6RoutesTemporaryNotAvailableData)); + } + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + oldest_ms = now_ms; + + for (i = 0; i < temporary_not_available->len; i++) { + const NMPObject *o = temporary_not_available->pdata[i]; + + data = g_hash_table_lookup (priv->rt6_temporary_not_available, o); + if (data) { + if (!data->dirty) + continue; + data->dirty = FALSE; + nm_assert (data->timestamp_ms > 0 && data->timestamp_ms <= now_ms); + if (now_ms > data->timestamp_ms + MAX_AGE_MS) { + /* timeout. Could not add this address. */ + _LOGW (LOGD_DEVICE, "failure to add IPv6 route: %s", + nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + success = FALSE; + } else + oldest_ms = MIN (data->timestamp_ms, oldest_ms); + continue; + } + + data = g_slice_new0 (IP6RoutesTemporaryNotAvailableData); + data->timestamp_ms = now_ms; + g_hash_table_insert (priv->rt6_temporary_not_available, (gpointer) nmp_object_ref (o), data); + } + + g_hash_table_iter_init (&iter, priv->rt6_temporary_not_available); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data)) { + if (data->dirty) + g_hash_table_iter_remove (&iter); + } + + nm_clear_g_source (&priv->rt6_temporary_not_available_id); + priv->rt6_temporary_not_available_id = g_timeout_add (oldest_ms + MAX_AGE_MS - now_ms, + _rt6_temporary_not_available_timeout, + self); + + return success; +} + +/*****************************************************************************/ + static void disconnect_cb (NMDevice *self, GDBusMethodInvocation *context, @@ -9835,47 +10059,43 @@ nm_device_get_ip4_config (NMDevice *self) static gboolean nm_device_set_ip4_config (NMDevice *self, NMIP4Config *new_config, - guint32 default_route_metric, gboolean commit, - gboolean routes_full_sync) + GPtrArray *ip4_dev_route_blacklist) { NMDevicePrivate *priv; NMIP4Config *old_config = NULL; gboolean has_changes = FALSE; gboolean success = TRUE; - gboolean def_route_changed; - int ip_ifindex, config_ifindex; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - _LOGD (LOGD_IP4, "ip4-config: update (commit=%d, routes-full-sync=%d, new-config=%p)", - commit, routes_full_sync, new_config); + _LOGD (LOGD_IP4, "ip4-config: update (commit=%d, new-config=%p)", + commit, new_config); - priv = NM_DEVICE_GET_PRIVATE (self); - ip_ifindex = nm_device_get_ip_ifindex (self); + nm_assert ( !new_config + || ( new_config + && ({ + int ip_ifindex = nm_device_get_ip_ifindex (self); - if (new_config) { - config_ifindex = nm_ip4_config_get_ifindex (new_config); - if (config_ifindex > 0) - g_return_val_if_fail (ip_ifindex == config_ifindex, FALSE); - } + ( ip_ifindex > 0 + && ip_ifindex == nm_ip4_config_get_ifindex (new_config)); + }))); + + priv = NM_DEVICE_GET_PRIVATE (self); old_config = priv->ip4_config; /* Always commit to nm-platform to update lifetimes */ if (commit && new_config) { - gboolean assumed = nm_device_sys_iface_state_is_external_or_assume (self); - _commit_mtu (self, new_config); - /* For assumed devices we must not touch the kernel-routes, such as the device-route. - * FIXME: this is wrong in case where "assumed" means "take-over-seamlessly". In this - * case, we should manage the device route, for example on new DHCP lease. */ success = nm_ip4_config_commit (new_config, nm_device_get_platform (self), - nm_netns_get_route_manager (priv->netns), - ip_ifindex, - routes_full_sync, - assumed ? (gint64) -1 : (gint64) default_route_metric); + nm_device_get_route_table (self, AF_INET, FALSE) + ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL + : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN); + nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self), + nm_ip4_config_get_ifindex (new_config), + ip4_dev_route_blacklist); } if (new_config) { @@ -9906,7 +10126,6 @@ nm_device_set_ip4_config (NMDevice *self, g_clear_object (&priv->dev_ip4_config); } - def_route_changed = nm_default_route_manager_ip4_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); concheck_periodic_update (self); if (!nm_device_sys_iface_state_is_external_or_assume (self)) @@ -9940,9 +10159,6 @@ nm_device_set_ip4_config (NMDevice *self, } nm_device_queue_recheck_assume (self); - } else if (def_route_changed) { - _LOGD (LOGD_IP4, "ip4-config: default route changed"); - g_signal_emit (self, signals[IP4_CONFIG_CHANGED], 0, priv->ip4_config, priv->ip4_config); } return success; @@ -9986,11 +10202,16 @@ nm_device_replace_vpn4_config (NMDevice *self, NMIP4Config *old, NMIP4Config *co { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + nm_assert (!old || NM_IS_IP4_CONFIG (old)); + nm_assert (!config || NM_IS_IP4_CONFIG (config)); + nm_assert (!old || nm_ip4_config_get_ifindex (old) == nm_device_get_ip_ifindex (self)); + nm_assert (!config || nm_ip4_config_get_ifindex (config) == nm_device_get_ip_ifindex (self)); + if (!_replace_vpn_config_in_list (&priv->vpn4_configs, (GObject *) old, (GObject *) config)) return; /* NULL to use existing configs */ - if (!ip4_config_merge_and_apply (self, NULL, TRUE)) + if (!ip4_config_merge_and_apply (self, TRUE)) _LOGW (LOGD_IP4, "failed to set VPN routes for device"); } @@ -10007,47 +10228,53 @@ nm_device_set_wwan_ip4_config (NMDevice *self, NMIP4Config *config) priv->wwan_ip4_config = g_object_ref (config); /* NULL to use existing configs */ - if (!ip4_config_merge_and_apply (self, NULL, TRUE)) + if (!ip4_config_merge_and_apply (self, TRUE)) _LOGW (LOGD_IP4, "failed to set WWAN IPv4 configuration"); } static gboolean nm_device_set_ip6_config (NMDevice *self, NMIP6Config *new_config, - gboolean commit, - gboolean routes_full_sync) + gboolean commit) { NMDevicePrivate *priv; NMIP6Config *old_config = NULL; gboolean has_changes = FALSE; gboolean success = TRUE; - gboolean def_route_changed; - int ip_ifindex, config_ifindex; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - _LOGD (LOGD_IP6, "ip6-config: update (commit=%d, routes-full-sync=%d, new-config=%p)", - commit, routes_full_sync, new_config); + _LOGD (LOGD_IP6, "ip6-config: update (commit=%d, new-config=%p)", + commit, new_config); - priv = NM_DEVICE_GET_PRIVATE (self); - ip_ifindex = nm_device_get_ip_ifindex (self); + nm_assert ( !new_config + || ( new_config + && ({ + int ip_ifindex = nm_device_get_ip_ifindex (self); - if (new_config) { - config_ifindex = nm_ip6_config_get_ifindex (new_config); - if (config_ifindex > 0) - g_return_val_if_fail (ip_ifindex == config_ifindex, FALSE); - } + ( ip_ifindex > 0 + && ip_ifindex == nm_ip6_config_get_ifindex (new_config)); + }))); + + priv = NM_DEVICE_GET_PRIVATE (self); old_config = priv->ip6_config; /* Always commit to nm-platform to update lifetimes */ if (commit && new_config) { + gs_unref_ptrarray GPtrArray *temporary_not_available = NULL; + _commit_mtu (self, priv->ip4_config); + success = nm_ip6_config_commit (new_config, nm_device_get_platform (self), - nm_netns_get_route_manager (priv->netns), - ip_ifindex, - routes_full_sync); + nm_device_get_route_table (self, AF_INET6, FALSE) + ? NM_IP_ROUTE_TABLE_SYNC_MODE_FULL + : NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN, + &temporary_not_available); + + if (!_rt6_temporary_not_available_set (self, temporary_not_available)) + success = FALSE; } if (new_config) { @@ -10077,8 +10304,6 @@ nm_device_set_ip6_config (NMDevice *self, nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config))); } - def_route_changed = nm_default_route_manager_ip6_update_default_route (nm_netns_get_default_route_manager (priv->netns), self); - if (has_changes) { NMSettingsConnection *settings_connection; @@ -10108,9 +10333,6 @@ nm_device_set_ip6_config (NMDevice *self, if (priv->ndisc) ndisc_set_router_config (priv->ndisc, self); - } else if (def_route_changed) { - _LOGD (LOGD_IP6, "ip6-config: default route changed"); - g_signal_emit (self, signals[IP6_CONFIG_CHANGED], 0, priv->ip6_config, priv->ip6_config); } return success; @@ -10121,6 +10343,11 @@ nm_device_replace_vpn6_config (NMDevice *self, NMIP6Config *old, NMIP6Config *co { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + nm_assert (!old || NM_IS_IP6_CONFIG (old)); + nm_assert (!config || NM_IS_IP6_CONFIG (config)); + nm_assert (!old || nm_ip6_config_get_ifindex (old) == nm_device_get_ip_ifindex (self)); + nm_assert (!config || nm_ip6_config_get_ifindex (config) == nm_device_get_ip_ifindex (self)); + if (!_replace_vpn_config_in_list (&priv->vpn6_configs, (GObject *) old, (GObject *) config)) return; @@ -10370,7 +10597,7 @@ nm_device_start_ip_check (NMDevice *self) NMSettingConnection *s_con; guint timeout = 0; const char *ping_binary = NULL; - char buf[INET6_ADDRSTRLEN] = { 0 }; + char buf[NM_UTILS_INET_ADDRSTRLEN]; NMLogDomain log_domain = LOGD_IP4; /* Shouldn't be any active ping here, since IP_CHECK happens after the @@ -10389,25 +10616,24 @@ nm_device_start_ip_check (NMDevice *self) g_assert (s_con); timeout = nm_setting_connection_get_gateway_ping_timeout (s_con); + buf[0] = '\0'; if (timeout) { - if (priv->ip4_config && priv->ip4_state == IP_DONE) { - guint gw = 0; + const NMPObject *gw; - ping_binary = nm_utils_find_helper ("ping", "/usr/bin/ping", NULL); - log_domain = LOGD_IP4; - - gw = nm_ip4_config_get_gateway (priv->ip4_config); - if (gw && !inet_ntop (AF_INET, &gw, buf, sizeof (buf))) - buf[0] = '\0'; + if (priv->ip4_config && priv->ip4_state == IP_DONE) { + gw = nm_ip4_config_best_default_route_get (priv->ip4_config); + if (gw) { + nm_utils_inet4_ntop (NMP_OBJECT_CAST_IP4_ROUTE (gw)->gateway, buf); + ping_binary = nm_utils_find_helper ("ping", "/usr/bin/ping", NULL); + log_domain = LOGD_IP4; + } } else if (priv->ip6_config && priv->ip6_state == IP_DONE) { - const struct in6_addr *gw = NULL; - - ping_binary = nm_utils_find_helper ("ping6", "/usr/bin/ping6", NULL); - log_domain = LOGD_IP6; - - gw = nm_ip6_config_get_gateway (priv->ip6_config); - if (gw && !inet_ntop (AF_INET6, gw, buf, sizeof (buf))) - buf[0] = '\0'; + gw = nm_ip6_config_best_default_route_get (priv->ip6_config); + if (gw) { + nm_utils_inet6_ntop (&NMP_OBJECT_CAST_IP6_ROUTE (gw)->gateway, buf); + ping_binary = nm_utils_find_helper ("ping6", "/usr/bin/ping6", NULL); + log_domain = LOGD_IP6; + } } } @@ -10507,6 +10733,8 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) * a timeout is reached. */ if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { + gint64 now_ms, until_ms; + /* we start a grace period of 5 seconds during which we will schedule * a pending action whenever we have no carrier. * @@ -10515,7 +10743,10 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) nm_clear_g_source (&priv->carrier_wait_id); if (!priv->carrier) nm_device_add_pending_action (self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); - priv->carrier_wait_id = g_timeout_add_seconds (5, carrier_wait_timeout, self); + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + until_ms = NM_MAX (now_ms + CARRIER_WAIT_TIME_MS, priv->carrier_wait_until_ms); + priv->carrier_wait_id = g_timeout_add (until_ms - now_ms, carrier_wait_timeout, self); } /* Can only get HW address of some devices when they are up */ @@ -10525,7 +10756,7 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) /* when the link comes up, we must restore IP configuration if necessary. */ if (priv->ip4_state == IP_DONE) { - if (!ip4_config_merge_and_apply (self, NULL, TRUE)) + if (!ip4_config_merge_and_apply (self, TRUE)) _LOGW (LOGD_IP4, "failed applying IP4 config after bringing link up"); } if (priv->ip6_state == IP_DONE) { @@ -10608,20 +10839,28 @@ find_ip4_lease_config (NMDevice *self, g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); leases = nm_dhcp_manager_get_lease_ip_configs (nm_dhcp_manager_get (), + nm_device_get_multi_index (self), + AF_INET, ip_iface, ip_ifindex, nm_connection_get_uuid (connection), - FALSE, - nm_device_get_ip4_route_metric (self)); + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET)); for (liter = leases; liter && !found; liter = liter->next) { NMIP4Config *lease_config = liter->data; - const NMPlatformIP4Address *address = nm_ip4_config_get_address (lease_config, 0); - guint32 gateway = nm_ip4_config_get_gateway (lease_config); + const NMPlatformIP4Address *address = nm_ip4_config_get_first_address (lease_config); + const NMPObject *gw1, *gw2; g_assert (address); if (!nm_ip4_config_address_exists (ext_ip4_config, address)) continue; - if (gateway != nm_ip4_config_get_gateway (ext_ip4_config)) + gw1 = nm_ip4_config_best_default_route_get (lease_config); + if (!gw1) + continue; + gw2 = nm_ip4_config_best_default_route_get (ext_ip4_config); + if (!gw2) + continue; + if (NMP_OBJECT_CAST_IP4_ROUTE (gw1)->gateway != NMP_OBJECT_CAST_IP4_ROUTE (gw2)->gateway) continue; found = g_object_ref (lease_config); } @@ -10641,23 +10880,24 @@ capture_lease_config (NMDevice *self, NMSettingsConnection *const*connections; guint i; gboolean dhcp_used = FALSE; + NMDedupMultiIter ipconf_iter; /* Ensure at least one address on the device has a non-infinite lifetime, * otherwise DHCP cannot possibly be active on the device right now. */ if (ext_ip4_config && out_ip4_config) { - for (i = 0; i < nm_ip4_config_get_num_addresses (ext_ip4_config); i++) { - const NMPlatformIP4Address *addr = nm_ip4_config_get_address (ext_ip4_config, i); + const NMPlatformIP4Address *addr; + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, ext_ip4_config, &addr) { if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { dhcp_used = TRUE; break; } } } else if (ext_ip6_config && out_ip6_config) { - for (i = 0; i < nm_ip6_config_get_num_addresses (ext_ip6_config); i++) { - const NMPlatformIP6Address *addr = nm_ip6_config_get_address (ext_ip6_config, i); + const NMPlatformIP6Address *addr; + nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, ext_ip6_config, &addr) { if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) { dhcp_used = TRUE; break; @@ -10695,184 +10935,161 @@ capture_lease_config (NMDevice *self, } } -static void -_ip4_config_intersect (gpointer value, gpointer user_data) -{ - NMIP4Config *dst = (NMIP4Config *) value; - NMIP4Config *src = (NMIP4Config *) user_data; - - nm_ip4_config_intersect (dst, src); -} - -static void -_ip4_config_subtract (gpointer value, gpointer user_data) -{ - NMIP4Config *dst = (NMIP4Config *) user_data; - NMIP4Config *src = (NMIP4Config *) value; - - nm_ip4_config_subtract (dst, src); -} - -static void -update_ip4_config (NMDevice *self, gboolean initial) +static gboolean +update_ext_ip_config (NMDevice *self, int addr_family, gboolean initial, gboolean intersect_configs) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int ifindex; gboolean capture_resolv_conf; + GSList *iter; - /* If a commit is scheduled, this function would potentially interfere with - * it changing IP configurations before they are applied. Postpone the - * update in such case. - */ - if ( !initial - && activation_source_is_scheduled (self, - activate_stage5_ip4_config_commit, - AF_INET)) { - priv->queued_ip4_config_pending = FALSE; - priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); - _LOGT (LOGD_DEVICE, "IP4 update was postponed"); - return; - } + nm_assert_addr_family (addr_family); ifindex = nm_device_get_ip_ifindex (self); if (!ifindex) - return; + return FALSE; capture_resolv_conf = initial && nm_dns_manager_get_resolv_conf_explicit (nm_dns_manager_get ()); - /* IPv4 */ - g_clear_object (&priv->ext_ip4_config); - priv->ext_ip4_config = nm_ip4_config_capture (nm_device_get_platform (self), - ifindex, - capture_resolv_conf); - if (priv->ext_ip4_config) { - if (initial) { - g_clear_object (&priv->dev_ip4_config); - capture_lease_config (self, priv->ext_ip4_config, &priv->dev_ip4_config, NULL, NULL); - } - - /* FIXME: ext_ip4_config does not contain routes with source==RTPROT_KERNEL. - * Hence, we will wrongly remove device-routes with metric=0 if they were added by - * the user on purpose. This should be fixed by also tracking and exposing - * kernel routes. */ - - /* This function was called upon external changes. Remove the configuration - * (addresses,routes) that is no longer present externally from the internal - * config. This way, we don't re-add addresses that were manually removed - * by the user. */ - if (priv->con_ip4_config) - nm_ip4_config_intersect (priv->con_ip4_config, priv->ext_ip4_config); - if (priv->dev_ip4_config) - nm_ip4_config_intersect (priv->dev_ip4_config, priv->ext_ip4_config); + if (addr_family == AF_INET) { - g_slist_foreach (priv->vpn4_configs, _ip4_config_intersect, priv->ext_ip4_config); + g_clear_object (&priv->ext_ip4_config); + priv->ext_ip4_config = nm_ip4_config_capture (nm_device_get_multi_index (self), + nm_device_get_platform (self), + ifindex, + capture_resolv_conf); + if (priv->ext_ip4_config) { + if (initial) { + g_clear_object (&priv->dev_ip4_config); + capture_lease_config (self, priv->ext_ip4_config, &priv->dev_ip4_config, NULL, NULL); + } - if (priv->wwan_ip4_config) - nm_ip4_config_intersect (priv->wwan_ip4_config, priv->ext_ip4_config); + if (intersect_configs) { + /* This function was called upon external changes. Remove the configuration + * (addresses,routes) that is no longer present externally from the internal + * config. This way, we don't re-add addresses that were manually removed + * by the user. */ + if (priv->con_ip4_config) { + nm_ip4_config_intersect (priv->con_ip4_config, priv->ext_ip4_config, + default_route_metric_penalty_get (self, AF_INET)); + } + if (priv->dev_ip4_config) { + nm_ip4_config_intersect (priv->dev_ip4_config, priv->ext_ip4_config, + default_route_metric_penalty_get (self, AF_INET)); + } + if (priv->wwan_ip4_config) { + nm_ip4_config_intersect (priv->wwan_ip4_config, priv->ext_ip4_config, + default_route_metric_penalty_get (self, AF_INET)); + } + for (iter = priv->vpn4_configs; iter; iter = iter->next) + nm_ip4_config_intersect (iter->data, priv->ext_ip4_config, 0); + } - /* Remove parts from ext_ip4_config to only contain the information that - * was configured externally -- we already have the same configuration from - * internal origins. */ - if (priv->con_ip4_config) - nm_ip4_config_subtract (priv->ext_ip4_config, priv->con_ip4_config); - if (priv->dev_ip4_config) - nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config); + /* Remove parts from ext_ip4_config to only contain the information that + * was configured externally -- we already have the same configuration from + * internal origins. */ + if (priv->con_ip4_config) { + nm_ip4_config_subtract (priv->ext_ip4_config, priv->con_ip4_config, + default_route_metric_penalty_get (self, AF_INET)); + } + if (priv->dev_ip4_config) { + nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config, + default_route_metric_penalty_get (self, AF_INET)); + } + if (priv->wwan_ip4_config) { + nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config, + default_route_metric_penalty_get (self, AF_INET)); + } + for (iter = priv->vpn4_configs; iter; iter = iter->next) + nm_ip4_config_subtract (priv->ext_ip4_config, iter->data, 0); + } - g_slist_foreach (priv->vpn4_configs, _ip4_config_subtract, priv->ext_ip4_config); + } else { + nm_assert (addr_family == AF_INET6); - if (priv->wwan_ip4_config) - nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config); + g_clear_object (&priv->ext_ip6_config); + g_clear_object (&priv->ext_ip6_config_captured); + priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_multi_index (self), + nm_device_get_platform (self), + ifindex, + capture_resolv_conf, + NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + if (priv->ext_ip6_config_captured) { + + priv->ext_ip6_config = nm_ip6_config_new_cloned (priv->ext_ip6_config_captured); + + if (intersect_configs) { + /* This function was called upon external changes. Remove the configuration + * (addresses,routes) that is no longer present externally from the internal + * config. This way, we don't re-add addresses that were manually removed + * by the user. */ + if (priv->con_ip6_config) { + nm_ip6_config_intersect (priv->con_ip6_config, priv->ext_ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + if (priv->ac_ip6_config) { + nm_ip6_config_intersect (priv->ac_ip6_config, priv->ext_ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + if (priv->dhcp6.ip6_config) { + nm_ip6_config_intersect (priv->dhcp6.ip6_config, priv->ext_ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + if (priv->wwan_ip6_config) { + nm_ip6_config_intersect (priv->wwan_ip6_config, priv->ext_ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + for (iter = priv->vpn6_configs; iter; iter = iter->next) + nm_ip6_config_intersect (iter->data, priv->ext_ip6_config, 0); + } - ip4_config_merge_and_apply (self, NULL, FALSE); + /* Remove parts from ext_ip6_config to only contain the information that + * was configured externally -- we already have the same configuration from + * internal origins. */ + if (priv->con_ip6_config) { + nm_ip6_config_subtract (priv->ext_ip6_config, priv->con_ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + if (priv->ac_ip6_config) { + nm_ip6_config_subtract (priv->ext_ip6_config, priv->ac_ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + if (priv->dhcp6.ip6_config) { + nm_ip6_config_subtract (priv->ext_ip6_config, priv->dhcp6.ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + if (priv->wwan_ip6_config) { + nm_ip6_config_subtract (priv->ext_ip6_config, priv->wwan_ip6_config, + default_route_metric_penalty_get (self, AF_INET6)); + } + for (iter = priv->vpn6_configs; iter; iter = iter->next) + nm_ip6_config_subtract (priv->ext_ip6_config, iter->data, 0); + } } -} - -static void -_ip6_config_intersect (gpointer value, gpointer user_data) -{ - NMIP6Config *dst = (NMIP6Config *) value; - NMIP6Config *src = (NMIP6Config *) user_data; - - nm_ip6_config_intersect (dst, src); -} - -static void -_ip6_config_subtract (gpointer value, gpointer user_data) -{ - NMIP6Config *dst = (NMIP6Config *) user_data; - NMIP6Config *src = (NMIP6Config *) value; - nm_ip6_config_subtract (dst, src); + return TRUE; } static void -update_ip6_config (NMDevice *self, gboolean initial) +update_ip_config (NMDevice *self, int addr_family, gboolean initial) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ifindex; - gboolean capture_resolv_conf; - /* If a commit is scheduled, this function would potentially interfere with - * it changing IP configurations before they are applied. Postpone the - * update in such case. - */ - if ( !initial - && activation_source_is_scheduled (self, - activate_stage5_ip6_config_commit, - AF_INET6)) { - priv->queued_ip6_config_pending = FALSE; - priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); - _LOGT (LOGD_DEVICE, "IP6 update was postponed"); - return; - } - - ifindex = nm_device_get_ip_ifindex (self); - if (!ifindex) - return; + nm_assert_addr_family (addr_family); - capture_resolv_conf = initial - && nm_dns_manager_get_resolv_conf_explicit (nm_dns_manager_get ()); + if (update_ext_ip_config (self, addr_family, initial, TRUE)) { + if (addr_family == AF_INET) { + if (priv->ext_ip4_config) + ip4_config_merge_and_apply (self, FALSE); + } else { + if (priv->ext_ip6_config_captured) + ip6_config_merge_and_apply (self, FALSE); + } + } - /* IPv6 */ - g_clear_object (&priv->ext_ip6_config); - g_clear_object (&priv->ext_ip6_config_captured); - priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_platform (self), ifindex, capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - if (priv->ext_ip6_config_captured) { - - priv->ext_ip6_config = nm_ip6_config_new_cloned (priv->ext_ip6_config_captured); - - /* This function was called upon external changes. Remove the configuration - * (addresses,routes) that is no longer present externally from the internal - * config. This way, we don't re-add addresses that were manually removed - * by the user. */ - if (priv->con_ip6_config) - nm_ip6_config_intersect (priv->con_ip6_config, priv->ext_ip6_config); - 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->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); - - /* Remove parts from ext_ip6_config to only contain the information that - * was configured externally -- we already have the same configuration from - * internal origins. */ - if (priv->con_ip6_config) - 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->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); - - ip6_config_merge_and_apply (self, FALSE); - } - - if ( priv->linklocal6_timeout_id + if ( addr_family == AF_INET6 + && priv->linklocal6_timeout_id && priv->ext_ip6_config_captured && nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE)) { /* linklocal6 is ready now, do the state transition... we are also @@ -10885,8 +11102,8 @@ update_ip6_config (NMDevice *self, gboolean initial) void nm_device_capture_initial_config (NMDevice *self) { - update_ip4_config (self, TRUE); - update_ip6_config (self, TRUE); + update_ip_config (self, AF_INET, TRUE); + update_ip_config (self, AF_INET6, TRUE); } static gboolean @@ -10906,10 +11123,27 @@ queued_ip4_config_change (gpointer user_data) return TRUE; priv->queued_ip4_config_id = 0; - update_ip4_config (self, FALSE); + + /* If a commit is scheduled, this function would potentially interfere with + * it changing IP configurations before they are applied. Postpone the + * update in such case. + */ + if (activation_source_is_scheduled (self, + activate_stage5_ip4_config_result, + AF_INET)) { + priv->queued_ip4_config_pending = FALSE; + priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); + _LOGT (LOGD_DEVICE, "IP4 update was postponed"); + } else + update_ip_config (self, AF_INET, FALSE); set_unmanaged_external_down (self, TRUE); + if (!nm_device_sys_iface_state_is_external_or_assume (self)) { + priv->v4_has_shadowed_routes = _v4_has_shadowed_routes_detect (self);; + ip4_rp_filter_update (self); + } + return FALSE; } @@ -10932,7 +11166,19 @@ queued_ip6_config_change (gpointer user_data) return TRUE; priv->queued_ip6_config_id = 0; - update_ip6_config (self, FALSE); + + /* If a commit is scheduled, this function would potentially interfere with + * it changing IP configurations before they are applied. Postpone the + * update in such case. + */ + if (activation_source_is_scheduled (self, + activate_stage5_ip6_config_commit, + AF_INET6)) { + priv->queued_ip6_config_pending = FALSE; + priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); + _LOGT (LOGD_DEVICE, "IP6 update was postponed"); + } else + update_ip_config (self, AF_INET6, FALSE); if (priv->state < NM_DEVICE_STATE_DEACTIVATING && nm_platform_link_get (nm_device_get_platform (self), priv->ifindex)) { @@ -10977,6 +11223,8 @@ queued_ip6_config_change (gpointer user_data) g_clear_object (&priv->dad6_ip6_config); _set_ip_state (self, AF_INET6, IP_DONE); check_ip_state (self, FALSE); + if (priv->rt6_temporary_not_available) + nm_device_activate_schedule_ip6_config_result (self); } } @@ -11052,6 +11300,7 @@ NM_UTILS_FLAGS2STR_DEFINE (nm_unmanaged_flags2str, NMUnmanagedFlags, NM_UTILS_FLAGS2STR (NM_UNMANAGED_USER_EXPLICIT, "user-explicit"), NM_UTILS_FLAGS2STR (NM_UNMANAGED_BY_DEFAULT, "by-default"), NM_UTILS_FLAGS2STR (NM_UNMANAGED_USER_SETTINGS, "user-settings"), + NM_UTILS_FLAGS2STR (NM_UNMANAGED_USER_CONF, "user-conf"), NM_UTILS_FLAGS2STR (NM_UNMANAGED_USER_UDEV, "user-udev"), NM_UTILS_FLAGS2STR (NM_UNMANAGED_EXTERNAL_DOWN, "external-down"), NM_UTILS_FLAGS2STR (NM_UNMANAGED_IS_SLAVE, "is-slave"), @@ -11144,11 +11393,19 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo if (NM_FLAGS_ANY (mask, NM_UNMANAGED_USER_UDEV)) { /* configuration from udev or nm-config overwrites the by-default flag - * which is based on the device type. */ - flags &= ~NM_UNMANAGED_BY_DEFAULT; + * which is based on the device type. + * configuration from udev overwrites external-down */ + flags &= ~( NM_UNMANAGED_BY_DEFAULT + | NM_UNMANAGED_EXTERNAL_DOWN); + } - /* configuration from udev overwrites external-down */ - flags &= ~NM_UNMANAGED_EXTERNAL_DOWN; + if (NM_FLAGS_ANY (mask, NM_UNMANAGED_USER_CONF)) { + /* configuration from NetworkManager.conf overwrites the by-default flag + * which is based on the device type. + * It also overwrites the udev configuration and external-down */ + flags &= ~( NM_UNMANAGED_BY_DEFAULT + | NM_UNMANAGED_USER_UDEV + | NM_UNMANAGED_EXTERNAL_DOWN); } if ( NM_FLAGS_HAS (mask, NM_UNMANAGED_IS_SLAVE) @@ -11160,9 +11417,9 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo if (NM_FLAGS_HAS (mask, NM_UNMANAGED_USER_EXPLICIT)) { /* if the device is managed by user-decision, certain other flags * are ignored. */ - flags &= ~( NM_UNMANAGED_BY_DEFAULT | NM_UNMANAGED_USER_UDEV + | NM_UNMANAGED_USER_CONF | NM_UNMANAGED_EXTERNAL_DOWN); } @@ -11469,6 +11726,35 @@ nm_device_set_unmanaged_by_user_udev (NMDevice *self) } void +nm_device_set_unmanaged_by_user_conf (NMDevice *self) +{ + gboolean value; + NMUnmanFlagOp set_op; + + value = nm_config_data_get_device_config_boolean (NM_CONFIG_GET_DATA, + NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, + self, + -1, + TRUE); + switch (value) { + case TRUE: + set_op = NM_UNMAN_FLAG_OP_SET_MANAGED; + break; + case FALSE: + set_op = NM_UNMAN_FLAG_OP_SET_UNMANAGED; + break; + default: + set_op = NM_UNMAN_FLAG_OP_FORGET; + break; + } + + nm_device_set_unmanaged_by_flags (self, + NM_UNMANAGED_USER_CONF, + set_op, + NM_DEVICE_STATE_REASON_USER_REQUESTED); +} + +void nm_device_set_unmanaged_by_quitting (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -11489,14 +11775,6 @@ nm_device_set_unmanaged_by_quitting (NMDevice *self) /*****************************************************************************/ void -nm_device_set_dhcp_timeout (NMDevice *self, guint32 timeout) -{ - g_return_if_fail (NM_IS_DEVICE (self)); - - NM_DEVICE_GET_PRIVATE (self)->dhcp_timeout = timeout; -} - -void nm_device_set_dhcp_anycast_address (NMDevice *self, const char *addr) { NMDevicePrivate *priv; @@ -11766,6 +12044,12 @@ check_connection_available (NMDevice *self, return TRUE; } + /* master types are always available even without carrier. + * Making connection non-available would un-enslave slaves which + * is not desired. */ + if (nm_device_is_master (self)) + return TRUE; + return FALSE; } @@ -12030,7 +12314,8 @@ nm_device_has_pending_action (NMDevice *self) if (priv->pending_actions) return TRUE; - if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) { + if ( nm_device_is_real (self) + && nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) { /* as long as the platform link is not yet initialized, we have a pending * action. */ return TRUE; @@ -12091,26 +12376,22 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { - _update_default_route (self, AF_INET, FALSE, FALSE); - _update_default_route (self, AF_INET6, FALSE, FALSE); - } else { - _update_default_route (self, AF_INET, priv->default_route.v4_has, TRUE); - _update_default_route (self, AF_INET6, priv->default_route.v6_has, TRUE); - } - _update_default_route (self, AF_INET, FALSE, TRUE); - _update_default_route (self, AF_INET6, FALSE, TRUE); - priv->v4_commit_first_time = TRUE; priv->v6_commit_first_time = TRUE; + priv->v4_route_table_initalized = FALSE; + priv->v6_route_table_initalized = FALSE; + + priv->default_route_metric_penalty_ip4_has = FALSE; + priv->default_route_metric_penalty_ip6_has = FALSE; + priv->linklocal6_dad_counter = 0; /* Clean up IP configs; this does not actually deconfigure the * interface; the caller must flush routes and addresses explicitly. */ - nm_device_set_ip4_config (self, NULL, 0, TRUE, TRUE); - nm_device_set_ip6_config (self, NULL, TRUE, TRUE); + nm_device_set_ip4_config (self, NULL, TRUE, NULL); + nm_device_set_ip6_config (self, NULL, TRUE); g_clear_object (&priv->proxy_config); g_clear_object (&priv->con_ip4_config); g_clear_object (&priv->dev_ip4_config); @@ -12125,6 +12406,9 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) g_clear_object (&priv->ip6_config); g_clear_object (&priv->dad6_ip6_config); + g_clear_pointer (&priv->rt6_temporary_not_available, g_hash_table_unref); + nm_clear_g_source (&priv->rt6_temporary_not_available_id); + g_slist_free_full (priv->vpn4_configs, g_object_unref); priv->vpn4_configs = NULL; g_slist_free_full (priv->vpn6_configs, g_object_unref); @@ -12198,18 +12482,24 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean if (NM_DEVICE_GET_CLASS (self)->deactivate) NM_DEVICE_GET_CLASS (self)->deactivate (self); + ifindex = nm_device_get_ip_ifindex (self); + if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { /* master: release slaves */ nm_device_master_release_slaves (self); /* 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_netns_get_route_manager (priv->netns), ifindex); - nm_platform_address_flush (nm_device_get_platform (self), ifindex); + NMPlatform *platform = nm_device_get_platform (self); + + nm_platform_ip_route_flush (platform, AF_UNSPEC, ifindex); + nm_platform_ip_address_flush (platform, AF_UNSPEC, ifindex); } } + if (ifindex > 0) + nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self), ifindex, NULL); + /* slave: mark no longer enslaved */ if ( priv->master && nm_platform_link_get_master (nm_device_get_platform (self), priv->ifindex) <= 0) @@ -12241,8 +12531,10 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean && cleanup_type == CLEANUP_TYPE_DECONFIGURE) { _LOGT (LOGD_DEVICE, "mtu: reset device-mtu: %u, ipv6-mtu: %u, ifindex: %d", (guint) priv->mtu_initial, (guint) priv->ip6_mtu_initial, ifindex); - if (priv->mtu_initial) + if (priv->mtu_initial) { nm_platform_link_set_mtu (nm_device_get_platform (self), ifindex, priv->mtu_initial); + priv->carrier_wait_until_ms = nm_utils_get_monotonic_timestamp_ms () + CARRIER_WAIT_TIME_AFTER_MTU_MS; + } if (priv->ip6_mtu_initial) { char sbuf[64]; @@ -12267,15 +12559,13 @@ static char * find_dhcp4_address (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - guint i, n; + const NMPlatformIP4Address *a; + NMDedupMultiIter ipconf_iter; if (!priv->ip4_config) return NULL; - n = nm_ip4_config_get_num_addresses (priv->ip4_config); - for (i = 0; i < n; i++) { - const NMPlatformIP4Address *a = nm_ip4_config_get_address (priv->ip4_config, i); - + nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, priv->ip4_config, &a) { if (a->addr_source == NM_IP_CONFIG_SOURCE_DHCP) return g_strdup (nm_utils_inet4_ntop (a->address, NULL)); } @@ -12346,7 +12636,7 @@ nm_device_spawn_iface_helper (NMDevice *self) g_assert (s_ip4); g_ptr_array_add (argv, g_strdup ("--priority4")); - g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_ip4_route_metric (self))); + g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_route_metric (self, AF_INET))); g_ptr_array_add (argv, g_strdup ("--dhcp4")); g_ptr_array_add (argv, g_strdup (dhcp4_address)); @@ -12388,7 +12678,7 @@ nm_device_spawn_iface_helper (NMDevice *self) g_assert (s_ip6); g_ptr_array_add (argv, g_strdup ("--priority6")); - g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_ip6_route_metric (self))); + g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_route_metric (self, AF_INET6))); g_ptr_array_add (argv, g_strdup ("--slaac")); @@ -12555,7 +12845,7 @@ _set_state_full (NMDevice *self, if ( (priv->state == state) && ( state != NM_DEVICE_STATE_UNAVAILABLE || !priv->firmware_missing)) { - _LOGD (LOGD_DEVICE, "state change: %s -> %s (reason '%s', internal state '%s'%s)", + _LOGD (LOGD_DEVICE, "state change: %s -> %s (reason '%s', sys-iface-state: '%s'%s)", nm_device_state_to_str (old_state), nm_device_state_to_str (state), reason_to_string (reason), @@ -12564,7 +12854,7 @@ _set_state_full (NMDevice *self, return; } - _LOGI (LOGD_DEVICE, "state change: %s -> %s (reason '%s', internal state '%s')", + _LOGI (LOGD_DEVICE, "state change: %s -> %s (reason '%s', sys-iface-state: '%s')", nm_device_state_to_str (old_state), nm_device_state_to_str (state), reason_to_string (reason), @@ -12593,6 +12883,10 @@ _set_state_full (NMDevice *self, NM_DEVICE_SYS_IFACE_STATE_ASSUME)) nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_MANAGED); + if ( state <= NM_DEVICE_STATE_DISCONNECTED + || state >= NM_DEVICE_STATE_ACTIVATED) + priv->auth_retries = NM_DEVICE_AUTH_RETRIES_UNSET; + if (state > NM_DEVICE_STATE_DISCONNECTED) nm_device_assume_state_reset (self); @@ -12752,15 +13046,11 @@ _set_state_full (NMDevice *self, if ( priv->queued_act_request && !priv->queued_act_request_is_waiting_for_carrier) { NMActRequest *queued_req; - gboolean success; queued_req = priv->queued_act_request; priv->queued_act_request = NULL; - success = _device_activate (self, queued_req); + _device_activate (self, queued_req); g_object_unref (queued_req); - if (success) - break; - /* fall through */ } break; case NM_DEVICE_STATE_ACTIVATED: @@ -13380,7 +13670,7 @@ handle_fail: _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)); + nm_platform_error_to_string_a (plerr)); } if (was_up) { @@ -13683,6 +13973,12 @@ nm_device_get_initial_hw_address (NMDevice *self) gboolean nm_device_spec_match_list (NMDevice *self, const GSList *specs) { + return nm_device_spec_match_list_full (self, specs, FALSE); +} + +int +nm_device_spec_match_list_full (NMDevice *self, const GSList *specs, int no_match_value) +{ NMDeviceClass *klass; NMMatchSpecMatchType m; @@ -13697,7 +13993,17 @@ nm_device_spec_match_list (NMDevice *self, const GSList *specs) nm_device_get_driver_version (self), nm_device_get_permanent_hw_address (self), klass->get_s390_subchannels ? klass->get_s390_subchannels (self) : NULL); - return m == NM_MATCH_SPEC_MATCH; + + switch (m) { + case NM_MATCH_SPEC_MATCH: + return TRUE; + case NM_MATCH_SPEC_NEG_MATCH: + return FALSE; + case NM_MATCH_SPEC_NO_MATCH: + return no_match_value; + } + nm_assert_not_reached (); + return no_match_value; } guint @@ -13727,6 +14033,54 @@ nm_device_get_supplicant_timeout (NMDevice *self) SUPPLICANT_DEFAULT_TIMEOUT); } +gboolean +nm_device_auth_retries_try_next (NMDevice *self) +{ + NMDevicePrivate *priv; + NMSettingConnection *s_con; + int auth_retries; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + auth_retries = priv->auth_retries; + + if (G_UNLIKELY (auth_retries == NM_DEVICE_AUTH_RETRIES_UNSET)) { + auth_retries = -1; + + s_con = NM_SETTING_CONNECTION (nm_device_get_applied_setting (self, NM_TYPE_SETTING_CONNECTION)); + if (s_con) + auth_retries = nm_setting_connection_get_auth_retries (s_con); + + if (auth_retries == -1) { + gs_free char *value = NULL; + + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "connection.auth-retries", + self); + auth_retries = _nm_utils_ascii_str_to_int64 (value, 10, -1, G_MAXINT32, -1); + } + + if (auth_retries == 0) + auth_retries = NM_DEVICE_AUTH_RETRIES_INFINITY; + else if (auth_retries == -1) + auth_retries = NM_DEVICE_AUTH_RETRIES_DEFAULT; + else + nm_assert (auth_retries > 0); + + priv->auth_retries = auth_retries; + } + + if (auth_retries == NM_DEVICE_AUTH_RETRIES_INFINITY) + return TRUE; + if (auth_retries <= 0) { + nm_assert (auth_retries == 0); + return FALSE; + } + priv->auth_retries--; + return TRUE; +} + /*****************************************************************************/ static const char * @@ -13742,7 +14096,7 @@ _activation_func_to_string (ActivationHandleFunc func) FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage3_ip_config_start); FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage4_ip4_config_timeout); FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage4_ip6_config_timeout); - FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage5_ip4_config_commit); + FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage5_ip4_config_result); FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage5_ip6_config_commit); g_return_val_if_reached ("unknown"); } @@ -13758,23 +14112,22 @@ nm_device_init (NMDevice *self) self->_priv = priv; + c_list_init (&priv->slaves); + priv->netns = g_object_ref (NM_NETNS_GET); + priv->auth_retries = NM_DEVICE_AUTH_RETRIES_UNSET; priv->type = NM_DEVICE_TYPE_UNKNOWN; priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED; priv->state = NM_DEVICE_STATE_UNMANAGED; priv->state_reason = NM_DEVICE_STATE_REASON_NONE; - priv->dhcp_timeout = 0; priv->rfkill_type = RFKILL_TYPE_UNKNOWN; priv->unmanaged_flags = NM_UNMANAGED_PLATFORM_INIT; priv->unmanaged_mask = priv->unmanaged_flags; priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); - priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); + priv->ip6_saved_properties = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_free); priv->sys_iface_state = NM_DEVICE_SYS_IFACE_STATE_EXTERNAL; - priv->default_route.v4_is_assumed = TRUE; - priv->default_route.v6_is_assumed = TRUE; - priv->v4_commit_first_time = TRUE; priv->v6_commit_first_time = TRUE; } @@ -13843,9 +14196,6 @@ constructed (GObject *object) 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); - g_signal_connect (nm_netns_get_route_manager (priv->netns), NM_ROUTE_MANAGER_IP4_ROUTES_CHANGED, - G_CALLBACK (ip4_routes_changed_changed_cb), self); - priv->settings = g_object_ref (NM_SETTINGS_GET); g_assert (priv->settings); @@ -13886,9 +14236,6 @@ dispose (GObject *object) 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); - g_signal_handlers_disconnect_by_func (nm_netns_get_route_manager (priv->netns), - G_CALLBACK (ip4_routes_changed_changed_cb), self); - g_slist_free_full (priv->arping.dad_list, (GDestroyNotify) nm_arping_manager_destroy); priv->arping.dad_list = NULL; @@ -13904,7 +14251,7 @@ dispose (GObject *object) _cleanup_generic_pre (self, CLEANUP_TYPE_KEEP); - g_warn_if_fail (priv->slaves == NULL); + g_warn_if_fail (c_list_is_empty (&priv->slaves)); g_assert (priv->master_ready_id == 0); /* Let the kernel manage IPv6LL again */ @@ -14083,10 +14430,6 @@ set_property (GObject *object, guint prop_id, /* construct-only */ priv->rfkill_type = g_value_get_uint (value); break; - case PROP_IS_MASTER: - /* construct-only */ - priv->is_master = g_value_get_boolean (value); - break; case PROP_PERM_HW_ADDRESS: /* construct-only */ priv->hw_addr_perm = g_value_dup_string (value); @@ -14221,9 +14564,6 @@ get_property (GObject *object, guint prop_id, case PROP_PHYSICAL_PORT_ID: g_value_set_string (value, priv->physical_port_id); break; - case PROP_IS_MASTER: - g_value_set_boolean (value, priv->is_master); - break; case PROP_MASTER: g_value_set_object (value, nm_device_get_master (self)); break; @@ -14260,13 +14600,15 @@ get_property (GObject *object, guint prop_id, g_value_set_boolean (value, nm_device_is_real (self)); break; case PROP_SLAVES: { - GSList *slave_iter; + CList *slave_iter; char **slave_list; - guint i; + gsize i, n; - slave_list = g_new (char *, g_slist_length (priv->slaves) + 1); - for (slave_iter = priv->slaves, i = 0; slave_iter; slave_iter = slave_iter->next) { - SlaveInfo *info = slave_iter->data; + n = c_list_length (&priv->slaves); + slave_list = g_new (char *, n + 1); + i = 0; + c_list_for_each (slave_iter, &priv->slaves) { + SlaveInfo *info = c_list_entry (slave_iter, SlaveInfo, lst_slave); const char *path; if (!NM_DEVICE_GET_PRIVATE (info->slave)->is_enslaved) @@ -14275,6 +14617,7 @@ get_property (GObject *object, guint prop_id, if (path) slave_list[i++] = g_strdup (path); } + nm_assert (i <= n); slave_list[i] = NULL; g_value_take_boxed (value, slave_list); break; @@ -14323,7 +14666,6 @@ nm_device_class_init (NMDeviceClass *klass) klass->act_stage3_ip6_config_start = act_stage3_ip6_config_start; klass->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout; klass->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout; - klass->have_any_ready_slaves = have_any_ready_slaves; klass->get_type_description = get_type_description; klass->get_autoconnect_allowed = get_autoconnect_allowed; @@ -14484,11 +14826,6 @@ nm_device_class_init (NMDeviceClass *klass) NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_IS_MASTER] = - g_param_spec_boolean (NM_DEVICE_IS_MASTER, "", "", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); obj_properties[PROP_MASTER] = g_param_spec_object (NM_DEVICE_MASTER, "", "", NM_TYPE_DEVICE, |