From 137d02a7e60660641ecd6547283f5d0a2056c2c6 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 26 Aug 2020 21:23:02 +0200 Subject: [PATCH 01/10] platform: add nm_platform_ip_address_get_peer_address() helper --- src/platform/nm-platform.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index cece00dfe9..48c656ff56 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -1919,6 +1919,18 @@ gboolean nm_platform_ip_address_flush (NMPlatform *self, int addr_family, int ifindex); +static inline gconstpointer +nm_platform_ip_address_get_peer_address (int addr_family, + const NMPlatformIPAddress *addr) +{ + nm_assert_addr_family (addr_family); + nm_assert (addr); + + if (NM_IS_IPv4 (addr_family)) + return &((NMPlatformIP4Address *) addr)->peer_address; + return &((NMPlatformIP6Address *) addr)->peer_address; +} + void nm_platform_ip_route_normalize (int addr_family, NMPlatformIPRoute *route); From 0ab341b9e6291af109efd7b0be9c05c0061f64fe Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 26 Aug 2020 21:23:44 +0200 Subject: [PATCH 02/10] l3cfg: add nm_l3_config_data_get_domains() accessor --- src/nm-l3-config-data.c | 12 ++++++++++++ src/nm-l3-config-data.h | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index 7d1cb3a1a8..ddd2c27116 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -1037,6 +1037,18 @@ nm_l3_config_data_set_nis_domain (NML3ConfigData *self, return nm_utils_strdup_reset (&self->nis_domain, nis_domain); } +const char *const* +nm_l3_config_data_get_domains (const NML3ConfigData *self, + int addr_family, + guint *out_len) +{ + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert_addr_family (addr_family); + nm_assert (out_len); + + return nm_strv_ptrarray_get_unsafe (self->domains_x[NM_IS_IPv4 (addr_family)], out_len); +} + gboolean nm_l3_config_data_add_domain (NML3ConfigData *self, int addr_family, diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index d6e275724e..8563e07256 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -410,6 +410,10 @@ gboolean nm_l3_config_data_add_nameserver (NML3ConfigData *self, gboolean nm_l3_config_data_add_nis_server (NML3ConfigData *self, in_addr_t nis_server); +const char *const*nm_l3_config_data_get_domains (const NML3ConfigData *self, + int addr_family, + guint *out_len); + gboolean nm_l3_config_data_set_nis_domain (NML3ConfigData *self, const char *nis_domain); From e89a095673690784769b100d88c0f8f1085d8c70 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 26 Aug 2020 21:15:32 +0200 Subject: [PATCH 03/10] l3cfg: add nm_l3_config_data_get_blacklisted_ip4_routes() util We will need to prune routes that kernel adds (and we don't want). --- src/nm-l3-config-data.c | 115 ++++++++++++++++++++++++++++--------- src/nm-l3-config-data.h | 6 +- src/platform/nm-platform.h | 2 + 3 files changed, 94 insertions(+), 29 deletions(-) diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index ddd2c27116..146dff4008 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -1380,16 +1380,101 @@ _data_get_direct_route_for_host (const NML3ConfigData *self, /*****************************************************************************/ +/* Kernel likes to add device routes for all addresses. Normally, we want to suppress that + * with IFA_F_NOPREFIXROUTE. But we also want to support kernels that don't support that + * flag. So, we collect here all those routes that kernel might add but we don't want. + * If the route shows up within a certain timeout of us configuring the address, we assume + * that it was (undesirably) added by kernel and we remove it. + * + * The most common reason is that for each IPv4 address we want to add a corresponding device + * route with the right ipv4.route-metric. The route that kernel adds has metric 0, so it is + * undesired. + * + * FIXME(l3cfg): implement handling blacklisted routes. + * + * For IPv6, IFA_F_NOPREFIXROUTE is supported for a longer time and we don't do such a hack. + */ +GPtrArray * +nm_l3_config_data_get_blacklisted_ip4_routes (const NML3ConfigData *self, + gboolean is_vrf) +{ + gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL; + const NMPObject *my_addr_obj; + NMDedupMultiIter iter; + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); + + /* For IPv6 slaac, we explicitly add the device-routes (onlink). + * As we don't do that for IPv4 and manual IPv6 addresses. Add them here + * as dependent routes. */ + + nm_l3_config_data_iter_obj_for_each (&iter, self, &my_addr_obj, NMP_OBJECT_TYPE_IP4_ADDRESS) { + const NMPlatformIP4Address *const my_addr = NMP_OBJECT_CAST_IP4_ADDRESS (my_addr_obj); + in_addr_t network_4; + NMPlatformIPXRoute rx; + + if (my_addr->external) + continue; + + nm_assert (my_addr->plen <= 32); + if (my_addr->plen == 0) + continue; + + network_4 = nm_utils_ip4_address_clear_host_address (my_addr->peer_address, + my_addr->plen); + + if (nm_utils_ip4_address_is_zeronet (network_4)) { + /* Kernel doesn't add device-routes for destinations that + * start with 0.x.y.z. Skip them. */ + continue; + } + + if ( my_addr->plen == 32 + && my_addr->address == my_addr->peer_address) { + /* Kernel doesn't add device-routes for /32 addresses unless + * they have a peer. */ + continue; + } + + rx.r4 = (NMPlatformIP4Route) { + .ifindex = self->ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_KERNEL, + .network = network_4, + .plen = my_addr->plen, + .pref_src = my_addr->address, + .table_coerced = nm_platform_route_table_coerce (RT_TABLE_MAIN), + .metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE, + .scope_inv = nm_platform_route_scope_inv (NM_RT_SCOPE_LINK), + }; + nm_platform_ip_route_normalize (AF_INET, &rx.rx); + + if (nm_l3_config_data_lookup_route (self, + AF_INET, + &rx.rx)) { + /* we track such a route explicitly. Don't blacklist it. */ + continue; + } + + if (!ip4_dev_route_blacklist) + ip4_dev_route_blacklist = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + + g_ptr_array_add (ip4_dev_route_blacklist, + nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, &rx)); + } + + return g_steal_pointer (&ip4_dev_route_blacklist); +} + +/*****************************************************************************/ + void nm_l3_config_data_add_dependent_routes (NML3ConfigData *self, int addr_family, guint32 route_table, guint32 route_metric, - gboolean is_vrf, - GPtrArray **out_ip4_dev_route_blacklist) + gboolean is_vrf) { const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); - gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL; gs_unref_ptrarray GPtrArray *extra_onlink_routes = NULL; const NMPObject *my_addr_obj; const NMPObject *my_route_obj; @@ -1490,28 +1575,6 @@ nm_l3_config_data_add_dependent_routes (NML3ConfigData *self, }; nm_platform_ip_route_normalize (addr_family, &rx.rx); nm_l3_config_data_add_route (self, addr_family, NULL, &rx.rx); - - if ( IS_IPv4 - && out_ip4_dev_route_blacklist - && ( route_table != RT_TABLE_MAIN - || route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE)) { - - rx.r4.table_coerced = nm_platform_route_table_coerce (RT_TABLE_MAIN); - rx.r4.metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE; - nm_platform_ip_route_normalize (addr_family, &rx.rx); - - if (nm_l3_config_data_lookup_route (self, - addr_family, - &rx.rx)) { - /* we track such a route explicitly. Don't blacklist it. */ - } else { - if (!ip4_dev_route_blacklist) - ip4_dev_route_blacklist = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); - - g_ptr_array_add (ip4_dev_route_blacklist, - nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, &rx)); - } - } } else { const gboolean has_peer = !IN6_IS_ADDR_UNSPECIFIED (&my_addr->a6.peer_address); int routes_i; @@ -1602,8 +1665,6 @@ nm_l3_config_data_add_dependent_routes (NML3ConfigData *self, NULL); } } - - NM_SET_OUT (out_ip4_dev_route_blacklist, g_steal_pointer (&ip4_dev_route_blacklist)); } /*****************************************************************************/ diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index 8563e07256..53f3a4f562 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -105,12 +105,14 @@ void nm_l3_config_data_merge (NML3ConfigData *self, NML3ConfigMergeHookAddObj hook_add_addr, gpointer hook_user_data); +GPtrArray *nm_l3_config_data_get_blacklisted_ip4_routes (const NML3ConfigData *self, + gboolean is_vrf); + void nm_l3_config_data_add_dependent_routes (NML3ConfigData *self, int addr_family, guint32 route_table, guint32 route_metric, - gboolean is_vrf, - GPtrArray **out_ip4_dev_route_blacklist); + gboolean is_vrf); /*****************************************************************************/ diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 48c656ff56..53bfcaf486 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -309,6 +309,8 @@ typedef enum { \ guint8 plen; \ \ + /* FIXME(l3cfg): the external marker won't be necessary anymore, because we only + * merge addresses we care about, and ignore (don't remove) external addresses. */ \ bool external:1; \ \ bool use_ip4_broadcast_address:1; \ From 38b2239146f5a21ec0e1130af6f2146b7fdac9d7 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 27 Aug 2020 15:43:49 +0200 Subject: [PATCH 04/10] l3cfg: add more l3cfg API that will be used next --- src/nm-l3-config-data.c | 33 +++++++++++++++++++++++++ src/nm-l3-config-data.h | 52 ++++++++++++++++++++++++++++++++++++--- src/nm-l3cfg.c | 2 +- src/platform/nmp-object.h | 6 +++++ 4 files changed, 89 insertions(+), 4 deletions(-) diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index 146dff4008..16f922f8d0 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -655,6 +655,26 @@ nm_l3_config_data_set_flags_full (NML3ConfigData *self, /*****************************************************************************/ +const NMPObject * +nm_l3_config_data_get_first_obj (const NML3ConfigData *self, + NMPObjectType obj_type, + gboolean (*predicate) (const NMPObject *obj)) +{ + NMDedupMultiIter iter; + const NMPObject *obj; + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); + + nm_l3_config_data_iter_obj_for_each (&iter, self, &obj, obj_type) { + if ( !predicate + || predicate (obj)) + return obj; + } + return NULL; +} + +/*****************************************************************************/ + static gboolean _l3_config_data_add_obj (NMDedupMultiIndex *multi_idx, DedupMultiIdxType *idx_type, @@ -997,6 +1017,19 @@ nm_l3_config_data_add_nameserver (NML3ConfigData *self, nameserver); } +gboolean +nm_l3_config_data_clear_nameserver (NML3ConfigData *self, + int addr_family) +{ + gs_unref_array GArray *old = NULL; + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert_addr_family (addr_family); + + old = g_steal_pointer (&self->nameservers_x[NM_IS_IPv4 (addr_family)]); + return (nm_g_array_len (old) > 0); +} + const in_addr_t * nm_l3_config_data_get_wins (const NML3ConfigData *self, guint *out_len) diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index 53f3a4f562..36f28f13ab 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -63,6 +63,8 @@ typedef enum { /*****************************************************************************/ +static inline gboolean NM_IS_L3_CONFIG_DATA (const NML3ConfigData *self); + NML3ConfigData *nm_l3_config_data_new (NMDedupMultiIndex *multi_idx, int ifindex); const NML3ConfigData *nm_l3_config_data_ref (const NML3ConfigData *self); @@ -70,12 +72,49 @@ const NML3ConfigData *nm_l3_config_data_ref_and_seal (const NML3ConfigData *self const NML3ConfigData *nm_l3_config_data_seal (const NML3ConfigData *self); void nm_l3_config_data_unref (const NML3ConfigData *self); +#define nm_clear_l3cd(ptr) nm_clear_pointer ((ptr), nm_l3_config_data_unref) + NM_AUTO_DEFINE_FCN0 (const NML3ConfigData *, _nm_auto_unref_l3cd, nm_l3_config_data_unref); #define nm_auto_unref_l3cd nm_auto (_nm_auto_unref_l3cd) NM_AUTO_DEFINE_FCN0 (NML3ConfigData *, _nm_auto_unref_l3cd_init, nm_l3_config_data_unref); #define nm_auto_unref_l3cd_init nm_auto (_nm_auto_unref_l3cd_init) +static inline gboolean +nm_l3_config_data_reset (const NML3ConfigData **dst, const NML3ConfigData *src) +{ + nm_auto_unref_l3cd const NML3ConfigData *old = NULL; + + nm_assert (dst); + nm_assert (!*dst || NM_IS_L3_CONFIG_DATA (*dst)); + nm_assert (!src || NM_IS_L3_CONFIG_DATA (src)); + + if (*dst == src) + return FALSE; + old = *dst; + *dst = src ? nm_l3_config_data_ref_and_seal (src) : NULL; + return TRUE; +} + +static inline gboolean +nm_l3_config_data_reset_take (const NML3ConfigData **dst, const NML3ConfigData *src) +{ + nm_auto_unref_l3cd const NML3ConfigData *old = NULL; + + nm_assert (dst); + nm_assert (!*dst || NM_IS_L3_CONFIG_DATA (*dst)); + nm_assert (!src || NM_IS_L3_CONFIG_DATA (src)); + + if (*dst == src) { + if (src) + nm_l3_config_data_unref (src); + return FALSE; + } + old = *dst; + *dst = src ? nm_l3_config_data_seal (src) : NULL; + return TRUE; +} + gboolean nm_l3_config_data_is_sealed (const NML3ConfigData *self); NML3ConfigData *nm_l3_config_data_new_clone (const NML3ConfigData *src, @@ -118,12 +157,10 @@ void nm_l3_config_data_add_dependent_routes (NML3ConfigData *self, int nm_l3_config_data_get_ifindex (const NML3ConfigData *self); -NMDedupMultiIndex *nm_l3_config_data_get_multi_idx (const NML3ConfigData *self); - static inline gboolean NM_IS_L3_CONFIG_DATA (const NML3ConfigData *self) { - /* NML3ConfigData is not an NMObject, so we cannot ask which type it has. + /* NML3ConfigData is not an NMObject/GObject, so we cannot ask which type it has. * This check here is really only useful for assertions, and there it is * enough to check whether the pointer is not NULL. * @@ -133,6 +170,8 @@ NM_IS_L3_CONFIG_DATA (const NML3ConfigData *self) return !!self; } +NMDedupMultiIndex *nm_l3_config_data_get_multi_idx (const NML3ConfigData *self); + /*****************************************************************************/ int nm_l3_config_data_cmp (const NML3ConfigData *a, const NML3ConfigData *b); @@ -301,6 +340,10 @@ nm_l3_config_data_unset_flags (NML3ConfigData *self, gboolean nm_l3_config_data_set_source (NML3ConfigData *self, NMIPConfigSource source); +const NMPObject *nm_l3_config_data_get_first_obj (const NML3ConfigData *self, + NMPObjectType obj_type, + gboolean (*predicate) (const NMPObject *obj)); + gboolean nm_l3_config_data_add_address_full (NML3ConfigData *self, int addr_family, const NMPObject *obj_new, @@ -409,6 +452,9 @@ gboolean nm_l3_config_data_add_nameserver (NML3ConfigData *self, int addr_family, gconstpointer /* (const NMIPAddr *) */ nameserver); +gboolean nm_l3_config_data_clear_nameserver (NML3ConfigData *self, + int addr_family); + gboolean nm_l3_config_data_add_nis_server (NML3ConfigData *self, in_addr_t nis_server); diff --git a/src/nm-l3cfg.c b/src/nm-l3cfg.c index 01a7f00e51..d831223357 100644 --- a/src/nm-l3cfg.c +++ b/src/nm-l3cfg.c @@ -2994,7 +2994,7 @@ finalize (GObject *object) g_clear_object (&self->priv.netns); g_clear_object (&self->priv.platform); - nm_clear_pointer (&self->priv.p->combined_l3cd, nm_l3_config_data_unref); + nm_clear_l3cd (&self->priv.p->combined_l3cd); nm_clear_pointer (&self->priv.pllink, nmp_object_unref); diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index bbe2193225..4d9ec2a638 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -1066,6 +1066,12 @@ nmp_object_ip_route_is_best_defaut_route (const NMPObject *obj) && r->type_coerced == nm_platform_route_type_coerce (1 /* RTN_UNICAST */); } +static inline gboolean +nmp_object_ip6_address_is_not_link_local (const NMPObject *obj) +{ + return !IN6_IS_ADDR_LINKLOCAL (&NMP_OBJECT_CAST_IP6_ADDRESS (obj)->address); +} + /*****************************************************************************/ static inline gboolean From 41b9d44f9bc794aae975278e3666655011cf77d7 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 28 Aug 2020 17:19:09 +0200 Subject: [PATCH 05/10] l3cfg: drop unused NM_L3_CONFIG_MERGE_FLAGS_EXTERNAL The current approach also tracks external configuration in an NMIP[46]Config, and we need to special handle those. In the future, we only want to track what we actually want to configure. So this flag won't be used with NML3Cfg/NML3ConfigData. --- src/nm-l3-config-data.c | 19 ------------------- src/nm-l3-config-data.h | 2 -- 2 files changed, 21 deletions(-) diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index 16f922f8d0..e2bc28265a 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -2028,25 +2028,6 @@ nm_l3_config_data_merge (NML3ConfigData *self, && !hook_add_addr (src, obj, hook_user_data)) continue; - if ( NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_EXTERNAL) - && !NMP_OBJECT_CAST_IP_ADDRESS (obj)->external) { - NMPlatformIPXAddress a; - - if (IS_IPv4) - a.a4 = *NMP_OBJECT_CAST_IP4_ADDRESS (obj); - else - a.a6 = *NMP_OBJECT_CAST_IP6_ADDRESS (obj); - a.ax.ifindex = self->ifindex; - a.ax.external = TRUE; - nm_l3_config_data_add_address_full (self, - addr_family, - NULL, - &a.ax, - NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE, - NULL); - continue; - } - nm_l3_config_data_add_address_full (self, addr_family, obj, diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index 36f28f13ab..a084974dde 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -51,14 +51,12 @@ typedef enum { * Note that if the respective NML3ConfigData has NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES * set, this flag gets ignored during merge. * @NM_L3_CONFIG_MERGE_FLAGS_NO_DNS: don't merge DNS information - * @NM_L3_CONFIG_MERGE_FLAGS_EXTERNAL: mark new addresses as external */ typedef enum { 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), NM_L3_CONFIG_MERGE_FLAGS_NO_DNS = (1LL << 2), - NM_L3_CONFIG_MERGE_FLAGS_EXTERNAL = (1LL << 3), } NML3ConfigMergeFlags; /*****************************************************************************/ From bd054bf6fdfd3d5ce8eaec82e417787b6e705904 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 28 Aug 2020 17:20:53 +0200 Subject: [PATCH 06/10] l3cfg: remove changed flag from nm_l3cfg_add_config()/nm_l3cfg_remove_config() --- src/nm-l3cfg.c | 27 +++++++++++++++++---------- src/nm-l3cfg.h | 30 +++++++++++++++--------------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/nm-l3cfg.c b/src/nm-l3cfg.c index d831223357..3c8bb524e3 100644 --- a/src/nm-l3cfg.c +++ b/src/nm-l3cfg.c @@ -2297,7 +2297,7 @@ nm_l3cfg_mark_config_dirty (NML3Cfg *self, } } -void +gboolean nm_l3cfg_add_config (NML3Cfg *self, gconstpointer tag, gboolean replace_same_tag, @@ -2391,15 +2391,18 @@ nm_l3cfg_add_config (NML3Cfg *self, if (changed) self->priv.changed_configs = TRUE; + + return changed; } -static void +static gboolean _l3cfg_remove_config (NML3Cfg *self, gconstpointer tag, gboolean only_dirty, const NML3ConfigData *l3cd) { GArray *l3_config_datas; + gboolean changed; gssize idx; nm_assert (NM_IS_L3CFG (self)); @@ -2407,16 +2410,17 @@ _l3cfg_remove_config (NML3Cfg *self, l3_config_datas = self->priv.p->l3_config_datas; if (!l3_config_datas) - return; + return FALSE; idx = 0; + changed = FALSE; while (TRUE) { idx = _l3_config_datas_find_next (l3_config_datas, idx, tag, l3cd); if (idx < 0) - return; + return changed; if ( only_dirty && !_l3_config_datas_at (l3_config_datas, idx)->dirty) { @@ -2426,27 +2430,30 @@ _l3cfg_remove_config (NML3Cfg *self, self->priv.changed_configs = TRUE; _l3_config_datas_remove_index_fast (l3_config_datas, idx); - if (!l3cd) - return; + if (l3cd) { + /* only one was requested to be removed. We are done. */ + return TRUE; + } + changed = TRUE; } } -void +gboolean nm_l3cfg_remove_config (NML3Cfg *self, gconstpointer tag, const NML3ConfigData *ifcfg) { nm_assert (ifcfg); - _l3cfg_remove_config (self, tag, FALSE, ifcfg); + return _l3cfg_remove_config (self, tag, FALSE, ifcfg); } -void +gboolean nm_l3cfg_remove_config_all (NML3Cfg *self, gconstpointer tag, gboolean only_dirty) { - _l3cfg_remove_config (self, tag, only_dirty, NULL); + return _l3cfg_remove_config (self, tag, only_dirty, NULL); } /*****************************************************************************/ diff --git a/src/nm-l3cfg.h b/src/nm-l3cfg.h index 335b59e614..a199f27cfb 100644 --- a/src/nm-l3cfg.h +++ b/src/nm-l3cfg.h @@ -128,23 +128,23 @@ void nm_l3cfg_mark_config_dirty (NML3Cfg *self, gconstpointer tag, gboolean dirty); -void nm_l3cfg_add_config (NML3Cfg *self, - gconstpointer tag, - gboolean replace_same_tag, - const NML3ConfigData *l3cd, - int priority, - guint32 default_route_penalty_4, - guint32 default_route_penalty_6, - guint32 acd_timeout_msec, - NML3ConfigMergeFlags merge_flags); +gboolean nm_l3cfg_add_config (NML3Cfg *self, + gconstpointer tag, + gboolean replace_same_tag, + const NML3ConfigData *l3cd, + int priority, + guint32 default_route_penalty_4, + guint32 default_route_penalty_6, + guint32 acd_timeout_msec, + NML3ConfigMergeFlags merge_flags); -void nm_l3cfg_remove_config (NML3Cfg *self, - gconstpointer tag, - const NML3ConfigData *ifcfg); - -void nm_l3cfg_remove_config_all (NML3Cfg *self, +gboolean nm_l3cfg_remove_config (NML3Cfg *self, gconstpointer tag, - gboolean only_dirty); + const NML3ConfigData *ifcfg); + +gboolean nm_l3cfg_remove_config_all (NML3Cfg *self, + gconstpointer tag, + gboolean only_dirty); /*****************************************************************************/ From c328c10227bbffea0e29b3c80ba26611124bbe75 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 27 Aug 2020 19:06:15 +0200 Subject: [PATCH 07/10] l3cfg: more integration of NMDevice with l3cfg Add more code that will be used later for glueing NMDevice with NML3Cfg and NML3ConfigData. The code is not yet really used. --- src/devices/nm-device-private.h | 2 + src/devices/nm-device.c | 363 +++++++++++++++++++++++++++++--- src/nm-l3-config-data.h | 2 +- 3 files changed, 340 insertions(+), 27 deletions(-) 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), From 4c07d345057c84a5005dea0385c8b3e9d1c6138d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 3 Sep 2020 12:24:08 +0200 Subject: [PATCH 08/10] l3cfg: add nm_l3_config_data_has_routes_with_type_local() helper --- src/nm-l3-config-data.c | 57 +++++++++++++++++++++++++++++++++++++++++ src/nm-l3-config-data.h | 3 +++ 2 files changed, 60 insertions(+) diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index e2bc28265a..d547b5bd60 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -116,6 +116,11 @@ struct _NML3ConfigData { NMTernary metered:3; bool is_sealed:1; + + bool has_routes_with_type_local_4_set:1; + bool has_routes_with_type_local_6_set:1; + bool has_routes_with_type_local_4_val:1; + bool has_routes_with_type_local_6_val:1; }; /*****************************************************************************/ @@ -614,6 +619,50 @@ nmtst_l3_config_data_get_obj_at (const NML3ConfigData *self, /*****************************************************************************/ +gboolean +nm_l3_config_data_has_routes_with_type_local (const NML3ConfigData *self, int addr_family) +{ + const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); + NML3ConfigData *self_mutable; + NMDedupMultiIter iter; + const NMPObject *obj; + gboolean val; + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert_addr_family (addr_family); + + if (IS_IPv4) { + if (G_LIKELY (self->has_routes_with_type_local_4_set)) + return self->has_routes_with_type_local_4_val; + } else { + if (G_LIKELY (self->has_routes_with_type_local_6_set)) + return self->has_routes_with_type_local_6_val; + } + + val = FALSE; + nm_l3_config_data_iter_obj_for_each (&iter, self, &obj, NMP_OBJECT_TYPE_IP_ROUTE (IS_IPv4)) { + if (NMP_OBJECT_CAST_IP_ROUTE (obj)->type_coerced == nm_platform_route_type_coerce (RTN_LOCAL)) { + val = TRUE; + break; + } + } + + /* the value gets accumulated and cached. Doing that is also permissible to a + * const/sealed instance. Hence, we cast the const-ness away. */ + self_mutable = (NML3ConfigData *) self; + if (IS_IPv4) { + self_mutable->has_routes_with_type_local_4_set = TRUE; + self_mutable->has_routes_with_type_local_4_val = val; + } else { + self_mutable->has_routes_with_type_local_6_set = TRUE; + self_mutable->has_routes_with_type_local_6_val = val; + } + + return val; +} + +/*****************************************************************************/ + NMDedupMultiIndex * nm_l3_config_data_get_multi_idx (const NML3ConfigData *self) { @@ -920,6 +969,10 @@ nm_l3_config_data_add_route_full (NML3ConfigData *self, || ( NMP_OBJECT_GET_ADDR_FAMILY (obj_new) == addr_family && _route_valid (addr_family, NMP_OBJECT_CAST_IP_ROUTE (obj_new)))); + if (IS_IPv4) + self->has_routes_with_type_local_4_set = FALSE; + else + self->has_routes_with_type_local_6_set = FALSE; if (_l3_config_data_add_obj (self->multi_idx, addr_family == AF_INET ? &self->idx_routes_4 @@ -1945,6 +1998,10 @@ _init_from_platform (NML3ConfigData *self, : NMP_OBJECT_TYPE_IP6_ADDRESS, self->ifindex); if (head_entry) { + if (IS_IPv4) + self->has_routes_with_type_local_4_set = FALSE; + else + self->has_routes_with_type_local_6_set = FALSE; nmp_cache_iter_for_each (&iter, head_entry, &plobj) { if (!_l3_config_data_add_obj (self->multi_idx, &self->idx_addresses_x[IS_IPv4], diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index 964e4698a9..429325626c 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -271,6 +271,9 @@ nm_l3_config_data_get_num_routes (const NML3ConfigData *self, int addr_family) : NMP_OBJECT_TYPE_IP6_ROUTE); } +gboolean nm_l3_config_data_has_routes_with_type_local (const NML3ConfigData *self, + int addr_family); + const NMPObject *nmtst_l3_config_data_get_obj_at (const NML3ConfigData *self, NMPObjectType obj_type, guint i); From 47e2ff0a712764d617609858261c48bbc85a2537 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 3 Sep 2020 12:24:08 +0200 Subject: [PATCH 09/10] l3cfg: add nm_l3cfg_commit_type*() API for tracking the level for committing changes NML3Cfg manages one ifindex. In the future, we may want that multiple NMDevice and/or NMVpnConnection instances independently contribute their NML3ConfigData to the NML3Cfg instance. That means, at any time somebody may want to call nm_l3cfg_platform_commit() to apply the changes. Even ACD internally may do that, when configuration changes (e.g. an IP address passes ACD check). We thus need to know whether we are assuming, updating or reapplying the settings. Add API so users can register their "commit" preference. --- src/nm-l3cfg.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++-- src/nm-l3cfg.h | 28 +++++++++++- 2 files changed, 143 insertions(+), 4 deletions(-) diff --git a/src/nm-l3cfg.c b/src/nm-l3cfg.c index 3c8bb524e3..2086cd00e7 100644 --- a/src/nm-l3cfg.c +++ b/src/nm-l3cfg.c @@ -95,6 +95,11 @@ typedef struct { bool announcing_failed_is_retrying:1; } AcdData; +struct _NML3CfgCommitTypeHandle { + CList commit_type_lst; + NML3CfgCommitType commit_type; +}; + typedef struct { const NML3ConfigData *l3cd; NML3ConfigMergeFlags merge_flags; @@ -131,6 +136,8 @@ typedef struct _NML3CfgPrivate { GArray *l3_config_datas; const NML3ConfigData *combined_l3cd; + CList commit_type_lst_head; + GHashTable *routes_temporary_not_available_hash; GHashTable *externally_removed_objs_hash; @@ -231,6 +238,8 @@ static AcdData *_l3_acd_data_find (NML3Cfg *self, static NM_UTILS_ENUM2STR_DEFINE (_l3_cfg_commit_type_to_string, NML3CfgCommitType, + NM_UTILS_ENUM2STR (NM_L3_CFG_COMMIT_TYPE_AUTO, "auto"), + NM_UTILS_ENUM2STR (NM_L3_CFG_COMMIT_TYPE_NONE, "none"), NM_UTILS_ENUM2STR (NM_L3_CFG_COMMIT_TYPE_ASSUME, "assume"), NM_UTILS_ENUM2STR (NM_L3_CFG_COMMIT_TYPE_UPDATE, "update"), NM_UTILS_ENUM2STR (NM_L3_CFG_COMMIT_TYPE_REAPPLY, "reapply"), @@ -878,7 +887,7 @@ _l3_acd_platform_commit_acd_update (NML3Cfg *self) _LOGT ("acd: acd update now"); self->priv.changed_configs = TRUE; nm_l3cfg_platform_commit (self, - NM_L3_CFG_COMMIT_TYPE_UPDATE, + NM_L3_CFG_COMMIT_TYPE_AUTO, AF_INET, NULL); } @@ -2756,7 +2765,8 @@ _platform_commit (NML3Cfg *self, gboolean success = TRUE; nm_assert (NM_IS_L3CFG (self)); - nm_assert (NM_IN_SET (commit_type, NM_L3_CFG_COMMIT_TYPE_REAPPLY, + nm_assert (NM_IN_SET (commit_type, NM_L3_CFG_COMMIT_TYPE_NONE, + NM_L3_CFG_COMMIT_TYPE_REAPPLY, NM_L3_CFG_COMMIT_TYPE_UPDATE, NM_L3_CFG_COMMIT_TYPE_ASSUME)); nm_assert_addr_family (addr_family); @@ -2873,7 +2883,9 @@ nm_l3cfg_platform_commit (NML3Cfg *self, gboolean acd_was_pending; g_return_val_if_fail (NM_IS_L3CFG (self), FALSE); - nm_assert (NM_IN_SET (commit_type, NM_L3_CFG_COMMIT_TYPE_REAPPLY, + nm_assert (NM_IN_SET (commit_type, NM_L3_CFG_COMMIT_TYPE_AUTO, + NM_L3_CFG_COMMIT_TYPE_NONE, + NM_L3_CFG_COMMIT_TYPE_REAPPLY, NM_L3_CFG_COMMIT_TYPE_UPDATE, NM_L3_CFG_COMMIT_TYPE_ASSUME)); @@ -2884,6 +2896,9 @@ nm_l3cfg_platform_commit (NML3Cfg *self, if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) nm_clear_g_source_inst (&self->priv.p->acd_ready_on_idle_source); + if (commit_type == NM_L3_CFG_COMMIT_TYPE_AUTO) + commit_type = nm_l3cfg_commit_type_get (self); + if (commit_type == NM_L3_CFG_COMMIT_TYPE_REAPPLY) _l3cfg_externally_removed_objs_drop (self, addr_family); @@ -2908,6 +2923,101 @@ nm_l3cfg_platform_commit (NML3Cfg *self, /*****************************************************************************/ +NML3CfgCommitType +nm_l3cfg_commit_type_get (NML3Cfg *self) +{ + NML3CfgCommitTypeHandle *handle; + + nm_assert (NM_IS_L3CFG (self)); + + handle = c_list_first_entry (&self->priv.p->commit_type_lst_head, NML3CfgCommitTypeHandle, commit_type_lst); + return handle + ? handle->commit_type + : NM_L3_CFG_COMMIT_TYPE_NONE; +} + +/** + * nm_l3cfg_commit_type_register: + * @self: the #NML3Cfg + * @commit_type: the commit type to register + * @existing_handle: instead of being a new registration, update an existing handle. + * This may be %NULL, which is like having no previous registration. + * + * NML3Cfg needs to know whether it is in charge of an interface (and how "much"). + * By default, it is not in charge, but various users can register themself with + * a certain @commit_type. The "higher" commit type is the used one when calling + * nm_l3cfg_platform_commit() with %NM_L3_CFG_COMMIT_TYPE_AUTO. + * + * Returns: a handle tracking the registration, or %NULL of @commit_type + * is %NM_L3_CFG_COMMIT_TYPE_NONE. + */ +NML3CfgCommitTypeHandle * +nm_l3cfg_commit_type_register (NML3Cfg *self, + NML3CfgCommitType commit_type, + NML3CfgCommitTypeHandle *existing_handle) +{ + NML3CfgCommitTypeHandle *handle; + NML3CfgCommitTypeHandle *h; + gboolean linked; + + nm_assert (NM_IS_L3CFG (self)); + nm_assert (NM_IN_SET (commit_type, NM_L3_CFG_COMMIT_TYPE_NONE, + NM_L3_CFG_COMMIT_TYPE_ASSUME, + NM_L3_CFG_COMMIT_TYPE_UPDATE, + NM_L3_CFG_COMMIT_TYPE_REAPPLY)); + nm_assert ( !existing_handle + || c_list_contains (&self->priv.p->commit_type_lst_head, &existing_handle->commit_type_lst)); + + if (existing_handle) { + if (commit_type == NM_L3_CFG_COMMIT_TYPE_NONE) { + nm_l3cfg_commit_type_unregister (self, existing_handle); + return NULL; + } + if (existing_handle->commit_type == commit_type) + return existing_handle; + c_list_unlink_stale (&existing_handle->commit_type_lst); + handle = existing_handle; + } else { + if (commit_type == NM_L3_CFG_COMMIT_TYPE_NONE) + return NULL; + handle = g_slice_new (NML3CfgCommitTypeHandle); + handle->commit_type = commit_type; + if (c_list_is_empty (&self->priv.p->commit_type_lst_head)) + g_object_ref (self); + } + + linked = FALSE; + c_list_for_each_entry (h, &self->priv.p->commit_type_lst_head, commit_type_lst) { + if (handle->commit_type >= h->commit_type) { + c_list_link_before (&self->priv.p->commit_type_lst_head, &handle->commit_type_lst); + linked = TRUE; + } + } + if (!linked) + c_list_link_tail (&self->priv.p->commit_type_lst_head, &handle->commit_type_lst); + + return handle; +} + +void +nm_l3cfg_commit_type_unregister (NML3Cfg *self, + NML3CfgCommitTypeHandle *handle) +{ + nm_assert (NM_IS_L3CFG (self)); + + if (!handle) + return; + + nm_assert (c_list_contains (&self->priv.p->commit_type_lst_head, &handle->commit_type_lst)); + + c_list_unlink_stale (&handle->commit_type_lst); + if (c_list_is_empty (&self->priv.p->commit_type_lst_head)) + g_object_unref (self); + nm_g_slice_free (handle); +} + +/*****************************************************************************/ + static void set_property (GObject *object, guint prop_id, @@ -2941,6 +3051,7 @@ nm_l3cfg_init (NML3Cfg *self) self->priv.p = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_L3CFG, NML3CfgPrivate); c_list_init (&self->priv.p->acd_lst_head); + c_list_init (&self->priv.p->commit_type_lst_head); } static void @@ -2979,6 +3090,8 @@ finalize (GObject *object) { NML3Cfg *self = NM_L3CFG (object); + nm_assert (c_list_is_empty (&self->priv.p->commit_type_lst_head)); + nm_clear_g_source_inst (&self->priv.p->acd_ready_on_idle_source); nm_assert (nm_g_array_len (self->priv.p->property_emit_list) == 0u); diff --git a/src/nm-l3cfg.h b/src/nm-l3cfg.h index a199f27cfb..4a3bab1111 100644 --- a/src/nm-l3cfg.h +++ b/src/nm-l3cfg.h @@ -148,7 +148,18 @@ gboolean nm_l3cfg_remove_config_all (NML3Cfg *self, /*****************************************************************************/ -typedef enum { +/* The numeric values of the enum matters: higher number mean more "important". + * E.g. "assume" tries to preserve the most settings, while "reapply" forces + * all configuration to match. */ +typedef enum _nm_packed { + + /* the NML3Cfg instance tracks with nm_l3cfg_commit_setup_register() the requested commit type. + * Use _NM_L3_CFG_COMMIT_TYPE_AUTO to automatically choose the level as requested. */ + NM_L3_CFG_COMMIT_TYPE_AUTO, + + /* Don't touch the interface. */ + NM_L3_CFG_COMMIT_TYPE_NONE, + /* ASSUME means to keep any pre-existing extra routes/addresses, while * also not adding routes/addresses that are not present yet. This is to * gracefully take over after restart, where the existing IP configuration @@ -172,4 +183,19 @@ gboolean nm_l3cfg_platform_commit (NML3Cfg *self, int addr_family, gboolean *out_final_failure_for_temporary_not_available); +/*****************************************************************************/ + +NML3CfgCommitType nm_l3cfg_commit_type_get (NML3Cfg *self); + +typedef struct _NML3CfgCommitTypeHandle NML3CfgCommitTypeHandle; + +NML3CfgCommitTypeHandle *nm_l3cfg_commit_type_register (NML3Cfg *self, + NML3CfgCommitType commit_type, + NML3CfgCommitTypeHandle *existing_handle); + +void nm_l3cfg_commit_type_unregister (NML3Cfg *self, + NML3CfgCommitTypeHandle *handle); + +/*****************************************************************************/ + #endif /* __NM_L3CFG_H__ */ From 4038a8ff993f9fb30611d76a6937c6ff3e730966 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 4 Sep 2020 18:04:37 +0200 Subject: [PATCH 10/10] core: accept void pointer in nm_dbus_object_export() and related NMDBusObject is an abstract type which provides the glue code for exposing a GObject on D-Bus. We almost never use that type directly, so as it was before, we always had to use a C cast to convince the compiler that this is right. Being always required to cast is not very useful, nor more typesafe. Just use a void pointer instead. --- src/nm-dbus-object.c | 59 +++++++++++++++++++++++++------------------- src/nm-dbus-object.h | 6 ++--- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/nm-dbus-object.c b/src/nm-dbus-object.c index db793b9da3..6320073ebe 100644 --- a/src/nm-dbus-object.c +++ b/src/nm-dbus-object.c @@ -84,26 +84,27 @@ _create_export_path (NMDBusObjectClass *klass) * Returns: the path @self was exported under */ const char * -nm_dbus_object_export (NMDBusObject *self) +nm_dbus_object_export (gpointer /* (NMDBusObject *) */ self) { + NMDBusObject *self1 = self; static guint64 id_counter = 0; - g_return_val_if_fail (NM_IS_DBUS_OBJECT (self), NULL); + g_return_val_if_fail (NM_IS_DBUS_OBJECT (self1), NULL); - g_return_val_if_fail (!self->internal.path, self->internal.path); + g_return_val_if_fail (!self1->internal.path, self1->internal.path); - nm_assert (!self->internal.is_unexporting); + nm_assert (!self1->internal.is_unexporting); - self->internal.path = _create_export_path (NM_DBUS_OBJECT_GET_CLASS (self)); + self1->internal.path = _create_export_path (NM_DBUS_OBJECT_GET_CLASS (self1)); - self->internal.export_version_id = ++id_counter; + self1->internal.export_version_id = ++id_counter; - _LOGT ("export: \"%s\"", self->internal.path); + _LOGT ("export: \"%s\"", self1->internal.path); - _nm_dbus_manager_obj_export (self); + _nm_dbus_manager_obj_export (self1); - _emit_exported_changed (self); - return self->internal.path; + _emit_exported_changed (self1); + return self1->internal.path; } /** @@ -114,13 +115,15 @@ nm_dbus_object_export (NMDBusObject *self) * auto-exported on future connections). */ void -nm_dbus_object_unexport (NMDBusObject *self) +nm_dbus_object_unexport (gpointer /* (NMDBusObject *) */ self) { - g_return_if_fail (NM_IS_DBUS_OBJECT (self)); + NMDBusObject *self1 = self; - g_return_if_fail (self->internal.path); + g_return_if_fail (NM_IS_DBUS_OBJECT (self1)); - _LOGT ("unexport: \"%s\"", self->internal.path); + g_return_if_fail (self1->internal.path); + + _LOGT ("unexport: \"%s\"", self1->internal.path); /* note that we emit the signal *before* actually unexporting the object. * The reason is, that listeners want to use this signal to know that @@ -133,16 +136,16 @@ nm_dbus_object_unexport (NMDBusObject *self) * The inconvenient part is, that at this point nm_dbus_object_get_path() * still returns the path. So, the callee needs to handle that. Possibly * by using "nm_dbus_object_get_path_still_exported()". */ - self->internal.is_unexporting = TRUE; + self1->internal.is_unexporting = TRUE; - _emit_exported_changed (self); + _emit_exported_changed (self1); - _nm_dbus_manager_obj_unexport (self); + _nm_dbus_manager_obj_unexport (self1); - nm_clear_g_free (&self->internal.path); - self->internal.export_version_id = 0; + nm_clear_g_free (&self1->internal.path); + self1->internal.export_version_id = 0; - self->internal.is_unexporting = FALSE; + self1->internal.is_unexporting = FALSE; } static gboolean @@ -155,21 +158,27 @@ _unexport_on_idle_cb (gpointer user_data) } void -nm_dbus_object_unexport_on_idle (NMDBusObject *self_take) +nm_dbus_object_unexport_on_idle (gpointer /* (NMDBusObject *) */ self_take) { - g_return_if_fail (NM_IS_DBUS_OBJECT (self_take)); + NMDBusObject *self = g_steal_pointer (&self_take); - g_return_if_fail (self_take->internal.path); + if (!self) + return; + + g_return_if_fail (NM_IS_DBUS_OBJECT (self)); + + g_return_if_fail (self->internal.path); /* There is no mechanism to cancel or abort the unexport. It will always * gonna happen. * * However, we register it to block shutdown, so that we ensure that it will happen. */ - nm_shutdown_wait_obj_register_object (self_take, "unexport-dbus-obj-on-idle"); + nm_shutdown_wait_obj_register_object (self, "unexport-dbus-obj-on-idle"); + /* pass on ownership. */ g_idle_add (_unexport_on_idle_cb, - g_steal_pointer (&self_take)); + g_steal_pointer (&self)); } /*****************************************************************************/ diff --git a/src/nm-dbus-object.h b/src/nm-dbus-object.h index ab09465f6c..014f043df2 100644 --- a/src/nm-dbus-object.h +++ b/src/nm-dbus-object.h @@ -163,10 +163,10 @@ nm_dbus_object_get_path_still_exported (NMDBusObject *self) : self->internal.path; } -const char *nm_dbus_object_export (NMDBusObject *self); -void nm_dbus_object_unexport (NMDBusObject *self); +const char *nm_dbus_object_export (gpointer /* (NMDBusObject *) */ self); +void nm_dbus_object_unexport (gpointer /* (NMDBusObject *) */ self); -void nm_dbus_object_unexport_on_idle (NMDBusObject *self_take); +void nm_dbus_object_unexport_on_idle (gpointer /* (NMDBusObject *) */ self_take); void _nm_dbus_object_clear_and_unexport (NMDBusObject **location); #define nm_dbus_object_clear_and_unexport(location) _nm_dbus_object_clear_and_unexport ((NMDBusObject **) (location))