diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index cd72f3d712..1cda2e1950 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -160,6 +160,8 @@ NMIP6Config *nm_device_ip6_config_new (NMDevice *self); NMIPConfig *nm_device_ip_config_new (NMDevice *self, int addr_family); +NML3ConfigData *nm_device_create_l3_config_data (NMDevice *self); + /*****************************************************************************/ gint64 nm_device_get_configured_mtu_from_connection_default (NMDevice *self, diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 0f47a2c3ba..53a3085bea 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -28,6 +28,8 @@ #include "nm-libnm-core-intern/nm-ethtool-utils.h" #include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-device-private.h" +#include "nm-l3cfg.h" +#include "nm-l3-config-data.h" #include "NetworkManagerUtils.h" #include "nm-manager.h" #include "platform/nm-platform.h" @@ -134,6 +136,21 @@ typedef struct { typedef void (*AcdCallback) (NMDevice *, NMIP4Config **, gboolean); +typedef enum { + /* The various NML3ConfigData types that we track explicitly. Note that + * their relative order matters: higher numbers in this enum means more + * important (and during merge overwrites other settings). */ + L3_CONFIG_DATA_TYPE_LL_4, + L3_CONFIG_DATA_TYPE_AC_6, + L3_CONFIG_DATA_TYPE_DHCP_4, + L3_CONFIG_DATA_TYPE_DHCP_6, + L3_CONFIG_DATA_TYPE_DEV_4, + L3_CONFIG_DATA_TYPE_DEV_6, + L3_CONFIG_DATA_TYPE_SETTING, + _L3_CONFIG_DATA_TYPE_NUM, + _L3_CONFIG_DATA_TYPE_NONE, +} L3ConfigDataType; + typedef struct { AcdCallback callback; NMDevice *device; @@ -285,24 +302,30 @@ typedef struct _NMDevicePrivate { char * udi; char * path; + union { + const char *const iface; + char * iface_; + }; + union { + const char *const ip_iface; + char * ip_iface_; + }; + union { NML3Cfg *const l3cfg; NML3Cfg *l3cfg_; }; - union { - NML3Cfg *const ip_l3cfg; - NML3Cfg *ip_l3cfg_; - }; - - union { - const char *const iface; - char * iface_; - }; union { const int ifindex; int ifindex_; }; + union { + const int ip_ifindex; + int ip_ifindex_; + }; + + const NML3ConfigData *l3cds[_L3_CONFIG_DATA_TYPE_NUM]; int parent_ifindex; @@ -320,14 +343,6 @@ typedef struct _NMDevicePrivate { bool update_ip_config_completed_v4:1; bool update_ip_config_completed_v6:1; - union { - const char *const ip_iface; - char * ip_iface_; - }; - union { - const int ip_ifindex; - int ip_ifindex_; - }; NMDeviceType type; char * type_desc; NMLinkType link_type; @@ -419,6 +434,14 @@ typedef struct _NMDevicePrivate { * in the future. This is used to extend the grace period in this particular case. */ gint64 carrier_wait_until_ms; + union { + struct { + NML3ConfigMergeFlags l3config_merge_flags_6; + NML3ConfigMergeFlags l3config_merge_flags_4; + }; + NML3ConfigMergeFlags l3config_merge_flags_x[2]; + }; + bool carrier:1; bool ignore_carrier:1; @@ -435,6 +458,8 @@ typedef struct _NMDevicePrivate { bool v4_route_table_initialized:1; bool v6_route_table_initialized:1; + bool l3config_merge_flags_has:1; + bool v4_route_table_all_sync_before:1; bool v6_route_table_all_sync_before:1; @@ -678,6 +703,10 @@ static void nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason static void addrconf6_start_with_link_ready (NMDevice *self); static gboolean linklocal6_start (NMDevice *self); +static guint32 default_route_metric_penalty_get (NMDevice *self, int addr_family); + +static guint get_ipv4_dad_timeout (NMDevice *self); + static void _carrier_wait_check_queued_act_request (NMDevice *self); static gint64 _get_carrier_wait_ms (NMDevice *self); @@ -1233,6 +1262,21 @@ nm_device_ip_config_new (NMDevice *self, int addr_family) : (gpointer) nm_device_ip6_config_new (self); } +NML3ConfigData * +nm_device_create_l3_config_data (NMDevice *self) +{ + int ifindex; + + nm_assert (NM_IS_DEVICE (self)); + + ifindex = nm_device_get_ip_ifindex (self); + if (ifindex <= 0) + g_return_val_if_reached (NULL); + + return nm_l3_config_data_new (nm_device_get_multi_index (self), + ifindex); +} + static void applied_config_clear (AppliedConfig *config) { @@ -1755,6 +1799,246 @@ _set_ip_state (NMDevice *self, int addr_family, NMDeviceIPState new_state) /*****************************************************************************/ +static L3ConfigDataType +_dev_l3_config_data_tag_to_type (NMDevice *self, + gconstpointer tag) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + int d; + + if ( tag < ((gpointer) &priv->l3cds[0]) + || tag >= ((gpointer) &priv->l3cds[G_N_ELEMENTS (priv->l3cds)])) + return _L3_CONFIG_DATA_TYPE_NONE; + + d = ((const NML3ConfigData **) tag) - (&priv->l3cds[0]); + + nm_assert (d >= 0); + nm_assert (d < _L3_CONFIG_DATA_TYPE_NUM); + nm_assert (tag == &priv->l3cds[d]); + return d; +} + +static NML3ConfigMergeFlags +_dev_l3_get_merge_flags (NMDevice *self, + L3ConfigDataType type) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NML3ConfigMergeFlags flags; + NMConnection *connection; + NMSettingIPConfig *s_ip; + + if (G_UNLIKELY (!priv->l3config_merge_flags_has)) { + int IS_IPv4; + + connection = nm_device_get_applied_connection (self); + + for (IS_IPv4 = 0; IS_IPv4 < 2; IS_IPv4++) { + flags = NM_L3_CONFIG_MERGE_FLAGS_NONE; + + if ( connection + && (s_ip = nm_connection_get_setting_ip_config (connection, IS_IPv4 ? AF_INET : AF_INET6))) { + + if (nm_setting_ip_config_get_ignore_auto_routes (s_ip)) + flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES; + + if (nm_setting_ip_config_get_ignore_auto_dns (s_ip)) + flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_DNS; + + if ( nm_setting_ip_config_get_never_default (s_ip) + || nm_setting_ip_config_get_gateway (s_ip)) { + /* if the connection has an explicit gateway, we also ignore + * the default routes from other sources. */ + flags |= NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES; + } + } + + priv->l3config_merge_flags_x[IS_IPv4] = flags; + } + priv->l3config_merge_flags_has = TRUE; + } + + switch (type) { + + case L3_CONFIG_DATA_TYPE_SETTING: + case L3_CONFIG_DATA_TYPE_LL_4: + return NM_L3_CONFIG_MERGE_FLAGS_NONE; + + case L3_CONFIG_DATA_TYPE_DHCP_4: + case L3_CONFIG_DATA_TYPE_DEV_4: + return priv->l3config_merge_flags_4; + + case L3_CONFIG_DATA_TYPE_AC_6: + case L3_CONFIG_DATA_TYPE_DHCP_6: + case L3_CONFIG_DATA_TYPE_DEV_6: + return priv->l3config_merge_flags_6; + + case _L3_CONFIG_DATA_TYPE_NUM: + case _L3_CONFIG_DATA_TYPE_NONE: + break; + } + return nm_assert_unreachable_val (NM_L3_CONFIG_MERGE_FLAGS_NONE); +} + +_nm_unused /* FIXME(l3cfg) */ +static gboolean +_dev_l3_register_l3cds_set_one (NMDevice *self, + L3ConfigDataType l3cd_type, + const NML3ConfigData *l3cd) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + nm_auto_unref_l3cd const NML3ConfigData *l3cd_old_free = NULL; + const NML3ConfigData *l3cd_old; + gboolean changed = FALSE; + + l3cd_old = priv->l3cds[l3cd_type]; + if (l3cd != priv->l3cds[l3cd_type]) { + l3cd_old_free = g_steal_pointer (&priv->l3cds[l3cd_type]); + if (l3cd) + priv->l3cds[l3cd_type] = nm_l3_config_data_ref_and_seal (l3cd); + } + + if (priv->l3cfg) { + if (nm_l3cfg_add_config (priv->l3cfg, + &priv->l3cds[l3cd_type], + FALSE, + priv->l3cds[l3cd_type], + l3cd_type, + default_route_metric_penalty_get (self, AF_INET), + default_route_metric_penalty_get (self, AF_INET6), + get_ipv4_dad_timeout (self), + _dev_l3_get_merge_flags (self, l3cd_type))) + changed = TRUE; + + if ( l3cd_old + && l3cd_old != l3cd) { + if (nm_l3cfg_remove_config (priv->l3cfg, &priv->l3cds[l3cd_type], l3cd_old)) + changed = TRUE; + } + } + + return changed; +} + +static gboolean +_dev_l3_register_l3cds (NMDevice *self, + NML3Cfg *l3cfg, + gboolean do_add /* else remove */) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + guint32 default_route_penalty_4 = 0; + guint32 default_route_penalty_6 = 0; + guint32 acd_timeout_msec = 0; + gboolean is_external; + gboolean changed; + int i; + + if (!l3cfg) + return FALSE; + + is_external = nm_device_sys_iface_state_is_external (self); + if (!is_external) { + default_route_penalty_4 = default_route_metric_penalty_get (self, AF_INET); + default_route_penalty_6 = default_route_metric_penalty_get (self, AF_INET6); + acd_timeout_msec = get_ipv4_dad_timeout (self); + } + + changed = FALSE; + for (i = 0; i < G_N_ELEMENTS (priv->l3cds); i++) { + if (!priv->l3cds[i]) + continue; + if (!do_add) { + if (nm_l3cfg_remove_config (l3cfg, &priv->l3cds[i], priv->l3cds[i])) + changed = TRUE; + continue; + } + if (is_external) + continue; + if (nm_l3cfg_add_config (l3cfg, + &priv->l3cds[i], + FALSE, + priv->l3cds[i], + i, + default_route_penalty_4, + default_route_penalty_6, + acd_timeout_msec, + _dev_l3_get_merge_flags (self, i))) + changed = TRUE; + } + + return changed; +} + +_nm_unused /* FIXME(l3cfg) */ +static gboolean +_dev_l3_platform_commit (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gboolean success; + + if (!priv->l3cfg) + return FALSE; + + if (nm_device_sys_iface_state_is_external (self)) + return TRUE; + + success = nm_l3cfg_platform_commit (priv->l3cfg, + nm_device_sys_iface_state_is_external_or_assume (self) + ? NM_L3_CFG_COMMIT_TYPE_ASSUME + : NM_L3_CFG_COMMIT_TYPE_UPDATE, + AF_UNSPEC, + NULL); + return success; +} + +static void +_dev_l3_cfg_acd_maybe_comlete (NMDevice *self) +{ + /* FIXME(l3cfg) */ +} + +static void +_dev_l3_cfg_notify_cb (NML3Cfg *l3cfg, + int notify_type_i, + const NML3ConfigNotifyPayload *payload, + NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + nm_assert (l3cfg == priv->l3cfg); + + switch ((NML3ConfigNotifyType) notify_type_i) { + case NM_L3_CONFIG_NOTIFY_TYPE_ACD_FAILED: { + const NML3ConfigNotifyPayloadAcdFailedSource *sources = payload->acd_failed.sources; + guint sources_len = payload->acd_failed.sources_len; + guint i; + + for (i = 0; i < sources_len; i++) { + L3ConfigDataType l3cd_type = _dev_l3_config_data_tag_to_type (self, sources[i].tag); + + if (NM_IN_SET (l3cd_type, L3_CONFIG_DATA_TYPE_DHCP_4)) { + nm_dhcp_client_decline (priv->dhcp_data_4.client, "Address conflict detected", NULL); + nm_device_ip_method_failed (self, AF_INET, + NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE); + return; + } + } + _dev_l3_cfg_acd_maybe_comlete (self); + return; + } + case NM_L3_CONFIG_NOTIFY_TYPE_ACD_COMPLETED: + _dev_l3_cfg_acd_maybe_comlete (self); + return; + case NM_L3_CONFIG_NOTIFY_TYPE_ROUTES_TEMPORARY_NOT_AVAILABLE_EXPIRED: + /* FIXME(l3cfg) */ + return; + case _NM_L3_CONFIG_NOTIFY_TYPE_NUM: + break; + } + nm_assert_not_reached (); +} + +/*****************************************************************************/ + const char * nm_device_get_udi (NMDevice *self) { @@ -1776,7 +2060,8 @@ _set_ifindex (NMDevice *self, int ifindex, gboolean is_ip_ifindex) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gs_unref_object NML3Cfg *l3cfg_old = NULL; - NML3Cfg **p_l3cfg; + gboolean l3_changed; + int ip_ifindex_new; int *p_ifindex; if (ifindex < 0) @@ -1791,24 +2076,47 @@ _set_ifindex (NMDevice *self, int ifindex, gboolean is_ip_ifindex) *p_ifindex = ifindex; - p_l3cfg = is_ip_ifindex - ? &priv->ip_l3cfg_ - : &priv->l3cfg_; + ip_ifindex_new = nm_device_get_ip_ifindex (self); - l3cfg_old = g_steal_pointer (p_l3cfg); - if (ifindex > 0) - *p_l3cfg = nm_netns_access_l3cfg (priv->netns, ifindex); + if (priv->l3cfg) { + if ( ip_ifindex_new <= 0 + || ip_ifindex_new != nm_l3cfg_get_ifindex (priv->l3cfg)) { + g_signal_handlers_disconnect_by_func (priv->l3cfg, + G_CALLBACK (_dev_l3_cfg_notify_cb), + self); + l3cfg_old = g_steal_pointer (&priv->l3cfg_); + } + } + if ( !priv->l3cfg + && ip_ifindex_new > 0) { + priv->l3cfg_ = nm_netns_access_l3cfg (priv->netns, ip_ifindex_new); + + g_signal_connect (priv->l3cfg, + NM_L3CFG_SIGNAL_NOTIFY, + G_CALLBACK (_dev_l3_cfg_notify_cb), + self); + } _LOGD (LOGD_DEVICE, "ifindex: set %sifindex %d%s%s%s%s%s%s", is_ip_ifindex ? "ip-" : "", ifindex, - NM_PRINT_FMT_QUOTED (l3cfg_old, " (old-l3cfg: ", nm_hash_obfuscated_ptr_str_a (l3cfg_old), ")", ""), - NM_PRINT_FMT_QUOTED (*p_l3cfg, " (l3cfg: ", nm_hash_obfuscated_ptr_str_a (*p_l3cfg), ")", "")); + NM_PRINT_FMT_QUOTED (l3cfg_old && l3cfg_old != priv->l3cfg, " (old-l3cfg: ", nm_hash_obfuscated_ptr_str_a (l3cfg_old), ")", ""), + NM_PRINT_FMT_QUOTED (priv->l3cfg && l3cfg_old != priv->l3cfg, " (l3cfg: ", nm_hash_obfuscated_ptr_str_a (priv->l3cfg), ")", "")); if (!is_ip_ifindex) _notify (self, PROP_IFINDEX); + l3_changed = FALSE; + if (_dev_l3_register_l3cds (self, priv->l3cfg, TRUE)) + l3_changed = TRUE; + if (_dev_l3_register_l3cds (self, l3cfg_old, FALSE)) + l3_changed = TRUE; + + if (l3_changed) { + /* FIXME(l3cfg) */ + } + return TRUE; } @@ -7089,6 +7397,7 @@ activate_stage1_device_prepare (NMDevice *self) priv->v4_route_table_initialized = FALSE; priv->v6_route_table_initialized = FALSE; + priv->l3config_merge_flags_has = FALSE; _set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_NONE); _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_NONE); @@ -12569,6 +12878,7 @@ check_and_reapply_connection (NMDevice *self, priv->v4_route_table_initialized = FALSE; priv->v6_route_table_initialized = FALSE; + priv->l3config_merge_flags_has = FALSE; /************************************************************************** * Reapply changes @@ -15653,6 +15963,7 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) priv->v4_route_table_initialized = FALSE; priv->v6_route_table_initialized = FALSE; + priv->l3config_merge_flags_has = FALSE; priv->v4_route_table_all_sync_before = FALSE; priv->v6_route_table_all_sync_before = FALSE; diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index a084974dde..964e4698a9 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -52,7 +52,7 @@ typedef enum { * set, this flag gets ignored during merge. * @NM_L3_CONFIG_MERGE_FLAGS_NO_DNS: don't merge DNS information */ -typedef enum { +typedef enum _nm_packed { NM_L3_CONFIG_MERGE_FLAGS_NONE = 0, NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES = (1LL << 0), NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES = (1LL << 1),