From b23d117079857879004bc88dac955e4767092839 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 28 Jul 2020 10:00:23 +0200 Subject: [PATCH 01/29] shared: use designated initializer in nm_dedup_multi_idx_type_init() --- shared/nm-glib-aux/nm-dedup-multi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/shared/nm-glib-aux/nm-dedup-multi.c b/shared/nm-glib-aux/nm-dedup-multi.c index 6e23228e37..4e61fd4143 100644 --- a/shared/nm-glib-aux/nm-dedup-multi.c +++ b/shared/nm-glib-aux/nm-dedup-multi.c @@ -54,9 +54,10 @@ nm_dedup_multi_idx_type_init (NMDedupMultiIdxType *idx_type, nm_assert (idx_type); nm_assert (klass); - memset (idx_type, 0, sizeof (*idx_type)); - idx_type->klass = klass; - c_list_init (&idx_type->lst_idx_head); + *idx_type = (NMDedupMultiIdxType) { + .klass = klass, + .lst_idx_head = C_LIST_INIT (idx_type->lst_idx_head), + }; ASSERT_idx_type (idx_type); } From 684b0ec0611a2b3bb7fbe274cdea61f05f1df87b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 24 Jul 2020 13:54:23 +0200 Subject: [PATCH 02/29] shared: add nm_g_ptr_array_pdata() helper --- shared/nm-glib-aux/nm-shared-utils.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index 2f6681cb70..b4470fceb2 100644 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -1547,6 +1547,12 @@ nm_g_ptr_array_len (const GPtrArray *arr) return arr ? arr->len : 0u; } +static inline gpointer * +nm_g_ptr_array_pdata (const GPtrArray *arr) +{ + return arr ? arr->pdata : NULL; +} + GPtrArray *_nm_g_ptr_array_copy (GPtrArray *array, GCopyFunc func, gpointer user_data, From d550eef02d8ff9a94cb57ba90cd0edeebc6e3f1f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 24 Jul 2020 13:54:49 +0200 Subject: [PATCH 03/29] shared: add nm_strv_ptrarray_cmp() helper --- shared/nm-glib-aux/nm-shared-utils.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index b4470fceb2..bff62b3226 100644 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -1879,6 +1879,20 @@ nm_strv_ptrarray_contains (const GPtrArray *strv, return nm_strv_ptrarray_find_first (strv, str) >= 0; } +static inline int +nm_strv_ptrarray_cmp (const GPtrArray *a, + const GPtrArray *b) +{ + /* _nm_utils_strv_cmp_n() will treat NULL and empty arrays the same. + * That means, an empty strv array can both be represented by NULL + * and an array of length zero. + * If you need to distinguish between these case, do that yourself. */ + return _nm_utils_strv_cmp_n ((const char *const*) nm_g_ptr_array_pdata (a), + nm_g_ptr_array_len (a), + (const char *const*) nm_g_ptr_array_pdata (b), + nm_g_ptr_array_len (b)); +} + /*****************************************************************************/ int nm_utils_getpagesize (void); From 62c1a1b5b238f27e589ef45c648e5229e19224ad Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 27 Jul 2020 09:50:26 +0200 Subject: [PATCH 04/29] shared: move nm_utils_is_power_of_two() to nm-stdaux and add nm_utils_is_power_of_two_or_zero() --- libnm-core/tests/test-general.c | 13 ++++++++++++- shared/nm-glib-aux/nm-macros-internal.h | 10 ---------- shared/nm-std-aux/nm-std-aux.h | 21 +++++++++++++++++++++ 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 1e4efe2452..73d68f8a62 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -7290,8 +7290,16 @@ enum TEST_IS_POWER_OF_TWP_ENUM_UNSIGNED_64 { G_STMT_START { \ typeof (x) x1 = (x); \ type x2 = (type) x1; \ + gboolean val; \ \ - g_assert_cmpint (expect, ==, nm_utils_is_power_of_two (x1)); \ + val = nm_utils_is_power_of_two (x1); \ + g_assert_cmpint (expect, ==, val); \ + if (x1 != 0) \ + g_assert_cmpint (val, ==, nm_utils_is_power_of_two_or_zero (x1)); \ + else { \ + g_assert (nm_utils_is_power_of_two_or_zero (x1)); \ + g_assert (!val); \ + } \ if ( ((typeof (x1)) x2) == x1 \ && ((typeof (x2)) x1) == x2 \ && x2 > 0) { \ @@ -7318,6 +7326,9 @@ test_nm_utils_is_power_of_two (void) GRand *rand = nmtst_get_rand (); int numbits; + g_assert (!nm_utils_is_power_of_two (0)); + g_assert (nm_utils_is_power_of_two_or_zero (0)); + for (i = -1; i < 64; i++) { /* find a (positive) x which is a power of two. */ diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h index a8a5360548..551cd92f16 100644 --- a/shared/nm-glib-aux/nm-macros-internal.h +++ b/shared/nm-glib-aux/nm-macros-internal.h @@ -1017,16 +1017,6 @@ nm_g_variant_take_ref (GVariant *v) /*****************************************************************************/ -/* Determine whether @x is a power of two (@x being an integer type). - * Basically, this returns TRUE, if @x has exactly one bit set. - * For negative values and zero, this always returns FALSE. */ -#define nm_utils_is_power_of_two(x) ({ \ - typeof(x) __x = (x); \ - \ - ( (__x > ((typeof(__x)) 0)) \ - && ((__x & (__x - (((typeof(__x)) 1)))) == ((typeof(__x)) 0))); \ - }) - #define NM_DIV_ROUND_UP(x, y) \ ({ \ const typeof(x) _x = (x); \ diff --git a/shared/nm-std-aux/nm-std-aux.h b/shared/nm-std-aux/nm-std-aux.h index 01409cefbb..b3f80bbd65 100644 --- a/shared/nm-std-aux/nm-std-aux.h +++ b/shared/nm-std-aux/nm-std-aux.h @@ -193,6 +193,27 @@ ((_A) > (_B)) ? (_A) : (_B), \ ((void) 0))) +/* Determine whether @x is a power of two (@x being an integer type). + * Basically, this returns TRUE, if @x has exactly one bit set. + * For negative values and zero, this always returns FALSE. */ +#define nm_utils_is_power_of_two(x) \ + ({ \ + typeof(x) _x2 = (x); \ + const typeof(_x2) _X_0 = ((typeof(_x2)) 0); \ + const typeof(_x2) _X_1 = ((typeof(_x2)) 1); \ + \ + ( (_x2 > _X_0) \ + && ((_x2 & (_x2 - _X_1)) == _X_0)); \ + }) + +#define nm_utils_is_power_of_two_or_zero(x) \ + ({ \ + typeof (x) _x1 = (x); \ + \ + ( (_x1 == 0) \ + || nm_utils_is_power_of_two (_x1)); \ + }) + /*****************************************************************************/ #define NM_SWAP(a, b) \ From 443859f71fb134e2896cfd8dfac31bfc912867db Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 27 Jul 2020 15:17:54 +0200 Subject: [PATCH 05/29] shared: add NM_CMP_DIRECT_UNSAFE() macro --- shared/nm-glib-aux/nm-shared-utils.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index bff62b3226..d44321d34e 100644 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -262,6 +262,12 @@ gboolean nm_utils_ipaddr_is_normalized (int addr_family, return (_a < _b) ? -1 : 1; \ } G_STMT_END +#define NM_CMP_DIRECT_UNSAFE(a, b) \ + G_STMT_START { \ + if ((a) != (b)) \ + return ((a) < (b)) ? -1 : 1; \ + } G_STMT_END + /* In the general case, direct pointer comparison is undefined behavior in C. * Avoid that by casting pointers to void* and then to uintptr_t. This comparison * is not really meaningful, except that it provides some kind of stable sort order From 1e3f5b7c017133a9763f19c97b8b17f3b60bf6a0 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 24 Jul 2020 21:30:22 +0200 Subject: [PATCH 06/29] platform: add NMP_OBJECT_TYPE_IP_ADDRESS()/NMP_OBJECT_TYPE_IP_ROUTE() macros --- src/nm-types.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/nm-types.h b/src/nm-types.h index 6277e4d811..9624bfa877 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -205,10 +205,15 @@ nm_link_type_supports_slaves (NMLinkType link_type) typedef enum { NMP_OBJECT_TYPE_UNKNOWN, NMP_OBJECT_TYPE_LINK, + +#define NMP_OBJECT_TYPE_IP_ADDRESS(is_ipv4) ((is_ipv4) ? NMP_OBJECT_TYPE_IP4_ADDRESS : NMP_OBJECT_TYPE_IP6_ADDRESS) NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS, + +#define NMP_OBJECT_TYPE_IP_ROUTE(is_ipv4) ((is_ipv4) ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE) NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE, + NMP_OBJECT_TYPE_ROUTING_RULE, NMP_OBJECT_TYPE_QDISC, From 7eb94b7dccc02d5500b9d87c9bd6c00fb8f2eac9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 28 Jul 2020 08:09:12 +0200 Subject: [PATCH 07/29] platform: add nm_platform_ip_route_get_gateway() 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 1a2e339100..3b28c4ffa3 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -1848,6 +1848,18 @@ gboolean nm_platform_ip_address_flush (NMPlatform *self, void nm_platform_ip_route_normalize (int addr_family, NMPlatformIPRoute *route); +static inline gconstpointer +nm_platform_ip_route_get_gateway (int addr_family, + const NMPlatformIPRoute *route) +{ + nm_assert_addr_family (addr_family); + nm_assert (route); + + if (NM_IS_IPv4 (addr_family)) + return &((NMPlatformIP4Route *) route)->gateway; + return &((NMPlatformIP6Route *) route)->gateway; +} + int nm_platform_ip_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPObject *route); From 14fa487a60acc7761c818c6f09a38c1593d740ba Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 28 Jul 2020 09:09:44 +0200 Subject: [PATCH 08/29] platform: add static assertion to check NMPlatformIP{Address,Route} struct layout --- src/platform/tests/test-platform-general.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/platform/tests/test-platform-general.c b/src/platform/tests/test-platform-general.c index 86ffdbedcf..9a59a7de9f 100644 --- a/src/platform/tests/test-platform-general.c +++ b/src/platform/tests/test-platform-general.c @@ -14,6 +14,14 @@ /*****************************************************************************/ +G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Address, address)); +G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Address, address)); + +G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Route, network)); +G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Route, network)); + +/*****************************************************************************/ + static void test_init_linux_platform (void) { From 1a4cc800f6d7619fc222be6dfc3650ceea7e45e5 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 28 Jul 2020 13:29:07 +0200 Subject: [PATCH 09/29] platform: add nmp_object_indirect_id_*() helpers --- src/platform/nmp-object.c | 19 +++++++++++++++++++ src/platform/nmp-object.h | 3 +++ 2 files changed, 22 insertions(+) diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 1cd61a8e7d..8f32b11007 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -1570,6 +1570,25 @@ _vt_cmd_plobj_hash_update_routing_rule (const NMPlatformObject *obj, NMHashState return nm_platform_routing_rule_hash_update ((const NMPlatformRoutingRule *) obj, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL, h); } +guint +nmp_object_indirect_id_hash (gconstpointer a) +{ + const NMPObject *const*p_obj = a; + + return nmp_object_id_hash (*p_obj); +} + +gboolean +nmp_object_indirect_id_equal (gconstpointer a, gconstpointer b) +{ + const NMPObject *const*p_obj_a = a; + const NMPObject *const*p_obj_b = b; + + return nmp_object_id_equal (*p_obj_a, *p_obj_b); +} + +/*****************************************************************************/ + gboolean nmp_object_is_alive (const NMPObject *obj) { diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index c31548cb18..388ea9dd5e 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -674,6 +674,9 @@ nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2) return nmp_object_id_cmp (obj1, obj2) == 0; } +guint nmp_object_indirect_id_hash (gconstpointer a); +gboolean nmp_object_indirect_id_equal (gconstpointer a, gconstpointer b); + gboolean nmp_object_is_alive (const NMPObject *obj); gboolean nmp_object_is_visible (const NMPObject *obj); From 63b788dad2f079118d77ba6bf00a89173026cfed Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 28 Jul 2020 08:25:28 +0200 Subject: [PATCH 10/29] core: add nm_utils_ip_address_same_prefix_cmp() helper --- src/nm-core-utils.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 5d36bd593c..e7211038de 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -113,6 +113,22 @@ nm_utils_ip4_address_same_prefix_cmp (in_addr_t addr_a, in_addr_t addr_b, guint8 int nm_utils_ip6_address_same_prefix_cmp (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen); +static inline int +nm_utils_ip_address_same_prefix_cmp (int addr_family, gconstpointer addr_a, gconstpointer addr_b, guint8 plen) +{ + nm_assert_addr_family (addr_family); + + NM_CMP_SELF (addr_a, addr_b); + + if (NM_IS_IPv4 (addr_family)) { + return nm_utils_ip4_address_same_prefix_cmp (*((const in_addr_t *) addr_a), + *((const in_addr_t *) addr_b), + plen); + } + + return nm_utils_ip6_address_same_prefix_cmp (addr_a, addr_b, plen); +} + static inline gboolean nm_utils_ip4_address_same_prefix (in_addr_t addr_a, in_addr_t addr_b, guint8 plen) { @@ -125,6 +141,12 @@ nm_utils_ip6_address_same_prefix (const struct in6_addr *addr_a, const struct in return nm_utils_ip6_address_same_prefix_cmp (addr_a, addr_b, plen) == 0; } +static inline gboolean +nm_utils_ip_address_same_prefix (int addr_family, gconstpointer addr_a, gconstpointer addr_b, guint8 plen) +{ + return nm_utils_ip_address_same_prefix_cmp (addr_family, addr_a, addr_b, plen) == 0; +} + #define NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a, b, plen) \ NM_CMP_RETURN (nm_utils_ip4_address_same_prefix_cmp ((a), (b), (plen))) From 7670899d2c73b8072996e57282cfbf119b3e6022 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 28 Jul 2020 09:16:28 +0200 Subject: [PATCH 11/29] core: add nm_utils_ip4_address_is_zeronet() helper --- src/nm-core-utils.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index e7211038de..8b5e69916d 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -479,6 +479,13 @@ nm_utils_ip4_address_is_link_local (in_addr_t addr) return (addr & NM_IPV4LL_NETMASK) == NM_IPV4LL_NETWORK; } +static inline gboolean +nm_utils_ip4_address_is_zeronet (in_addr_t network) +{ + /* Same as ipv4_is_zeronet() from kernel's include/linux/in.h. */ + return (network & htonl (0xFF000000u)) == htonl (0x00000000u); +} + /*****************************************************************************/ const char *nm_utils_dnsmasq_status_to_string (int status, char *dest, gsize size); From d61bb9b97c7c4e3d0ad6e9867016446ea785f247 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 28 Jul 2020 09:16:37 +0200 Subject: [PATCH 12/29] core: use nm_utils_ip4_address_is_zeronet() helper --- src/nm-ip4-config.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 4a9026cdb3..9e080fa405 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -343,15 +343,6 @@ nm_ip4_config_get_multi_idx (const NMIP4Config *self) /*****************************************************************************/ -static gboolean -_ipv4_is_zeronet (in_addr_t network) -{ - /* Same as ipv4_is_zeronet() from kernel's include/linux/in.h. */ - return (network & htonl(0xff000000)) == htonl(0x00000000); -} - -/*****************************************************************************/ - const NMDedupMultiHeadEntry * nm_ip4_config_lookup_addresses (const NMIP4Config *self) { @@ -686,7 +677,7 @@ nm_ip4_config_add_dependent_routes (NMIP4Config *self, _add_local_route_from_addr4 (self, my_addr, ifindex, route_table, is_vrf); - if (_ipv4_is_zeronet (network)) { + if (nm_utils_ip4_address_is_zeronet (network)) { /* Kernel doesn't add device-routes for destinations that * start with 0.x.y.z. Skip them. */ continue; From 8225785956d2f809eed9e10fd5b5c11e30fb1699 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 27 Jul 2020 15:52:21 +0200 Subject: [PATCH 13/29] device: use nm_utils_ip4_address_is_link_local() in nm_device_handle_ipv4ll_event() --- src/devices/nm-device.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 02b1c548eb..03a1a59db6 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -7747,9 +7747,6 @@ ipv4ll_get_ip4_config (NMDevice *self, guint32 lla) return config; } -#define IPV4LL_NETWORK (htonl (0xA9FE0000L)) -#define IPV4LL_NETMASK (htonl (0xFFFF0000L)) - static void nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) { @@ -7774,7 +7771,7 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) return; } - if ((address.s_addr & IPV4LL_NETMASK) != IPV4LL_NETWORK) { + if (!nm_utils_ip4_address_is_link_local (address.s_addr)) { _LOGE (LOGD_AUTOIP4, "invalid address %08x received (not link-local).", address.s_addr); nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_ERROR); return; From ba9c15028694ff84781b1bbab2dbaa3a706ef444 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 27 Jul 2020 16:28:02 +0200 Subject: [PATCH 14/29] core: inline _add_local_route_from_addr[46] helper function In this case, the functions are only called once. Having a helper function that has no clear, unique purpose does not necessarily make the code simpler. Also, NMIP[46]Config is going to change completely. It will thereby move this code (and change it). Doing that is simpler, if we see all the relevant parts in one place. --- src/nm-ip4-config.c | 38 ++++++++---------------- src/nm-ip6-config.c | 72 ++++++++++++++++++--------------------------- 2 files changed, 42 insertions(+), 68 deletions(-) diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 9e080fa405..a31b24c84f 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -614,30 +614,6 @@ nm_ip4_config_update_routes_metric (NMIP4Config *self, gint64 metric) g_object_thaw_notify (G_OBJECT (self)); } -static void -_add_local_route_from_addr4 (NMIP4Config * self, - const NMPlatformIP4Address *addr, - int ifindex, - guint32 route_table, - gboolean is_vrf) -{ - nm_auto_nmpobj NMPObject *r = NULL; - NMPlatformIP4Route *route; - - r = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, NULL); - route = NMP_OBJECT_CAST_IP4_ROUTE (r); - route->ifindex = ifindex; - route->rt_source = NM_IP_CONFIG_SOURCE_KERNEL; - route->network = addr->address; - route->plen = 32; - route->pref_src = addr->address; - route->type_coerced = nm_platform_route_type_coerce (RTN_LOCAL); - route->scope_inv = nm_platform_route_scope_inv (RT_SCOPE_HOST); - route->table_coerced = nm_platform_route_table_coerce (is_vrf ? route_table : RT_TABLE_LOCAL); - - _add_route (self, r, NULL, NULL); -} - void nm_ip4_config_add_dependent_routes (NMIP4Config *self, guint32 route_table, @@ -675,7 +651,19 @@ nm_ip4_config_add_dependent_routes (NMIP4Config *self, if (my_addr->external) continue; - _add_local_route_from_addr4 (self, my_addr, ifindex, route_table, is_vrf); + /* Pre-generate local route added by kernel */ + r = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, NULL); + route = NMP_OBJECT_CAST_IP4_ROUTE (r); + route->ifindex = ifindex; + route->rt_source = NM_IP_CONFIG_SOURCE_KERNEL; + route->network = my_addr->address; + route->plen = 32; + route->pref_src = my_addr->address; + route->type_coerced = nm_platform_route_type_coerce (RTN_LOCAL); + route->scope_inv = nm_platform_route_scope_inv (RT_SCOPE_HOST); + route->table_coerced = nm_platform_route_table_coerce (is_vrf ? route_table : RT_TABLE_LOCAL); + _add_route (self, r, NULL, NULL); + nm_clear_pointer (&r, nmp_object_unref); if (nm_utils_ip4_address_is_zeronet (network)) { /* Kernel doesn't add device-routes for destinations that diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 00865734c5..cf7aa59f0d 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -364,46 +364,6 @@ nm_ip6_config_update_routes_metric (NMIP6Config *self, gint64 metric) g_object_thaw_notify (G_OBJECT (self)); } -static void -_add_multicast_route6 (NMIP6Config *self, int ifindex) -{ - nm_auto_nmpobj NMPObject *r = NULL; - NMPlatformIP6Route *route; - - r = nmp_object_new (NMP_OBJECT_TYPE_IP6_ROUTE, NULL); - route = NMP_OBJECT_CAST_IP6_ROUTE (r); - route->ifindex = ifindex; - route->network.s6_addr[0] = 0xffu; - route->plen = 8; - route->table_coerced = nm_platform_route_table_coerce (RT_TABLE_LOCAL); - route->type_coerced = nm_platform_route_type_coerce (RTN_UNICAST); - route->metric = 256; - - _add_route (self, r, NULL, NULL); -} - -static void -_add_local_route_from_addr6 (NMIP6Config * self, - const NMPlatformIP6Address *addr, - int ifindex, - guint32 route_table, - gboolean is_vrf) -{ - nm_auto_nmpobj NMPObject *r = NULL; - NMPlatformIP6Route * route; - - r = nmp_object_new (NMP_OBJECT_TYPE_IP6_ROUTE, NULL); - route = NMP_OBJECT_CAST_IP6_ROUTE (r); - route->ifindex = ifindex; - route->network = addr->address; - route->plen = 128; - route->type_coerced = nm_platform_route_type_coerce (RTN_LOCAL); - route->metric = 0; - route->table_coerced = nm_platform_route_table_coerce (is_vrf ? route_table : RT_TABLE_LOCAL); - - _add_route (self, r, NULL, NULL); -} - void nm_ip6_config_add_dependent_routes (NMIP6Config *self, guint32 route_table, @@ -426,7 +386,21 @@ nm_ip6_config_add_dependent_routes (NMIP6Config *self, * For manually added IPv6 routes, add the device routes explicitly. */ /* Pre-generate multicast route */ - _add_multicast_route6 (self, ifindex); + { + nm_auto_nmpobj NMPObject *r = NULL; + NMPlatformIP6Route *route; + + r = nmp_object_new (NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + route = NMP_OBJECT_CAST_IP6_ROUTE (r); + route->ifindex = ifindex; + route->network.s6_addr[0] = 0xffu; + route->plen = 8; + route->table_coerced = nm_platform_route_table_coerce (RT_TABLE_LOCAL); + route->type_coerced = nm_platform_route_type_coerce (RTN_UNICAST); + route->metric = 256; + + _add_route (self, r, NULL, NULL); + } nm_ip_config_iter_ip6_address_for_each (&iter, self, &my_addr) { NMPlatformIP6Route *route; @@ -436,8 +410,20 @@ nm_ip6_config_add_dependent_routes (NMIP6Config *self, if (my_addr->external) continue; - /* Pre-generate local route added by kernel */ - _add_local_route_from_addr6 (self, my_addr, ifindex, route_table, is_vrf); + { + nm_auto_nmpobj NMPObject *r = NULL; + + /* Pre-generate local route added by kernel */ + r = nmp_object_new (NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + route = NMP_OBJECT_CAST_IP6_ROUTE (r); + route->ifindex = ifindex; + route->network = my_addr->address; + route->plen = 128; + route->type_coerced = nm_platform_route_type_coerce (RTN_LOCAL); + route->metric = 0; + route->table_coerced = nm_platform_route_table_coerce (is_vrf ? route_table : RT_TABLE_LOCAL); + _add_route (self, r, NULL, NULL); + } if (NM_FLAGS_HAS (my_addr->n_ifa_flags, IFA_F_NOPREFIXROUTE)) continue; From 30a8b1c71308980f45be4350025f7a1acee89e8a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Jul 2020 08:38:41 +0200 Subject: [PATCH 15/29] platform: add NMP_OBJECT_TYPE_TO_ADDR_FAMILY() helper --- src/platform/nmp-object.c | 3 +++ src/platform/nmp-object.h | 17 +++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 8f32b11007..615c458ab7 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -3046,6 +3046,9 @@ ASSERT_nmp_cache_is_consistent (const NMPCache *cache) /*****************************************************************************/ +/* below, ensure that addr_family get's automatically initialize to AF_UNSPEC. */ +G_STATIC_ASSERT (AF_UNSPEC == 0); + const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { [NMP_OBJECT_TYPE_LINK - 1] = { .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 388ea9dd5e..bf4ff42379 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -556,19 +556,16 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMPObjectType obj_type) #define NMP_OBJECT_CAST_TFILTER(obj) _NMP_OBJECT_CAST (obj, tfilter, NMP_OBJECT_TYPE_TFILTER) #define NMP_OBJECT_CAST_LNK_WIREGUARD(obj) _NMP_OBJECT_CAST (obj, lnk_wireguard, NMP_OBJECT_TYPE_LNK_WIREGUARD) +static inline int +NMP_OBJECT_TYPE_TO_ADDR_FAMILY (NMPObjectType obj_type) +{ + return nmp_class_from_type (obj_type)->addr_family; +} + static inline int NMP_OBJECT_GET_ADDR_FAMILY (const NMPObject *obj) { - switch (NMP_OBJECT_GET_TYPE (obj)) { - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP4_ROUTE: - return AF_INET; - case NMP_OBJECT_TYPE_IP6_ADDRESS: - case NMP_OBJECT_TYPE_IP6_ROUTE: - return AF_INET6; - default: - return AF_UNSPEC; - } + return NMP_OBJECT_GET_CLASS (obj)->addr_family; } static inline const NMPObject * From dca62c06fdef989480d7b2222487875fb835e740 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Jul 2020 12:46:21 +0200 Subject: [PATCH 16/29] platform: merge nm_platform_ip_address_sync() implementations It is beneficial to have both address families side by side. A lot of operations are exactly the same, so it's preferable to see that. Especially in the cases where they differ, it's preferable to see how they differ (and why). --- src/platform/nm-platform.c | 517 +++++++++++++++++-------------------- src/platform/nm-platform.h | 17 +- 2 files changed, 255 insertions(+), 279 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 7b1f87f189..94ad209cb0 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3743,168 +3743,6 @@ ip4_addr_subnets_is_secondary (const NMPObject *address, return FALSE; } -/** - * nm_platform_ip4_address_sync: - * @self: platform instance - * @ifindex: Interface index - * @known_addresses: List of addresses. The list will be modified and only - * addresses that were successfully added will be kept in the list. - * That means, expired addresses and addresses that could not be added - * will be dropped. - * Hence, the input argument @known_addresses is also an output argument - * telling which addresses were successfully added. - * Addresses are removed by unrefing the instance via nmp_object_unref() - * and leaving a NULL tombstone. - * - * A convenience function to synchronize addresses for a specific interface - * with the least possible disturbance. It simply removes addresses that are - * not listed and adds addresses that are. - * - * Returns: %TRUE on success. - */ -gboolean -nm_platform_ip4_address_sync (NMPlatform *self, - int ifindex, - GPtrArray *known_addresses) -{ - gs_unref_ptrarray GPtrArray *plat_addresses = NULL; - const NMPlatformIP4Address *known_address; - gint32 now = nm_utils_get_monotonic_timestamp_sec (); - GHashTable *plat_subnets = NULL; - GHashTable *known_subnets = NULL; - gs_unref_hashtable GHashTable *known_addresses_idx = NULL; - guint i, j, len; - NMPLookup lookup; - guint32 lifetime, preferred; - guint32 ifa_flags; - - _CHECK_SELF (self, klass, FALSE); - - if (!_addr_array_clean_expired (AF_INET, ifindex, known_addresses, now, &known_addresses_idx)) - known_addresses = NULL; - - plat_addresses = nm_platform_lookup_clone (self, - nmp_lookup_init_object (&lookup, - NMP_OBJECT_TYPE_IP4_ADDRESS, - ifindex), - NULL, NULL); - if (plat_addresses) - plat_subnets = ip4_addr_subnets_build_index (plat_addresses, TRUE, TRUE); - - /* Delete unknown addresses */ - len = plat_addresses ? plat_addresses->len : 0; - for (i = 0; i < len; i++) { - const NMPObject *plat_obj; - const NMPlatformIP4Address *plat_address; - const GPtrArray *addr_list; - - plat_obj = plat_addresses->pdata[i]; - if (!plat_obj) { - /* Already deleted */ - continue; - } - - plat_address = NMP_OBJECT_CAST_IP4_ADDRESS (plat_obj); - - if (known_addresses) { - const NMPObject *o; - - o = g_hash_table_lookup (known_addresses_idx, plat_obj); - if (o) { - gboolean secondary; - - if (!known_subnets) - known_subnets = ip4_addr_subnets_build_index (known_addresses, FALSE, FALSE); - - secondary = ip4_addr_subnets_is_secondary (o, known_subnets, known_addresses, NULL); - if (secondary == NM_FLAGS_HAS (plat_address->n_ifa_flags, IFA_F_SECONDARY)) { - /* if we have an existing known-address, with matching secondary role, - * do not delete the platform-address. */ - continue; - } - } - } - - nm_platform_ip4_address_delete (self, ifindex, - plat_address->address, - plat_address->plen, - plat_address->peer_address); - - if ( !ip4_addr_subnets_is_secondary (plat_obj, plat_subnets, plat_addresses, &addr_list) - && addr_list) { - /* If we just deleted a primary addresses and there were - * secondary ones the kernel can do two things, depending on - * version and sysctl setting: delete also secondary addresses - * or promote a secondary to primary. Ensure that secondary - * addresses are deleted, so that we can start with a clean - * slate and add addresses in the right order. */ - for (j = 1; j < addr_list->len; j++) { - const NMPObject **o; - - o = ip4_addr_subnets_addr_list_get (addr_list, j); - nm_assert (o); - - if (*o) { - const NMPlatformIP4Address *a; - - a = NMP_OBJECT_CAST_IP4_ADDRESS (*o); - nm_platform_ip4_address_delete (self, ifindex, - a->address, - a->plen, - a->peer_address); - nmp_object_unref (*o); - *o = NULL; - } - } - } - } - ip4_addr_subnets_destroy_index (plat_subnets, plat_addresses); - - if (!known_addresses) - return TRUE; - - ip4_addr_subnets_destroy_index (known_subnets, known_addresses); - - ifa_flags = nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS) - ? IFA_F_NOPREFIXROUTE - : 0; - - /* Add missing addresses */ - for (i = 0; i < known_addresses->len; i++) { - const NMPObject *o; - - o = known_addresses->pdata[i]; - if (!o) - continue; - - known_address = NMP_OBJECT_CAST_IP4_ADDRESS (o); - - lifetime = nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, - now, &preferred); - if (!lifetime) - goto delete_and_next2; - - if (!nm_platform_ip4_address_add (self, - ifindex, - known_address->address, - known_address->plen, - known_address->peer_address, - nm_platform_ip4_broadcast_address_from_addr (known_address), - lifetime, - preferred, - ifa_flags, - known_address->label)) - goto delete_and_next2; - - continue; -delete_and_next2: - nmp_object_unref (o); - known_addresses->pdata[i] = NULL; - } - - return TRUE; -} - typedef enum { IP6_ADDR_SCOPE_LOOPBACK, IP6_ADDR_SCOPE_LINKLOCAL, @@ -3941,8 +3779,9 @@ ip6_address_scope_cmp (gconstpointer p_a, gconstpointer p_b, gpointer increasing } /** - * nm_platform_ip6_address_sync: + * nm_platform_ip_address_sync: * @self: platform instance + * @addr_family: the address family AF_INET or AF_INET6. * @ifindex: Interface index * @known_addresses: List of addresses. The list will be modified and only * addresses that were successfully added will be kept in the list. @@ -3952,7 +3791,7 @@ ip6_address_scope_cmp (gconstpointer p_a, gconstpointer p_b, gpointer increasing * telling which addresses were successfully added. * Addresses are removed by unrefing the instance via nmp_object_unref() * and leaving a NULL tombstone. - * @full_sync: Also remove link-local and temporary addresses. + * @full_sync: Also remove link-local and temporary addresses (IPv6 only). * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -3961,152 +3800,241 @@ ip6_address_scope_cmp (gconstpointer p_a, gconstpointer p_b, gpointer increasing * Returns: %TRUE on success. */ gboolean -nm_platform_ip6_address_sync (NMPlatform *self, - int ifindex, - GPtrArray *known_addresses, - gboolean full_sync) +nm_platform_ip_address_sync (NMPlatform *self, + int addr_family, + int ifindex, + GPtrArray *known_addresses, + gboolean full_sync) { + const gint32 now = nm_utils_get_monotonic_timestamp_sec (); + const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); gs_unref_ptrarray GPtrArray *plat_addresses = NULL; - gint32 now = nm_utils_get_monotonic_timestamp_sec (); - guint i_plat, i_know; gs_unref_hashtable GHashTable *known_addresses_idx = NULL; + GHashTable *known_subnets = NULL; NMPLookup lookup; guint32 ifa_flags; + guint i_plat; + guint i_know; + guint i; + guint j; + + _CHECK_SELF (self, klass, FALSE); /* The order we want to enforce is only among addresses with the same * scope, as the kernel keeps addresses sorted by scope. Therefore, * apply the same sorting to known addresses, so that we don't try to * unnecessary change the order of addresses with different scopes. */ - if (known_addresses) - g_ptr_array_sort_with_data (known_addresses, ip6_address_scope_cmp, GINT_TO_POINTER (TRUE)); + if (!IS_IPv4) { + if (known_addresses) + g_ptr_array_sort_with_data (known_addresses, ip6_address_scope_cmp, GINT_TO_POINTER (TRUE)); + } - if (!_addr_array_clean_expired (AF_INET6, ifindex, known_addresses, now, &known_addresses_idx)) + if (!_addr_array_clean_expired (addr_family, ifindex, known_addresses, now, &known_addresses_idx)) known_addresses = NULL; /* @plat_addresses is in decreasing priority order (highest priority addresses first), contrary to * @known_addresses which is in increasing priority order (lowest priority addresses first). */ plat_addresses = nm_platform_lookup_clone (self, nmp_lookup_init_object (&lookup, - NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP_ADDRESS (IS_IPv4), ifindex), NULL, NULL); if (plat_addresses) { - guint known_addresses_len; - IP6AddrScope cur_scope; - gboolean delete_remaining_addrs; + /* Delete unknown addresses */ + if (IS_IPv4) { + GHashTable *plat_subnets; - g_ptr_array_sort_with_data (plat_addresses, ip6_address_scope_cmp, GINT_TO_POINTER (FALSE)); + plat_subnets = ip4_addr_subnets_build_index (plat_addresses, TRUE, TRUE); - known_addresses_len = known_addresses ? known_addresses->len : 0; + for (i = 0; i < plat_addresses->len; i++) { + const NMPObject *plat_obj; + const NMPlatformIP4Address *plat_address; + const GPtrArray *addr_list; - /* First, compare every address whether it is still a "known address", that is, whether - * to keep it or to delete it. - * - * If we don't find a matching valid address in @known_addresses, we will delete - * plat_addr. - * - * Certain addresses, like temporary addresses, are ignored by this function - * if not run with full_sync. These addresses are usually not managed by NetworkManager - * directly, or at least, they are not managed via nm_platform_ip6_address_sync(). - * Only in full_sync mode, we really want to get rid of them (usually, when we take - * the interface down). - * - * Note that we mark handled addresses by setting it to %NULL in @plat_addresses array. */ - for (i_plat = 0; i_plat < plat_addresses->len; i_plat++) { - const NMPObject *plat_obj = plat_addresses->pdata[i_plat]; - const NMPObject *know_obj; - const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_obj); - - if (NM_FLAGS_HAS (plat_addr->n_ifa_flags, IFA_F_TEMPORARY)) { - if (!full_sync) { - /* just mark as handled, without actually deleting the address. */ - goto clear_and_next; - } - } else if (known_addresses_idx) { - know_obj = g_hash_table_lookup (known_addresses_idx, plat_obj); - if ( know_obj - && plat_addr->plen == NMP_OBJECT_CAST_IP6_ADDRESS (know_obj)->plen) { - /* technically, plen is not part of the ID for IPv6 addresses and thus - * @plat_addr is essentially the same address as @know_addr (regrading - * its identity, not its other attributes). - * However, we cannot modify an existing addresses' plen without - * removing and readding it. Thus, only keep plat_addr, if the plen - * matches. - * - * keep this one, and continue */ + plat_obj = plat_addresses->pdata[i]; + if (!plat_obj) { + /* Already deleted */ continue; } - } - nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen); -clear_and_next: - nmp_object_unref (g_steal_pointer (&plat_addresses->pdata[i_plat])); - } + plat_address = NMP_OBJECT_CAST_IP4_ADDRESS (plat_obj); - /* Next, we must preserve the priority of the routes. That is, source address - * selection will choose addresses in the order as they are reported by kernel. - * Note that the order in @plat_addresses of the remaining matches is highest - * priority first. - * We need to compare this to the order of addresses with same scope in - * @known_addresses (which has lowest priority first). - * - * If we find a first discrepancy, we need to delete all remaining addresses - * with same scope from that point on, because below we must re-add all the - * addresses in the right order to get their priority right. */ - cur_scope = IP6_ADDR_SCOPE_LOOPBACK; - delete_remaining_addrs = FALSE; - i_plat = plat_addresses->len; - i_know = 0; - while (i_plat > 0) { - const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[--i_plat]); - IP6AddrScope plat_scope; + if (known_addresses) { + const NMPObject *o; - if (!plat_addr) - continue; + o = g_hash_table_lookup (known_addresses_idx, plat_obj); + if (o) { + gboolean secondary; - plat_scope = ip6_address_scope (plat_addr); - if (cur_scope != plat_scope) { - nm_assert (cur_scope < plat_scope); - delete_remaining_addrs = FALSE; - cur_scope = plat_scope; - } + if (!known_subnets) + known_subnets = ip4_addr_subnets_build_index (known_addresses, FALSE, FALSE); - if (!delete_remaining_addrs) { - delete_remaining_addrs = TRUE; - for (; i_know < known_addresses_len; i_know++) { - const NMPlatformIP6Address *know_addr = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i_know]); - IP6AddrScope know_scope; - - if (!know_addr) - continue; - - know_scope = ip6_address_scope (know_addr); - if (know_scope < plat_scope) - continue; - - if (IN6_ARE_ADDR_EQUAL (&plat_addr->address, &know_addr->address)) { - /* we have a match. Mark address as handled. */ - i_know++; - delete_remaining_addrs = FALSE; - goto next_plat; + secondary = ip4_addr_subnets_is_secondary (o, known_subnets, known_addresses, NULL); + if (secondary == NM_FLAGS_HAS (plat_address->n_ifa_flags, IFA_F_SECONDARY)) { + /* if we have an existing known-address, with matching secondary role, + * do not delete the platform-address. */ + continue; + } } + } - /* plat_address has no match. Now delete_remaining_addrs is TRUE and we will - * delete all the remaining addresses with cur_scope. */ - break; + nm_platform_ip4_address_delete (self, + ifindex, + plat_address->address, + plat_address->plen, + plat_address->peer_address); + + if ( !ip4_addr_subnets_is_secondary (plat_obj, plat_subnets, plat_addresses, &addr_list) + && addr_list) { + /* If we just deleted a primary addresses and there were + * secondary ones the kernel can do two things, depending on + * version and sysctl setting: delete also secondary addresses + * or promote a secondary to primary. Ensure that secondary + * addresses are deleted, so that we can start with a clean + * slate and add addresses in the right order. */ + for (j = 1; j < addr_list->len; j++) { + const NMPObject **o; + + o = ip4_addr_subnets_addr_list_get (addr_list, j); + nm_assert (o); + + if (*o) { + const NMPlatformIP4Address *a; + + a = NMP_OBJECT_CAST_IP4_ADDRESS (*o); + nm_platform_ip4_address_delete (self, + ifindex, + a->address, + a->plen, + a->peer_address); + nmp_object_unref (*o); + *o = NULL; + } + } } } + ip4_addr_subnets_destroy_index (plat_subnets, plat_addresses); + } else { + guint known_addresses_len; + IP6AddrScope cur_scope; + gboolean delete_remaining_addrs; - nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen); + g_ptr_array_sort_with_data (plat_addresses, ip6_address_scope_cmp, GINT_TO_POINTER (FALSE)); + + known_addresses_len = known_addresses ? known_addresses->len : 0; + + /* First, compare every address whether it is still a "known address", that is, whether + * to keep it or to delete it. + * + * If we don't find a matching valid address in @known_addresses, we will delete + * plat_addr. + * + * Certain addresses, like temporary addresses, are ignored by this function + * if not run with full_sync. These addresses are usually not managed by NetworkManager + * directly, or at least, they are not managed via nm_platform_ip6_address_sync(). + * Only in full_sync mode, we really want to get rid of them (usually, when we take + * the interface down). + * + * Note that we mark handled addresses by setting it to %NULL in @plat_addresses array. */ + for (i_plat = 0; i_plat < plat_addresses->len; i_plat++) { + const NMPObject *plat_obj = plat_addresses->pdata[i_plat]; + const NMPObject *know_obj; + const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_obj); + + if (NM_FLAGS_HAS (plat_addr->n_ifa_flags, IFA_F_TEMPORARY)) { + if (!full_sync) { + /* just mark as handled, without actually deleting the address. */ + goto clear_and_next; + } + } else if (known_addresses_idx) { + know_obj = g_hash_table_lookup (known_addresses_idx, plat_obj); + if ( know_obj + && plat_addr->plen == NMP_OBJECT_CAST_IP6_ADDRESS (know_obj)->plen) { + /* technically, plen is not part of the ID for IPv6 addresses and thus + * @plat_addr is essentially the same address as @know_addr (regrading + * its identity, not its other attributes). + * However, we cannot modify an existing addresses' plen without + * removing and readding it. Thus, only keep plat_addr, if the plen + * matches. + * + * keep this one, and continue */ + continue; + } + } + + nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen); +clear_and_next: + nmp_object_unref (g_steal_pointer (&plat_addresses->pdata[i_plat])); + } + + /* Next, we must preserve the priority of the routes. That is, source address + * selection will choose addresses in the order as they are reported by kernel. + * Note that the order in @plat_addresses of the remaining matches is highest + * priority first. + * We need to compare this to the order of addresses with same scope in + * @known_addresses (which has lowest priority first). + * + * If we find a first discrepancy, we need to delete all remaining addresses + * with same scope from that point on, because below we must re-add all the + * addresses in the right order to get their priority right. */ + cur_scope = IP6_ADDR_SCOPE_LOOPBACK; + delete_remaining_addrs = FALSE; + i_plat = plat_addresses->len; + i_know = 0; + while (i_plat > 0) { + const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[--i_plat]); + IP6AddrScope plat_scope; + + if (!plat_addr) + continue; + + plat_scope = ip6_address_scope (plat_addr); + if (cur_scope != plat_scope) { + nm_assert (cur_scope < plat_scope); + delete_remaining_addrs = FALSE; + cur_scope = plat_scope; + } + + if (!delete_remaining_addrs) { + delete_remaining_addrs = TRUE; + for (; i_know < known_addresses_len; i_know++) { + const NMPlatformIP6Address *know_addr = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i_know]); + IP6AddrScope know_scope; + + if (!know_addr) + continue; + + know_scope = ip6_address_scope (know_addr); + if (know_scope < plat_scope) + continue; + + if (IN6_ARE_ADDR_EQUAL (&plat_addr->address, &know_addr->address)) { + /* we have a match. Mark address as handled. */ + i_know++; + delete_remaining_addrs = FALSE; + goto next_plat; + } + + /* plat_address has no match. Now delete_remaining_addrs is TRUE and we will + * delete all the remaining addresses with cur_scope. */ + break; + } + } + + nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen); next_plat: - ; + ; + } } } if (!known_addresses) return TRUE; + if (IS_IPv4) + ip4_addr_subnets_destroy_index (known_subnets, known_addresses); + ifa_flags = nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS) ? IFA_F_NOPREFIXROUTE : 0; @@ -4115,20 +4043,55 @@ next_plat: * priority. */ for (i_know = 0; i_know < known_addresses->len; i_know++) { - const NMPlatformIP6Address *known_address = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i_know]); - guint32 lifetime, preferred; + const NMPlatformIPXAddress *known_address; + const NMPObject *o; + guint32 lifetime; + guint32 preferred; - if (!known_address) + o = known_addresses->pdata[i_know]; + if (!o) continue; - lifetime = nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred, - now, &preferred); + nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_TYPE_IP_ADDRESS (IS_IPv4)); - if (!nm_platform_ip6_address_add (self, ifindex, known_address->address, - known_address->plen, known_address->peer_address, - lifetime, preferred, - ifa_flags | known_address->n_ifa_flags)) - return FALSE; + known_address = NMP_OBJECT_CAST_IPX_ADDRESS (o); + + lifetime = nm_utils_lifetime_get (known_address->ax.timestamp, + known_address->ax.lifetime, + known_address->ax.preferred, + now, + &preferred); + if (IS_IPv4) { + if (lifetime <= 0) + goto delete_and_next2; + if (!nm_platform_ip4_address_add (self, + ifindex, + known_address->a4.address, + known_address->a4.plen, + known_address->a4.peer_address, + nm_platform_ip4_broadcast_address_from_addr (&known_address->a4), + lifetime, + preferred, + ifa_flags, + known_address->a4.label)) + goto delete_and_next2; + } else { + if (!nm_platform_ip6_address_add (self, + ifindex, + known_address->a6.address, + known_address->a6.plen, + known_address->a6.peer_address, + lifetime, + preferred, + ifa_flags + | known_address->a6.n_ifa_flags)) + return FALSE; + } + continue; + +delete_and_next2: + nmp_object_unref (o); + known_addresses->pdata[i_know] = NULL; } return TRUE; diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 3b28c4ffa3..8247814140 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -1839,8 +1839,21 @@ gboolean nm_platform_ip6_address_add (NMPlatform *self, guint32 flags); gboolean nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); gboolean nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen); -gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresses); -gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresses, gboolean full_sync); + +gboolean nm_platform_ip_address_sync (NMPlatform *self, int addr_family, int ifindex, GPtrArray *known_addresses, gboolean full_sync); + +static inline gboolean +nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresses) +{ + return nm_platform_ip_address_sync (self, AF_INET, ifindex, known_addresses, TRUE); +} + +static inline gboolean +nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresses, gboolean full_sync) +{ + return nm_platform_ip_address_sync (self, AF_INET6, ifindex, known_addresses, full_sync); +} + gboolean nm_platform_ip_address_flush (NMPlatform *self, int addr_family, int ifindex); From 0acb5773202e6e962b7b80512bb2e2f3e82873cb Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Jul 2020 13:30:08 +0200 Subject: [PATCH 17/29] platform: minor cleanup in nm_platform_ip_address_sync() The known address must not have a zero lifetime, because we filter those addresses out first. --- src/platform/nm-platform.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 94ad209cb0..4415aa84c1 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3577,8 +3577,11 @@ _addr_array_clean_expired (int addr_family, int ifindex, GPtrArray *array, guint goto clear_and_next; } - if (!nm_utils_lifetime_get (a->timestamp, a->lifetime, a->preferred, - now, NULL)) + if (!nm_utils_lifetime_get (a->timestamp, + a->lifetime, + a->preferred, + now, + NULL)) goto clear_and_next; if (idx) { @@ -4061,9 +4064,9 @@ next_plat: known_address->ax.preferred, now, &preferred); + nm_assert (lifetime > 0); + if (IS_IPv4) { - if (lifetime <= 0) - goto delete_and_next2; if (!nm_platform_ip4_address_add (self, ifindex, known_address->a4.address, @@ -4073,8 +4076,9 @@ next_plat: lifetime, preferred, ifa_flags, - known_address->a4.label)) - goto delete_and_next2; + known_address->a4.label)) { + /* ignore error, for unclear reasons. */ + } } else { if (!nm_platform_ip6_address_add (self, ifindex, @@ -4087,11 +4091,6 @@ next_plat: | known_address->a6.n_ifa_flags)) return FALSE; } - continue; - -delete_and_next2: - nmp_object_unref (o); - known_addresses->pdata[i_know] = NULL; } return TRUE; From dadfc3abd5103c61bd84b421bead3c5664007240 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Jul 2020 13:49:31 +0200 Subject: [PATCH 18/29] platform: allow injecting the list of addresses to prune Follow the pattern of nm_platform_ip_route_sync(), which also accepts the list of addresses that are potential candidates for removal. This allows the caller to carefully construct the list of addresses which are possibly removed, so that sync (possibly) only adds new addresses. --- src/platform/nm-platform.c | 40 ++++++++++++++++++++++++++++---------- src/platform/nm-platform.h | 26 ++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 4415aa84c1..25fa732cb1 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3794,7 +3794,16 @@ ip6_address_scope_cmp (gconstpointer p_a, gconstpointer p_b, gpointer increasing * telling which addresses were successfully added. * Addresses are removed by unrefing the instance via nmp_object_unref() * and leaving a NULL tombstone. - * @full_sync: Also remove link-local and temporary addresses (IPv6 only). + * @addresses_prune: (allow-none): the list of addresses to delete. + * If platform has such an address configured, it will be deleted + * at the beginning of the sync. Note that the array will be modified + * by the function. + * Note that the addresses must be properly sorted, by their priority. + * Create this list with nm_platform_ip_address_get_prune_list() which + * gets the sorting right. + * @full_sync: Also remove temporary addresses (IPv6 only). + * In general, only addresses from @addresses_prune will be removed. Setting + * this to FALSE (for IPv6) limits that some addresses won't be removed. * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -3807,14 +3816,14 @@ nm_platform_ip_address_sync (NMPlatform *self, int addr_family, int ifindex, GPtrArray *known_addresses, + GPtrArray *addresses_prune, gboolean full_sync) { const gint32 now = nm_utils_get_monotonic_timestamp_sec (); const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); - gs_unref_ptrarray GPtrArray *plat_addresses = NULL; gs_unref_hashtable GHashTable *known_addresses_idx = NULL; + GPtrArray *plat_addresses; GHashTable *known_subnets = NULL; - NMPLookup lookup; guint32 ifa_flags; guint i_plat; guint i_know; @@ -3835,15 +3844,11 @@ nm_platform_ip_address_sync (NMPlatform *self, if (!_addr_array_clean_expired (addr_family, ifindex, known_addresses, now, &known_addresses_idx)) known_addresses = NULL; - /* @plat_addresses is in decreasing priority order (highest priority addresses first), contrary to + /* @plat_addresses must be sorted in decreasing priority order (highest priority addresses first), contrary to * @known_addresses which is in increasing priority order (lowest priority addresses first). */ - plat_addresses = nm_platform_lookup_clone (self, - nmp_lookup_init_object (&lookup, - NMP_OBJECT_TYPE_IP_ADDRESS (IS_IPv4), - ifindex), - NULL, NULL); + plat_addresses = addresses_prune; - if (plat_addresses) { + if (nm_g_ptr_array_len (plat_addresses) > 0) { /* Delete unknown addresses */ if (IS_IPv4) { GHashTable *plat_subnets; @@ -4154,6 +4159,21 @@ _err_inval_due_to_ipv6_tentative_pref_src (NMPlatform *self, const NMPObject *ob return TRUE; } +GPtrArray * +nm_platform_ip_address_get_prune_list (NMPlatform *self, + int addr_family, + int ifindex) +{ + NMPLookup lookup; + + return nm_platform_lookup_clone (self, + nmp_lookup_init_object (&lookup, + NMP_OBJECT_TYPE_IP_ADDRESS (NM_IS_IPv4 (addr_family)), + ifindex), + NULL, + NULL); +} + GPtrArray * nm_platform_ip_route_get_prune_list (NMPlatform *self, int addr_family, diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 8247814140..a571960c03 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -1840,18 +1840,38 @@ gboolean nm_platform_ip6_address_add (NMPlatform *self, gboolean nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); gboolean nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen); -gboolean nm_platform_ip_address_sync (NMPlatform *self, int addr_family, int ifindex, GPtrArray *known_addresses, gboolean full_sync); +gboolean nm_platform_ip_address_sync (NMPlatform *self, + int addr_family, + int ifindex, + GPtrArray *known_addresses, + GPtrArray *addresses_prune, + gboolean full_sync); + +GPtrArray *nm_platform_ip_address_get_prune_list (NMPlatform *self, + int addr_family, + int ifindex); + +static inline gboolean +_nm_platform_ip_address_sync (NMPlatform *self, int addr_family, int ifindex, GPtrArray *known_addresses, gboolean full_sync) +{ + gs_unref_ptrarray GPtrArray *addresses_prune = NULL; + + addresses_prune = nm_platform_ip_address_get_prune_list (self, + addr_family, + ifindex); + return nm_platform_ip_address_sync (self, addr_family, ifindex, known_addresses, addresses_prune, TRUE); +} static inline gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresses) { - return nm_platform_ip_address_sync (self, AF_INET, ifindex, known_addresses, TRUE); + return _nm_platform_ip_address_sync (self, AF_INET, ifindex, known_addresses, TRUE); } static inline gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresses, gboolean full_sync) { - return nm_platform_ip_address_sync (self, AF_INET6, ifindex, known_addresses, full_sync); + return _nm_platform_ip_address_sync (self, AF_INET6, ifindex, known_addresses, full_sync); } gboolean nm_platform_ip_address_flush (NMPlatform *self, From 5ccfa3856593692b6258440cbb20556a003d4cb1 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Jul 2020 14:06:56 +0200 Subject: [PATCH 19/29] platform: move full-sync parameter away from nm_platform_ip_address_sync() This parameter really affects whether a candidate in @addresses_prune will be considered or not. Since we already construct the prune list separately, this parameter is at the wrong place. This requires us to re-implement nm_platform_lookup_clone(). While the function has a predicate callback that we could use for this purpose, I will later add a separate predicate argument to nm_platform_ip_address_get_prune_list(). When that happens, it would be cumbersome to chain the two function pointers. Instead, reimplement nm_platform_lookup_clone(). --- src/platform/nm-platform.c | 56 +++++++++++++++++++++++++------------- src/platform/nm-platform.h | 11 ++++---- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 25fa732cb1..f50d272746 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3801,9 +3801,6 @@ ip6_address_scope_cmp (gconstpointer p_a, gconstpointer p_b, gpointer increasing * Note that the addresses must be properly sorted, by their priority. * Create this list with nm_platform_ip_address_get_prune_list() which * gets the sorting right. - * @full_sync: Also remove temporary addresses (IPv6 only). - * In general, only addresses from @addresses_prune will be removed. Setting - * this to FALSE (for IPv6) limits that some addresses won't be removed. * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -3816,8 +3813,7 @@ nm_platform_ip_address_sync (NMPlatform *self, int addr_family, int ifindex, GPtrArray *known_addresses, - GPtrArray *addresses_prune, - gboolean full_sync) + GPtrArray *addresses_prune) { const gint32 now = nm_utils_get_monotonic_timestamp_sec (); const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); @@ -3950,12 +3946,7 @@ nm_platform_ip_address_sync (NMPlatform *self, const NMPObject *know_obj; const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_obj); - if (NM_FLAGS_HAS (plat_addr->n_ifa_flags, IFA_F_TEMPORARY)) { - if (!full_sync) { - /* just mark as handled, without actually deleting the address. */ - goto clear_and_next; - } - } else if (known_addresses_idx) { + if (known_addresses_idx) { know_obj = g_hash_table_lookup (known_addresses_idx, plat_obj); if ( know_obj && plat_addr->plen == NMP_OBJECT_CAST_IP6_ADDRESS (know_obj)->plen) { @@ -3972,7 +3963,6 @@ nm_platform_ip_address_sync (NMPlatform *self, } nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen); -clear_and_next: nmp_object_unref (g_steal_pointer (&plat_addresses->pdata[i_plat])); } @@ -4162,16 +4152,44 @@ _err_inval_due_to_ipv6_tentative_pref_src (NMPlatform *self, const NMPObject *ob GPtrArray * nm_platform_ip_address_get_prune_list (NMPlatform *self, int addr_family, - int ifindex) + int ifindex, + gboolean exclude_ipv6_temporary_addrs) { + const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); + const NMDedupMultiHeadEntry *head_entry; NMPLookup lookup; + GPtrArray *result; + CList *iter; - return nm_platform_lookup_clone (self, - nmp_lookup_init_object (&lookup, - NMP_OBJECT_TYPE_IP_ADDRESS (NM_IS_IPv4 (addr_family)), - ifindex), - NULL, - NULL); + nmp_lookup_init_object (&lookup, + NMP_OBJECT_TYPE_IP_ADDRESS (NM_IS_IPv4 (addr_family)), + ifindex); + + head_entry = nm_platform_lookup (self, &lookup); + + if (!head_entry) + return NULL; + + result = g_ptr_array_new_full (head_entry->len, + (GDestroyNotify) nmp_object_unref); + + c_list_for_each (iter, &head_entry->lst_entries_head) { + const NMPObject *obj = c_list_entry (iter, NMDedupMultiEntry, lst_entries)->obj; + + if (!IS_IPv4) { + if ( exclude_ipv6_temporary_addrs + && NM_FLAGS_HAS (NMP_OBJECT_CAST_IP_ADDRESS (obj)->n_ifa_flags, IFA_F_TEMPORARY)) + continue; + } + + g_ptr_array_add (result, (gpointer) nmp_object_ref (obj)); + } + + if (result->len == 0) { + g_ptr_array_unref (result); + return NULL; + } + return result; } GPtrArray * diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index a571960c03..84dcfbb4aa 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -1844,12 +1844,12 @@ gboolean nm_platform_ip_address_sync (NMPlatform *self, int addr_family, int ifindex, GPtrArray *known_addresses, - GPtrArray *addresses_prune, - gboolean full_sync); + GPtrArray *addresses_prune); GPtrArray *nm_platform_ip_address_get_prune_list (NMPlatform *self, int addr_family, - int ifindex); + int ifindex, + gboolean exclude_ipv6_temporary_addrs); static inline gboolean _nm_platform_ip_address_sync (NMPlatform *self, int addr_family, int ifindex, GPtrArray *known_addresses, gboolean full_sync) @@ -1858,8 +1858,9 @@ _nm_platform_ip_address_sync (NMPlatform *self, int addr_family, int ifindex, GP addresses_prune = nm_platform_ip_address_get_prune_list (self, addr_family, - ifindex); - return nm_platform_ip_address_sync (self, addr_family, ifindex, known_addresses, addresses_prune, TRUE); + ifindex, + !full_sync); + return nm_platform_ip_address_sync (self, addr_family, ifindex, known_addresses, addresses_prune); } static inline gboolean From e3e60bf87de9ee325cbf17460ccc4d8f3cd965f1 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 24 Jul 2020 13:16:33 +0200 Subject: [PATCH 20/29] l3cfg: add NM_IS_L3_CONFIG_DATA() for asserting valid NML3ConfigData instances --- src/nm-l3-config-data.c | 42 ++++++++++++++++++++--------------------- src/nm-l3-config-data.h | 21 +++++++++++++++++---- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index 23d2dd0fae..b591934ec0 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -216,7 +216,7 @@ _route_valid (int addr_family, gconstpointer r) } static gboolean -NM_IS_L3_CONFIG_DATA (const NML3ConfigData *self, gboolean allow_sealed) +_NM_IS_L3_CONFIG_DATA (const NML3ConfigData *self, gboolean allow_sealed) { nm_assert ( !self || ( self->ifindex > 0 @@ -288,7 +288,7 @@ nm_l3_config_data_new (NMDedupMultiIndex *multi_idx, const NML3ConfigData * nm_l3_config_data_ref (const NML3ConfigData *self) { - nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); ((NML3ConfigData *) self)->ref_count++; return self; } @@ -296,7 +296,7 @@ nm_l3_config_data_ref (const NML3ConfigData *self) const NML3ConfigData * nm_l3_config_data_ref_and_seal (const NML3ConfigData *self) { - nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); ((NML3ConfigData *) self)->is_sealed = TRUE; ((NML3ConfigData *) self)->ref_count++; return self; @@ -305,7 +305,7 @@ nm_l3_config_data_ref_and_seal (const NML3ConfigData *self) const NML3ConfigData * nm_l3_config_data_seal (const NML3ConfigData *self) { - nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); ((NML3ConfigData *) self)->is_sealed = TRUE; return self; } @@ -313,7 +313,7 @@ nm_l3_config_data_seal (const NML3ConfigData *self) gboolean nm_l3_config_data_is_sealed (const NML3ConfigData *self) { - nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); return self->is_sealed; } @@ -325,7 +325,7 @@ nm_l3_config_data_unref (const NML3ConfigData *self) if (!self) return; - nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); /* NML3ConfigData aims to be an immutable, ref-counted type. The mode of operation * is to create/initialize the instance once, then seal it and pass around the reference. @@ -372,7 +372,7 @@ nm_l3_config_data_lookup_objs (const NML3ConfigData *self, NMPObjectType obj_typ { const DedupMultiIdxType *idx; - nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); switch (obj_type) { case NMP_OBJECT_TYPE_IP4_ADDRESS: @@ -400,7 +400,7 @@ nm_l3_config_data_lookup_objs (const NML3ConfigData *self, NMPObjectType obj_typ int nm_l3_config_data_get_ifindex (const NML3ConfigData *self) { - nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); return self->ifindex; } @@ -410,7 +410,7 @@ nm_l3_config_data_get_ifindex (const NML3ConfigData *self) NML3ConfigDatFlags nm_l3_config_data_get_flags (const NML3ConfigData *self) { - nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); return self->flags; } @@ -420,7 +420,7 @@ nm_l3_config_data_set_flags_full (NML3ConfigData *self, NML3ConfigDatFlags flags, NML3ConfigDatFlags mask) { - nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); nm_assert (!NM_FLAGS_ANY (flags, ~mask)); self->flags = (self->flags & ~mask) @@ -617,7 +617,7 @@ nm_l3_config_data_add_address (NML3ConfigData *self, const NMPObject *new; gboolean changed; - nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); nm_assert_addr_family (addr_family); nm_assert ((!pl_new) != (!obj_new)); nm_assert ( !obj_new @@ -660,7 +660,7 @@ nm_l3_config_data_add_route (NML3ConfigData *self, gboolean changed = FALSE; gboolean changed_best_default_route = FALSE; - nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); nm_assert_addr_family (addr_family); nm_assert ((!pl_new) != (!obj_new)); nm_assert ( !pl_new @@ -736,7 +736,7 @@ nm_l3_config_data_add_nameserver (NML3ConfigData *self, int addr_family, gconstpointer /* (const NMIPAddr *) */ nameserver) { - nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); nm_assert_addr_family (addr_family); nm_assert (nameserver); @@ -749,7 +749,7 @@ gboolean nm_l3_config_data_add_wins (NML3ConfigData *self, in_addr_t wins) { - nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); return _garray_inaddr_add (&self->wins_4, AF_INET, @@ -761,7 +761,7 @@ nm_l3_config_data_add_domain (NML3ConfigData *self, int addr_family, const char *domain) { - nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); nm_assert_addr_family (addr_family); return _check_and_add_domain (&self->domains_x[NM_IS_IPv4 (addr_family)], domain); @@ -772,7 +772,7 @@ nm_l3_config_data_add_search (NML3ConfigData *self, int addr_family, const char *search) { - nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); nm_assert_addr_family (addr_family); return _check_and_add_domain (&self->searches_x[NM_IS_IPv4 (addr_family)], search); @@ -785,7 +785,7 @@ nm_l3_config_data_add_dns_option (NML3ConfigData *self, { GPtrArray **p_arr; - nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); nm_assert_addr_family (addr_family); g_return_val_if_fail (dns_option, FALSE); @@ -810,7 +810,7 @@ nm_l3_config_data_set_dns_priority (NML3ConfigData *self, { int *p_val; - nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); nm_assert_addr_family (addr_family); p_val = &self->dns_priority_x[NM_IS_IPv4 (addr_family)]; @@ -841,7 +841,7 @@ _init_from_connection_ip (NML3ConfigData *self, guint i; int idx; - nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); nm_assert_addr_family (addr_family); nm_assert (!connection || NM_IS_CONNECTION (connection)); @@ -1061,7 +1061,7 @@ _init_from_platform (NML3ConfigData *self, const NMPObject *plobj = NULL; NMDedupMultiIter iter; - nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); nm_assert_addr_family (addr_family); head_entry = nm_platform_lookup_object (platform, @@ -1134,7 +1134,7 @@ nm_l3_config_data_new_clone (const NML3ConfigData *src, NMDedupMultiIter iter; const NMPObject *obj; - nm_assert (NM_IS_L3_CONFIG_DATA (src, TRUE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (src, TRUE)); /* pass 0, to use the original ifindex. You can also use this function to * copy the configuration for a different ifindex. */ diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index 5b81b57e19..2677d69255 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -51,6 +51,23 @@ NML3ConfigData *nm_l3_config_data_new_from_platform (NMDedupMultiIndex *multi_id /*****************************************************************************/ +int nm_l3_config_data_get_ifindex (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. + * This check here is really only useful for assertions, and there it is + * enough to check whether the pointer is not NULL. + * + * Additionally, also call nm_l3_config_data_get_ifindex(), which does more + * checks during nm_assert(). */ + nm_assert (nm_l3_config_data_get_ifindex (self) > 0); + return !!self; +} + +/*****************************************************************************/ + const NMDedupMultiHeadEntry *nm_l3_config_data_lookup_objs (const NML3ConfigData *self, NMPObjectType obj_type); static inline const NMDedupMultiHeadEntry * @@ -102,10 +119,6 @@ nm_l3_config_data_lookup_routes (const NML3ConfigData *self, int addr_family) /*****************************************************************************/ -int nm_l3_config_data_get_ifindex (const NML3ConfigData *self); - -/*****************************************************************************/ - NML3ConfigDatFlags nm_l3_config_data_get_flags (const NML3ConfigData *self); void nm_l3_config_data_set_flags_full (NML3ConfigData *self, From 98e311899de24683209ac8dc3b659380c4e70569 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 24 Jul 2020 14:01:27 +0200 Subject: [PATCH 21/29] l3cfg: add nm_l3_config_data_cmp()/nm_l3_config_data_equal() --- src/nm-l3-config-data.c | 98 +++++++++++++++++++++++++++++++++++++++++ src/nm-l3-config-data.h | 10 +++++ 2 files changed, 108 insertions(+) diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index b591934ec0..c522101c80 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -183,6 +183,20 @@ _garray_inaddr_add (GArray **p_arr, return TRUE; } +static int +_garray_inaddr_cmp (const GArray *a, const GArray *b, int addr_family) +{ + guint l; + + l = nm_g_array_len (a); + NM_CMP_DIRECT (l, nm_g_array_len (b)); + + if (l > 0) + NM_CMP_DIRECT_MEMCMP (a->data, b->data, l * nm_utils_addr_family_to_size (addr_family)); + + return 0; +} + /*****************************************************************************/ static gboolean @@ -823,6 +837,90 @@ nm_l3_config_data_set_dns_priority (NML3ConfigData *self, /*****************************************************************************/ +static int +_dedup_multi_index_cmp (const NML3ConfigData *a, + const NML3ConfigData *b, + NMPObjectType obj_type) +{ + const NMDedupMultiHeadEntry *h_a = nm_l3_config_data_lookup_objs (a, obj_type); + const NMDedupMultiHeadEntry *h_b = nm_l3_config_data_lookup_objs (b, obj_type); + NMDedupMultiIter iter_a; + NMDedupMultiIter iter_b; + + NM_CMP_SELF (h_a, h_b); + NM_CMP_DIRECT (h_a->len, h_b->len); + + nm_assert (h_a->len > 0); + + nm_dedup_multi_iter_init (&iter_a, h_a); + nm_dedup_multi_iter_init (&iter_b, h_b); + + while (TRUE) { + const NMPObject *obj_a; + const NMPObject *obj_b; + gboolean have_a; + gboolean have_b; + + have_a = nm_platform_dedup_multi_iter_next_obj (&iter_a, &obj_a, obj_type); + if (!have_a) { + nm_assert (!nm_platform_dedup_multi_iter_next_obj (&iter_b, &obj_b, obj_type)); + break; + } + + have_b = nm_platform_dedup_multi_iter_next_obj (&iter_b, &obj_b, obj_type); + nm_assert (have_b); + + NM_CMP_RETURN (nmp_object_cmp (obj_a, obj_b)); + } + + return 0; +} + +int +nm_l3_config_data_cmp (const NML3ConfigData *a, const NML3ConfigData *b) +{ + int IS_IPv4; + + NM_CMP_SELF (a, b); + + NM_CMP_DIRECT (a->ifindex, b->ifindex); + + _dedup_multi_index_cmp (a, b, NMP_OBJECT_TYPE_IP4_ADDRESS); + _dedup_multi_index_cmp (a, b, NMP_OBJECT_TYPE_IP6_ADDRESS); + _dedup_multi_index_cmp (a, b, NMP_OBJECT_TYPE_IP4_ROUTE); + _dedup_multi_index_cmp (a, b, NMP_OBJECT_TYPE_IP6_ROUTE); + + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + const int addr_family = IS_IPv4 ? AF_INET : AF_INET6; + + NM_CMP_RETURN (nmp_object_cmp (a->best_default_route_x[IS_IPv4], b->best_default_route_x[IS_IPv4])); + + NM_CMP_RETURN (_garray_inaddr_cmp (a->nameservers_x[IS_IPv4], b->nameservers_x[IS_IPv4], addr_family)); + + NM_CMP_RETURN (nm_strv_ptrarray_cmp (a->domains_x[IS_IPv4], b->domains_x[IS_IPv4])); + NM_CMP_RETURN (nm_strv_ptrarray_cmp (a->searches_x[IS_IPv4], b->searches_x[IS_IPv4])); + NM_CMP_RETURN (nm_strv_ptrarray_cmp (a->dns_options_x[IS_IPv4], b->dns_options_x[IS_IPv4])); + + NM_CMP_DIRECT (a->dns_priority_x[IS_IPv4], b->dns_priority_x[IS_IPv4]); + } + + NM_CMP_RETURN (_garray_inaddr_cmp (a->wins_4, b->wins_4, AF_INET)); + NM_CMP_DIRECT (a->mdns, b->mdns); + NM_CMP_DIRECT (a->llmnr, b->llmnr); + NM_CMP_DIRECT (a->flags, b->flags); + + /* these fields are not considered by cmp(): + * + * - multi_idx + * - ref_count + * - is_sealed + */ + + return 0; +} + +/*****************************************************************************/ + static void _init_from_connection_ip (NML3ConfigData *self, int addr_family, diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index 2677d69255..ee00634f43 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -68,6 +68,16 @@ NM_IS_L3_CONFIG_DATA (const NML3ConfigData *self) /*****************************************************************************/ +int nm_l3_config_data_cmp (const NML3ConfigData *a, const NML3ConfigData *b); + +static inline gboolean +nm_l3_config_data_equal (const NML3ConfigData *a, const NML3ConfigData *b) +{ + return nm_l3_config_data_cmp (a, b) == 0; +} + +/*****************************************************************************/ + const NMDedupMultiHeadEntry *nm_l3_config_data_lookup_objs (const NML3ConfigData *self, NMPObjectType obj_type); static inline const NMDedupMultiHeadEntry * From bfc77c89d1870580f2b313b2fdcc0942d8cde255 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 24 Jul 2020 16:49:24 +0200 Subject: [PATCH 22/29] l3cfg: add flags for affecting how add-object works for NML3ConfigData We need a way to say don't touch the existing object during add. Add flags for that. --- src/nm-l3-config-data.c | 67 +++++++++++++++++---------------- src/nm-l3-config-data.h | 82 +++++++++++++++++++++++++++++++++-------- 2 files changed, 102 insertions(+), 47 deletions(-) diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index c522101c80..abb48b76b2 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -449,8 +449,7 @@ _l3_config_data_add_obj (NMDedupMultiIndex *multi_idx, int ifindex, const NMPObject *obj_new, const NMPlatformObject *pl_new, - gboolean merge, - gboolean append_force, + NML3ConfigAddFlags add_flags, const NMPObject **out_obj_old /* returns a reference! */, const NMPObject **out_obj_new /* does not return a reference */) { @@ -458,6 +457,8 @@ _l3_config_data_add_obj (NMDedupMultiIndex *multi_idx, const NMDedupMultiEntry *entry_old; const NMDedupMultiEntry *entry_new; + nm_assert ( NM_FLAGS_HAS (add_flags, NM_L3_CONFIG_ADD_FLAGS_MERGE) + != NM_FLAGS_HAS (add_flags, NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE)); nm_assert (multi_idx); nm_assert (idx_type); nm_assert (ifindex > 0); @@ -488,14 +489,17 @@ _l3_config_data_add_obj (NMDedupMultiIndex *multi_idx, gboolean modified = FALSE; const NMPObject *obj_old = entry_old->obj; + if (NM_FLAGS_HAS (add_flags, NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE)) { + nm_dedup_multi_entry_set_dirty (entry_old, FALSE); + goto append_force_and_out; + } + if (nmp_object_equal (obj_new, obj_old)) { nm_dedup_multi_entry_set_dirty (entry_old, FALSE); goto append_force_and_out; } - /* if @merge, we merge the new object with the existing one. - * Otherwise, we replace it entirely. */ - if (merge) { + if (NM_FLAGS_HAS (add_flags, NM_L3_CONFIG_ADD_FLAGS_MERGE)) { switch (idx_type->obj_type) { case NMP_OBJECT_TYPE_IP4_ADDRESS: case NMP_OBJECT_TYPE_IP6_ADDRESS: @@ -546,7 +550,7 @@ _l3_config_data_add_obj (NMDedupMultiIndex *multi_idx, if (!nm_dedup_multi_index_add_full (multi_idx, &idx_type->parent, obj_new, - append_force + NM_FLAGS_HAS (add_flags, NM_L3_CONFIG_ADD_FLAGS_APPEND_FORCE) ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE : NM_DEDUP_MULTI_IDX_MODE_APPEND, NULL, @@ -565,7 +569,7 @@ _l3_config_data_add_obj (NMDedupMultiIndex *multi_idx, append_force_and_out: NM_SET_OUT (out_obj_old, nmp_object_ref (entry_old->obj)); NM_SET_OUT (out_obj_new, entry_old->obj); - if (append_force) { + if (NM_FLAGS_HAS (add_flags, NM_L3_CONFIG_ADD_FLAGS_APPEND_FORCE)) { if (nm_dedup_multi_entry_reorder (entry_old, NULL, TRUE)) return TRUE; } @@ -622,11 +626,12 @@ _l3_config_best_default_route_find_better (const NMPObject *obj_cur, const NMPOb } gboolean -nm_l3_config_data_add_address (NML3ConfigData *self, - int addr_family, - const NMPObject *obj_new, - const NMPlatformIPAddress *pl_new, - const NMPObject **out_obj_new) +nm_l3_config_data_add_address_full (NML3ConfigData *self, + int addr_family, + const NMPObject *obj_new, + const NMPlatformIPAddress *pl_new, + NML3ConfigAddFlags add_flags, + const NMPObject **out_obj_new) { const NMPObject *new; gboolean changed; @@ -644,8 +649,7 @@ nm_l3_config_data_add_address (NML3ConfigData *self, self->ifindex, obj_new, (const NMPlatformObject *) pl_new, - TRUE, - FALSE, + add_flags, NULL, &new); NM_SET_OUT (out_obj_new, nmp_object_ref (new)); @@ -661,12 +665,13 @@ _l3_config_best_default_route_merge (const NMPObject **best_default_route, const } gboolean -nm_l3_config_data_add_route (NML3ConfigData *self, - int addr_family, - const NMPObject *obj_new, - const NMPlatformIPRoute *pl_new, - const NMPObject **out_obj_new, - gboolean *out_changed_best_default_route) +nm_l3_config_data_add_route_full (NML3ConfigData *self, + int addr_family, + const NMPObject *obj_new, + const NMPlatformIPRoute *pl_new, + NML3ConfigAddFlags add_flags, + const NMPObject **out_obj_new, + gboolean *out_changed_best_default_route) { const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); nm_auto_nmpobj const NMPObject *obj_old = NULL; @@ -690,8 +695,7 @@ nm_l3_config_data_add_route (NML3ConfigData *self, self->ifindex, obj_new, (const NMPlatformObject *) pl_new, - TRUE, - FALSE, + add_flags, &obj_old, &obj_new_2)) { @@ -972,7 +976,7 @@ _init_from_connection_ip (NML3ConfigData *self, }; } - nm_l3_config_data_add_route (self, addr_family, NULL, &r.rx, NULL, NULL); + nm_l3_config_data_add_route (self, addr_family, NULL, &r.rx); } naddresses = nm_setting_ip_config_get_num_addresses (s_ip); @@ -1012,7 +1016,7 @@ _init_from_connection_ip (NML3ConfigData *self, nm_assert (a.a6.plen <= 128); } - nm_l3_config_data_add_address (self, addr_family, NULL, &a.ax, NULL); + nm_l3_config_data_add_address (self, addr_family, NULL, &a.ax); } nroutes = nm_setting_ip_config_get_num_routes (s_ip); @@ -1066,7 +1070,7 @@ _init_from_connection_ip (NML3ConfigData *self, &r.rx, route_table); - nm_l3_config_data_add_route (self, addr_family, NULL, &r.rx, NULL, NULL); + nm_l3_config_data_add_route (self, addr_family, NULL, &r.rx); } nnameservers = nm_setting_ip_config_get_num_dns (s_ip); @@ -1174,8 +1178,7 @@ _init_from_platform (NML3ConfigData *self, self->ifindex, plobj, NULL, - FALSE, - TRUE, + NM_L3_CONFIG_ADD_FLAGS_APPEND_FORCE, NULL, NULL)) nm_assert_not_reached (); @@ -1195,7 +1198,7 @@ _init_from_platform (NML3ConfigData *self, : NMP_OBJECT_TYPE_IP6_ROUTE, self->ifindex); nmp_cache_iter_for_each (&iter, head_entry, &plobj) - nm_l3_config_data_add_route (self, addr_family, plobj, NULL, NULL, NULL); + nm_l3_config_data_add_route (self, addr_family, plobj, NULL); } NML3ConfigData * @@ -1243,13 +1246,13 @@ nm_l3_config_data_new_clone (const NML3ConfigData *src, self = nm_l3_config_data_new (src->multi_idx, ifindex); nm_l3_config_data_iter_obj_for_each (iter, src, obj, NMP_OBJECT_TYPE_IP4_ADDRESS) - nm_l3_config_data_add_address (self, AF_INET, obj, NULL, NULL); + nm_l3_config_data_add_address (self, AF_INET, obj, NULL); nm_l3_config_data_iter_obj_for_each (iter, src, obj, NMP_OBJECT_TYPE_IP6_ADDRESS) - nm_l3_config_data_add_address (self, AF_INET6, obj, NULL, NULL); + nm_l3_config_data_add_address (self, AF_INET6, obj, NULL); nm_l3_config_data_iter_obj_for_each (iter, src, obj, NMP_OBJECT_TYPE_IP4_ROUTE) - nm_l3_config_data_add_route (self, AF_INET, obj, NULL, NULL, NULL); + nm_l3_config_data_add_route (self, AF_INET, obj, NULL); nm_l3_config_data_iter_obj_for_each (iter, src, obj, NMP_OBJECT_TYPE_IP6_ROUTE) - nm_l3_config_data_add_route (self, AF_INET6, obj, NULL, NULL, NULL); + nm_l3_config_data_add_route (self, AF_INET6, obj, NULL); nm_assert (self->best_default_route_4 == src->best_default_route_4); nm_assert (self->best_default_route_6 == src->best_default_route_6); diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index ee00634f43..3fddf89963 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -16,6 +16,27 @@ typedef enum { NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES = (1ull << 0), } NML3ConfigDatFlags; +typedef enum { + NM_L3_CONFIG_ADD_FLAGS_NONE = 0, + + /* If the object does not yet exist, it will be added. If it already exists, + * by default the object will be replaced. With this flag, the new object will + * be merged with the existing one. */ + NM_L3_CONFIG_ADD_FLAGS_MERGE = (1ull << 0), + + /* If the object does not yet exist, it will be added. If it already exists, + * by default the object will be replaced. With this flag, the add will have + * no effect and the existing object will be kept. */ + NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE = (1ull << 1), + + /* A new object gets appended by default. If the object already exists, + * by default it will not be moved. With APPEND-FORCE, we will always move + * an existing object to the end of the list. */ + NM_L3_CONFIG_ADD_FLAGS_APPEND_FORCE = (1ull << 2), +} NML3ConfigAddFlags; + +/*****************************************************************************/ + typedef struct _NML3ConfigData NML3ConfigData; NML3ConfigData *nm_l3_config_data_new (NMDedupMultiIndex *multi_idx, @@ -151,41 +172,72 @@ nm_l3_config_data_unset_flags (NML3ConfigData *self, /*****************************************************************************/ -gboolean nm_l3_config_data_add_address (NML3ConfigData *self, - int addr_family, - const NMPObject *obj_new, - const NMPlatformIPAddress *pl_new, - const NMPObject **out_obj_new); +gboolean nm_l3_config_data_add_address_full (NML3ConfigData *self, + int addr_family, + const NMPObject *obj_new, + const NMPlatformIPAddress *pl_new, + NML3ConfigAddFlags add_flags, + const NMPObject **out_obj_new); + +static inline gboolean +nm_l3_config_data_add_address (NML3ConfigData *self, + int addr_family, + const NMPObject *obj_new, + const NMPlatformIPAddress *pl_new) +{ + return nm_l3_config_data_add_address_full (self, + addr_family, + obj_new, + pl_new, + NM_L3_CONFIG_ADD_FLAGS_MERGE, + NULL); +} static inline gboolean nm_l3_config_data_add_address_4 (NML3ConfigData *self, const NMPlatformIP4Address *addr) { - return nm_l3_config_data_add_address (self, AF_INET, NULL, NM_PLATFORM_IP_ADDRESS_CAST (addr), NULL); + return nm_l3_config_data_add_address (self, AF_INET, NULL, NM_PLATFORM_IP_ADDRESS_CAST (addr)); } static inline gboolean nm_l3_config_data_add_address_6 (NML3ConfigData *self, const NMPlatformIP6Address *addr) { - return nm_l3_config_data_add_address (self, AF_INET6, NULL, NM_PLATFORM_IP_ADDRESS_CAST (addr), NULL); + return nm_l3_config_data_add_address (self, AF_INET6, NULL, NM_PLATFORM_IP_ADDRESS_CAST (addr)); } -gboolean nm_l3_config_data_add_route (NML3ConfigData *self, - int addr_family, - const NMPObject *obj_new, - const NMPlatformIPRoute *pl_new, - const NMPObject **out_obj_new, - gboolean *out_changed_best_default_route); +gboolean nm_l3_config_data_add_route_full (NML3ConfigData *self, + int addr_family, + const NMPObject *obj_new, + const NMPlatformIPRoute *pl_new, + NML3ConfigAddFlags add_flags, + const NMPObject **out_obj_new, + gboolean *out_changed_best_default_route); + +static inline gboolean +nm_l3_config_data_add_route (NML3ConfigData *self, + int addr_family, + const NMPObject *obj_new, + const NMPlatformIPRoute *pl_new) +{ + return nm_l3_config_data_add_route_full (self, + addr_family, + obj_new, + pl_new, + NM_L3_CONFIG_ADD_FLAGS_MERGE, + NULL, + NULL); +} static inline gboolean nm_l3_config_data_add_route_4 (NML3ConfigData *self, const NMPlatformIP4Route *rt) { - return nm_l3_config_data_add_route (self, AF_INET, NULL, NM_PLATFORM_IP_ROUTE_CAST (rt), NULL, NULL); + return nm_l3_config_data_add_route (self, AF_INET, NULL, NM_PLATFORM_IP_ROUTE_CAST (rt)); } static inline gboolean nm_l3_config_data_add_route_6 (NML3ConfigData *self, const NMPlatformIP6Route *rt) { - return nm_l3_config_data_add_route (self, AF_INET6, NULL, NM_PLATFORM_IP_ROUTE_CAST (rt), NULL, NULL); + return nm_l3_config_data_add_route (self, AF_INET6, NULL, NM_PLATFORM_IP_ROUTE_CAST (rt)); } gboolean nm_l3_config_data_add_nameserver (NML3ConfigData *self, From 297eb4d1692d00bd755caad398e604baf4cddf83 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 27 Jul 2020 15:20:04 +0200 Subject: [PATCH 23/29] l3cfg: support more properties in NML3ConfigData --- src/nm-l3-config-data.c | 139 +++++++++++++++++++++++++++++++++------- src/nm-l3-config-data.h | 28 +++++++- 2 files changed, 141 insertions(+), 26 deletions(-) diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index abb48b76b2..78a0e332fd 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -46,7 +46,10 @@ struct _NML3ConfigData { const NMPObject *best_default_route_x[2]; }; - GArray *wins_4; + GArray *wins; + GArray *nis_servers; + + char *nis_domain; union { struct { @@ -80,6 +83,10 @@ struct _NML3ConfigData { GPtrArray *dns_options_x[2]; }; + int ifindex; + + int ref_count; + union { struct { int dns_priority_6; @@ -91,12 +98,12 @@ struct _NML3ConfigData { NMSettingConnectionMdns mdns; NMSettingConnectionLlmnr llmnr; - int ifindex; - - int ref_count; - NML3ConfigDatFlags flags; + guint32 mtu; + + NMTernary metered:3; + bool is_sealed:1; }; @@ -118,7 +125,7 @@ _garray_inaddr_ensure (GArray **p_arr, } static GArray * -_garray_inaddr_clone (GArray *src, +_garray_inaddr_clone (const GArray *src, int addr_family) { const gsize elt_size = nm_utils_addr_family_to_size (addr_family); @@ -289,6 +296,7 @@ nm_l3_config_data_new (NMDedupMultiIndex *multi_idx, .mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT, .llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT, .flags = NM_L3_CONFIG_DAT_FLAGS_NONE, + .metered = NM_TERNARY_DEFAULT, }; _idx_type_init (&self->idx_addresses_4, NMP_OBJECT_TYPE_IP4_ADDRESS); @@ -360,7 +368,8 @@ nm_l3_config_data_unref (const NML3ConfigData *self) nmp_object_unref (mutable->best_default_route_4); nmp_object_unref (mutable->best_default_route_6); - nm_clear_pointer (&mutable->wins_4, g_array_unref); + nm_clear_pointer (&mutable->wins, g_array_unref); + nm_clear_pointer (&mutable->nis_servers, g_array_unref); nm_clear_pointer (&mutable->nameservers_4, g_array_unref); nm_clear_pointer (&mutable->nameservers_6, g_array_unref); @@ -376,6 +385,8 @@ nm_l3_config_data_unref (const NML3ConfigData *self) nm_dedup_multi_index_unref (mutable->multi_idx); + g_free (mutable->nis_domain); + nm_g_slice_free (mutable); } @@ -436,6 +447,7 @@ nm_l3_config_data_set_flags_full (NML3ConfigData *self, { nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); nm_assert (!NM_FLAGS_ANY (flags, ~mask)); + nm_assert (!NM_FLAGS_ANY (mask, ~NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES)); self->flags = (self->flags & ~mask) | (flags & mask); @@ -457,8 +469,8 @@ _l3_config_data_add_obj (NMDedupMultiIndex *multi_idx, const NMDedupMultiEntry *entry_old; const NMDedupMultiEntry *entry_new; - nm_assert ( NM_FLAGS_HAS (add_flags, NM_L3_CONFIG_ADD_FLAGS_MERGE) - != NM_FLAGS_HAS (add_flags, NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE)); + nm_assert (nm_utils_is_power_of_two_or_zero (add_flags & ( NM_L3_CONFIG_ADD_FLAGS_MERGE + | NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE))); nm_assert (multi_idx); nm_assert (idx_type); nm_assert (ifindex > 0); @@ -769,11 +781,31 @@ nm_l3_config_data_add_wins (NML3ConfigData *self, { nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); - return _garray_inaddr_add (&self->wins_4, + return _garray_inaddr_add (&self->wins, AF_INET, &wins); } +gboolean +nm_l3_config_data_add_nis_server (NML3ConfigData *self, + in_addr_t nis_server) +{ + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); + + return _garray_inaddr_add (&self->nis_servers, + AF_INET, + &nis_server); +} + +gboolean +nm_l3_config_data_set_nis_domain (NML3ConfigData *self, + const char *nis_domain) +{ + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); + + return nm_utils_strdup_reset (&self->nis_domain, nis_domain); +} + gboolean nm_l3_config_data_add_domain (NML3ConfigData *self, int addr_family, @@ -826,16 +858,72 @@ nm_l3_config_data_set_dns_priority (NML3ConfigData *self, int addr_family, int dns_priority) { - int *p_val; + const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); + const NML3ConfigDatFlags has_dns_priority_flag = NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY (IS_IPv4); nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); nm_assert_addr_family (addr_family); - p_val = &self->dns_priority_x[NM_IS_IPv4 (addr_family)]; - if (*p_val == dns_priority) + if ( self->dns_priority_x[IS_IPv4] == dns_priority + && NM_FLAGS_ANY (self->flags, has_dns_priority_flag)) return FALSE; - *p_val = dns_priority; + self->flags |= has_dns_priority_flag; + self->dns_priority_x[IS_IPv4] = dns_priority; + return TRUE; +} + +gboolean +nm_l3_config_data_set_mdns (NML3ConfigData *self, + NMSettingConnectionMdns mdns) +{ + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); + + if (self->mdns == mdns) + return FALSE; + + self->mdns = mdns; + return TRUE; +} + +gboolean +nm_l3_config_data_set_llmnr (NML3ConfigData *self, + NMSettingConnectionLlmnr llmnr) +{ + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); + + if (self->llmnr == llmnr) + return FALSE; + + self->llmnr = llmnr; + return TRUE; +} + +gboolean +nm_l3_config_data_set_metered (NML3ConfigData *self, + NMTernary metered) +{ + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); + + if (self->metered == metered) + return FALSE; + + self->metered = metered; + return TRUE; +} + +gboolean +nm_l3_config_data_set_mtu (NML3ConfigData *self, + guint32 mtu) +{ + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); + + if ( self->mtu == mtu + && NM_FLAGS_HAS (self->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_MTU)) + return FALSE; + + self->mtu = mtu; + self->flags |= NM_L3_CONFIG_DAT_FLAGS_HAS_MTU; return TRUE; } @@ -889,6 +977,8 @@ nm_l3_config_data_cmp (const NML3ConfigData *a, const NML3ConfigData *b) NM_CMP_DIRECT (a->ifindex, b->ifindex); + NM_CMP_DIRECT (a->flags, b->flags); + _dedup_multi_index_cmp (a, b, NMP_OBJECT_TYPE_IP4_ADDRESS); _dedup_multi_index_cmp (a, b, NMP_OBJECT_TYPE_IP6_ADDRESS); _dedup_multi_index_cmp (a, b, NMP_OBJECT_TYPE_IP4_ROUTE); @@ -905,13 +995,18 @@ nm_l3_config_data_cmp (const NML3ConfigData *a, const NML3ConfigData *b) NM_CMP_RETURN (nm_strv_ptrarray_cmp (a->searches_x[IS_IPv4], b->searches_x[IS_IPv4])); NM_CMP_RETURN (nm_strv_ptrarray_cmp (a->dns_options_x[IS_IPv4], b->dns_options_x[IS_IPv4])); - NM_CMP_DIRECT (a->dns_priority_x[IS_IPv4], b->dns_priority_x[IS_IPv4]); + if (NM_FLAGS_ANY (a->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY (IS_IPv4))) + NM_CMP_DIRECT (a->dns_priority_x[IS_IPv4], b->dns_priority_x[IS_IPv4]); } - NM_CMP_RETURN (_garray_inaddr_cmp (a->wins_4, b->wins_4, AF_INET)); + NM_CMP_RETURN (_garray_inaddr_cmp (a->wins, b->wins, AF_INET)); + NM_CMP_RETURN (_garray_inaddr_cmp (a->nis_servers, b->nis_servers, AF_INET)); + NM_CMP_FIELD_STR0 (a, b, nis_domain); NM_CMP_DIRECT (a->mdns, b->mdns); NM_CMP_DIRECT (a->llmnr, b->llmnr); - NM_CMP_DIRECT (a->flags, b->flags); + if (NM_FLAGS_HAS (a->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_MTU)) + NM_CMP_DIRECT (a->mtu, b->mtu); + NM_CMP_DIRECT_UNSAFE (a->metered, b->metered); /* these fields are not considered by cmp(): * @@ -1108,8 +1203,6 @@ NML3ConfigData * nm_l3_config_data_new_from_connection (NMDedupMultiIndex *multi_idx, int ifindex, NMConnection *connection, - NMSettingConnectionMdns mdns, - NMSettingConnectionLlmnr llmnr, guint32 route_table, guint32 route_metric) { @@ -1119,10 +1212,6 @@ nm_l3_config_data_new_from_connection (NMDedupMultiIndex *multi_idx, _init_from_connection_ip (self, AF_INET, connection, route_table, route_metric); _init_from_connection_ip (self, AF_INET6, connection, route_table, route_metric); - - self->mdns = mdns; - self->llmnr = llmnr; - return self; } @@ -1257,7 +1346,7 @@ nm_l3_config_data_new_clone (const NML3ConfigData *src, nm_assert (self->best_default_route_4 == src->best_default_route_4); nm_assert (self->best_default_route_6 == src->best_default_route_6); - self->wins_4 = _garray_inaddr_clone (src->wins_4, AF_INET); + self->wins = _garray_inaddr_clone (src->wins, AF_INET); self->nameservers_4 = _garray_inaddr_clone (src->nameservers_4, AF_INET); self->nameservers_6 = _garray_inaddr_clone (src->nameservers_6, AF_INET6); self->domains_4 = nm_strv_ptrarray_clone (src->domains_4, TRUE); @@ -1271,5 +1360,7 @@ nm_l3_config_data_new_clone (const NML3ConfigData *src, self->mdns = src->mdns; self->llmnr = src->llmnr; + /* TODO: some fields are not cloned. Will be done next. */ + return self; } diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index 3fddf89963..e8649e83e1 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -14,6 +14,14 @@ typedef enum { /* if set, then the merge flag NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES gets * ignored during merge. */ NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES = (1ull << 0), + + NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_4 = (1ull << 1), + NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_6 = (1ull << 2), +#define NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY(is_ipv4) ( (is_ipv4) \ + ? NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_4 \ + : NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY_6) + NM_L3_CONFIG_DAT_FLAGS_HAS_MTU = (1ull << 3), + } NML3ConfigDatFlags; typedef enum { @@ -60,8 +68,6 @@ NML3ConfigData *nm_l3_config_data_new_clone (const NML3ConfigData *src, NML3ConfigData *nm_l3_config_data_new_from_connection (NMDedupMultiIndex *multi_idx, int ifindex, NMConnection *connection, - NMSettingConnectionMdns mdns, - NMSettingConnectionLlmnr llmnr, guint32 route_table, guint32 route_metric); @@ -240,6 +246,18 @@ nm_l3_config_data_add_route_6 (NML3ConfigData *self, const NMPlatformIP6Route *r return nm_l3_config_data_add_route (self, AF_INET6, NULL, NM_PLATFORM_IP_ROUTE_CAST (rt)); } +gboolean nm_l3_config_data_set_mdns (NML3ConfigData *self, + NMSettingConnectionMdns mdns); + +gboolean nm_l3_config_data_set_llmnr (NML3ConfigData *self, + NMSettingConnectionLlmnr llmnr); + +gboolean nm_l3_config_data_set_metered (NML3ConfigData *self, + NMTernary metered); + +gboolean nm_l3_config_data_set_mtu (NML3ConfigData *self, + guint32 mtu); + gboolean nm_l3_config_data_add_nameserver (NML3ConfigData *self, int addr_family, gconstpointer /* (const NMIPAddr *) */ nameserver); @@ -247,6 +265,12 @@ gboolean nm_l3_config_data_add_nameserver (NML3ConfigData *self, gboolean nm_l3_config_data_add_wins (NML3ConfigData *self, in_addr_t wins); +gboolean nm_l3_config_data_add_nis_server (NML3ConfigData *self, + in_addr_t nis_server); + +gboolean nm_l3_config_data_set_nis_domain (NML3ConfigData *self, + const char *nis_domain); + gboolean nm_l3_config_data_add_domain (NML3ConfigData *self, int addr_family, const char *domain); From 81cf493b96fb2138e046b00fdfbebe95294b459b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 23 Jul 2020 16:15:26 +0200 Subject: [PATCH 24/29] l3cfg: support tracking NML3ConfigData in NML3Cfg NML3Cfg is supposed to manage an interface (by ifindex). As such, it later will itself implement DHCP and similar addressing methods. However, in various cases we get additional IP configuration from external (e.g. from a VPN connection). To support that, let NML3Cfg track any number of NML3ConfigData instances. --- src/nm-l3-config-data.c | 246 ++++++++++++++++++++++++++--- src/nm-l3-config-data.h | 35 +++++ src/nm-l3cfg.c | 340 ++++++++++++++++++++++++++++++++++++++++ src/nm-l3cfg.h | 29 +++- 4 files changed, 622 insertions(+), 28 deletions(-) diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index 78a0e332fd..1dec11ed89 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -143,6 +143,44 @@ _garray_inaddr_clone (const GArray *src, return dst; } +static void +_garray_inaddr_merge (GArray **p_dst, + const GArray *src, + int addr_family) +{ + guint dst_initial_len; + const char *p_dst_arr; + const char *p_src; + gsize elt_size; + guint i; + guint j; + + if (nm_g_array_len (src) == 0) + return; + + if (!*p_dst) { + *p_dst = _garray_inaddr_clone (src, addr_family); + return; + } + + elt_size = nm_utils_addr_family_to_size (addr_family); + + dst_initial_len = (*p_dst)->len; + p_dst_arr = (*p_dst)->data; + p_src = src->data; + + for (i = 0; i < src->len; i++, p_src += elt_size) { + for (j = 0; j < dst_initial_len; j++) { + if (memcmp (&p_dst_arr[j * elt_size], p_src, elt_size) == 0) + goto next; + } + g_array_append_vals (*p_dst, p_src, 1); + p_dst_arr = (*p_dst)->data; +next: + ; + } +} + static gssize _garray_inaddr_find (GArray *arr, int addr_family, @@ -204,6 +242,36 @@ _garray_inaddr_cmp (const GArray *a, const GArray *b, int addr_family) return 0; } +static void +_strv_ptrarray_merge (GPtrArray **p_dst, const GPtrArray *src) +{ + guint dst_initial_len; + guint i; + + if (nm_g_ptr_array_len (src) == 0) + return; + + if (!*p_dst) { + /* we trust src to contain unique strings. Just clone it. */ + *p_dst = nm_strv_ptrarray_clone (src, TRUE); + return; + } + + nm_strv_ptrarray_ensure (p_dst); + + dst_initial_len = (*p_dst)->len; + + for (i = 0; i < src->len; i++) { + const char *s = src->pdata[i]; + + if ( dst_initial_len > 0 + && nm_utils_strv_find_first ((char **) ((*p_dst)->pdata), dst_initial_len, s) >= 0) + continue; + + g_ptr_array_add (*p_dst, g_strdup (s)); + } +} + /*****************************************************************************/ static gboolean @@ -1316,13 +1384,143 @@ nm_l3_config_data_new_from_platform (NMDedupMultiIndex *multi_idx, /*****************************************************************************/ +static void +_init_merge (NML3ConfigData *self, + const NML3ConfigData *src, + NML3ConfigMergeFlags merge_flags, + const guint32 *default_route_penalty_x /* length 2, for IS_IPv4 */) +{ + NMDedupMultiIter iter; + const NMPObject *obj; + int IS_IPv4; + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert (_NM_IS_L3_CONFIG_DATA (src, TRUE)); + + for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { + const int addr_family = IS_IPv4 ? AF_INET : AF_INET6; + const NML3ConfigDatFlags has_dns_priority_flag = NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY (IS_IPv4); + + nm_l3_config_data_iter_obj_for_each (iter, + src, + obj, + NMP_OBJECT_TYPE_IP_ADDRESS (IS_IPv4)) { + 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, + NULL, + NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE, + NULL); + } + + if (!NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES)) { + nm_l3_config_data_iter_obj_for_each (iter, + src, + obj, + NMP_OBJECT_TYPE_IP_ROUTE (IS_IPv4)) { + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (NMP_OBJECT_CAST_IP_ROUTE (obj))) { + if ( NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES) + && !NM_FLAGS_HAS (src->flags, NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES)) + continue; + + if ( default_route_penalty_x + && default_route_penalty_x[IS_IPv4] > 0) { + NMPlatformIPXRoute r; + + if (IS_IPv4) + r.r4 = *NMP_OBJECT_CAST_IP4_ROUTE (obj); + else + r.r6 = *NMP_OBJECT_CAST_IP6_ROUTE (obj); + r.rx.ifindex = self->ifindex; + r.rx.metric = nm_utils_ip_route_metric_penalize (r.rx.metric, default_route_penalty_x[IS_IPv4]); + nm_l3_config_data_add_route_full (self, + addr_family, + NULL, + &r.rx, + NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE, + NULL, + NULL); + continue; + } + } + nm_l3_config_data_add_route_full (self, + addr_family, + obj, + NULL, + NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE, + NULL, + NULL); + } + } + + if (!NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS)) + _garray_inaddr_merge (&self->nameservers_x[IS_IPv4], src->nameservers_x[IS_IPv4], addr_family); + + if (!NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS)) + _strv_ptrarray_merge (&self->domains_x[IS_IPv4], src->domains_x[IS_IPv4]); + + if (!NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS)) + _strv_ptrarray_merge (&self->searches_x[IS_IPv4], src->searches_x[IS_IPv4]); + + if (!NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS)) + _strv_ptrarray_merge (&self->dns_options_x[IS_IPv4], src->dns_options_x[IS_IPv4]); + + if ( !NM_FLAGS_ANY (self->flags, has_dns_priority_flag) + && NM_FLAGS_ANY (src->flags, has_dns_priority_flag)) { + self->dns_priority_x[IS_IPv4] = src->dns_priority_x[IS_IPv4]; + self->flags |= has_dns_priority_flag; + } + } + + if (!NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS)) { + _garray_inaddr_merge (&self->wins, src->wins, AF_INET); + _garray_inaddr_merge (&self->nis_servers, src->nis_servers, AF_INET); + + if ( !self->nis_domain + && src->nis_domain) + self->nis_domain = g_strdup (src->nis_domain); + } + + if (self->mdns == NM_SETTING_CONNECTION_MDNS_DEFAULT) + self->mdns = src->mdns; + + if (self->llmnr == NM_SETTING_CONNECTION_LLMNR_DEFAULT) + self->llmnr = src->llmnr; + + if (self->metered == NM_TERNARY_DEFAULT) + self->metered = src->metered; + + if ( !NM_FLAGS_HAS (self->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_MTU) + && NM_FLAGS_HAS (src->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_MTU)) { + self->mtu = src->mtu; + self->flags |= NM_L3_CONFIG_DAT_FLAGS_HAS_MTU; + } +} + NML3ConfigData * nm_l3_config_data_new_clone (const NML3ConfigData *src, int ifindex) { NML3ConfigData *self; - NMDedupMultiIter iter; - const NMPObject *obj; nm_assert (_NM_IS_L3_CONFIG_DATA (src, TRUE)); @@ -1333,34 +1531,30 @@ nm_l3_config_data_new_clone (const NML3ConfigData *src, ifindex = src->ifindex; self = nm_l3_config_data_new (src->multi_idx, ifindex); + _init_merge (self, src, NM_L3_CONFIG_MERGE_FLAGS_NONE, NULL); + return self; +} - nm_l3_config_data_iter_obj_for_each (iter, src, obj, NMP_OBJECT_TYPE_IP4_ADDRESS) - nm_l3_config_data_add_address (self, AF_INET, obj, NULL); - nm_l3_config_data_iter_obj_for_each (iter, src, obj, NMP_OBJECT_TYPE_IP6_ADDRESS) - nm_l3_config_data_add_address (self, AF_INET6, obj, NULL); - nm_l3_config_data_iter_obj_for_each (iter, src, obj, NMP_OBJECT_TYPE_IP4_ROUTE) - nm_l3_config_data_add_route (self, AF_INET, obj, NULL); - nm_l3_config_data_iter_obj_for_each (iter, src, obj, NMP_OBJECT_TYPE_IP6_ROUTE) - nm_l3_config_data_add_route (self, AF_INET6, obj, NULL); +NML3ConfigData * +nm_l3_config_data_new_combined (NMDedupMultiIndex *multi_idx, + int ifindex, + const NML3ConfigDatMergeInfo *const*merge_infos, + guint merge_infos_len) +{ + NML3ConfigData *self; + guint i; - nm_assert (self->best_default_route_4 == src->best_default_route_4); - nm_assert (self->best_default_route_6 == src->best_default_route_6); + nm_assert (multi_idx); + nm_assert (ifindex > 0); - self->wins = _garray_inaddr_clone (src->wins, AF_INET); - self->nameservers_4 = _garray_inaddr_clone (src->nameservers_4, AF_INET); - self->nameservers_6 = _garray_inaddr_clone (src->nameservers_6, AF_INET6); - self->domains_4 = nm_strv_ptrarray_clone (src->domains_4, TRUE); - self->domains_6 = nm_strv_ptrarray_clone (src->domains_6, TRUE); - self->searches_4 = nm_strv_ptrarray_clone (src->searches_4, TRUE); - self->searches_6 = nm_strv_ptrarray_clone (src->searches_6, TRUE); - self->dns_options_4 = nm_strv_ptrarray_clone (src->dns_options_4, TRUE); - self->dns_options_6 = nm_strv_ptrarray_clone (src->dns_options_6, TRUE); - self->dns_priority_4 = src->dns_priority_4; - self->dns_priority_6 = src->dns_priority_6; - self->mdns = src->mdns; - self->llmnr = src->llmnr; + self = nm_l3_config_data_new (multi_idx, ifindex); - /* TODO: some fields are not cloned. Will be done next. */ + for (i = 0; i < merge_infos_len; i++) { + _init_merge (self, + merge_infos[i]->l3cfg, + merge_infos[i]->merge_flags, + merge_infos[i]->default_route_penalty_x); + } return self; } diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index e8649e83e1..09b46a2ce4 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -43,10 +43,40 @@ typedef enum { NM_L3_CONFIG_ADD_FLAGS_APPEND_FORCE = (1ull << 2), } NML3ConfigAddFlags; +/** + * NML3ConfigMergeFlags: + * @NM_L3_CONFIG_MERGE_FLAGS_NONE: no flags set + * @NM_L3_CONFIG_MERGE_FLAGS_NO_ROUTES: don't merge routes + * @NM_L3_CONFIG_MERGE_FLAGS_NO_DEFAULT_ROUTES: don't merge default routes. + * 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; + /*****************************************************************************/ typedef struct _NML3ConfigData NML3ConfigData; +typedef struct { + const NML3ConfigData *l3cfg; + NML3ConfigMergeFlags merge_flags; + union { + struct { + guint32 default_route_penalty_6; + guint32 default_route_penalty_4; + }; + guint32 default_route_penalty_x[2]; + }; +} NML3ConfigDatMergeInfo; + NML3ConfigData *nm_l3_config_data_new (NMDedupMultiIndex *multi_idx, int ifindex); const NML3ConfigData *nm_l3_config_data_ref (const NML3ConfigData *self); @@ -76,6 +106,11 @@ NML3ConfigData *nm_l3_config_data_new_from_platform (NMDedupMultiIndex *multi_id NMPlatform *platform, NMSettingIP6ConfigPrivacy ipv6_privacy_rfc4941); +NML3ConfigData *nm_l3_config_data_new_combined (NMDedupMultiIndex *multi_idx, + int ifindex, + const NML3ConfigDatMergeInfo *const*merge_infos, + guint merge_infos_len); + /*****************************************************************************/ int nm_l3_config_data_get_ifindex (const NML3ConfigData *self); diff --git a/src/nm-l3cfg.c b/src/nm-l3cfg.c index aefaef1cd8..3709ff7dbc 100644 --- a/src/nm-l3cfg.c +++ b/src/nm-l3cfg.c @@ -10,6 +10,16 @@ /*****************************************************************************/ +typedef struct { + NML3ConfigDatMergeInfo merge_info; + gconstpointer tag; + guint64 pseudo_timestamp; + int priority; + bool dirty:1; +} L3ConfigData; + +/*****************************************************************************/ + NM_GOBJECT_PROPERTIES_DEFINE (NML3Cfg, PROP_NETNS, PROP_IFINDEX, @@ -17,6 +27,9 @@ NM_GOBJECT_PROPERTIES_DEFINE (NML3Cfg, typedef struct _NML3CfgPrivate { GArray *property_emit_list; + GArray *l3_config_datas; + const NML3ConfigData *combined_l3cfg; + guint64 pseudo_timestamp_counter; } NML3CfgPrivate; struct _NML3CfgClass { @@ -226,6 +239,331 @@ nm_l3cfg_property_emit_unregister (NML3Cfg *self, /*****************************************************************************/ +static GArray * +_l3_config_datas_ensure (GArray **p_arr) +{ + if (!*p_arr) + *p_arr = g_array_new (FALSE, FALSE, sizeof (L3ConfigData)); + return *p_arr; +} + +#define _l3_config_datas_at(l3_config_datas, idx) \ + (&g_array_index ((l3_config_datas), L3ConfigData, (idx))) + +static gssize +_l3_config_datas_find_next (GArray *l3_config_datas, + guint start_idx, + gconstpointer needle_tag, + const NML3ConfigData *needle_l3cfg) +{ + guint i; + + nm_assert (l3_config_datas); + nm_assert (start_idx <= l3_config_datas->len); + + for (i = start_idx; i < l3_config_datas->len; i++) { + const L3ConfigData *l3_config_data = _l3_config_datas_at (l3_config_datas, i); + + if ( NM_IN_SET (needle_tag, NULL, l3_config_data->tag) + && NM_IN_SET (needle_l3cfg, NULL, l3_config_data->merge_info.l3cfg)) + return i; + } + return -1; +} + +static void +_l3_config_datas_remove_index_fast (GArray *arr, + guint idx) +{ + L3ConfigData *l3_config_data; + + nm_assert (arr); + nm_assert (idx < arr->len); + + l3_config_data = _l3_config_datas_at (arr, idx); + + nm_l3_config_data_unref (l3_config_data->merge_info.l3cfg); + + g_array_remove_index_fast (arr, idx); +} + +void +nm_l3cfg_mark_config_dirty (NML3Cfg *self, + gconstpointer tag, + gboolean dirty) +{ + GArray *l3_config_datas; + gssize idx; + + nm_assert (NM_IS_L3CFG (self)); + nm_assert (tag); + + l3_config_datas = self->priv.p->l3_config_datas; + if (!l3_config_datas) + return; + + idx = 0; + while (TRUE) { + idx = _l3_config_datas_find_next (l3_config_datas, + idx, + tag, + NULL); + if (idx < 0) + return; + + _l3_config_datas_at (l3_config_datas, idx)->dirty = dirty; + idx++; + } +} + +void +nm_l3cfg_add_config (NML3Cfg *self, + gconstpointer tag, + gboolean replace_same_tag, + const NML3ConfigData *l3cfg, + int priority, + guint32 default_route_penalty_4, + guint32 default_route_penalty_6, + NML3ConfigMergeFlags merge_flags) +{ + GArray *l3_config_datas; + L3ConfigData *l3_config_data; + gssize idx; + gboolean changed = FALSE; + + nm_assert (NM_IS_L3CFG (self)); + nm_assert (tag); + nm_assert (l3cfg); + nm_assert (nm_l3_config_data_get_ifindex (l3cfg) == self->priv.ifindex); + + l3_config_datas = _l3_config_datas_ensure (&self->priv.p->l3_config_datas); + + idx = _l3_config_datas_find_next (l3_config_datas, + 0, + tag, + replace_same_tag ? NULL : l3cfg); + + if (replace_same_tag) { + gssize idx2; + + idx2 = idx; + idx = -1; + while (TRUE) { + l3_config_data = _l3_config_datas_at (l3_config_datas, idx2); + + if (l3_config_data->merge_info.l3cfg == l3cfg) { + nm_assert (idx == -1); + idx = idx2; + continue; + } + + changed = TRUE; + _l3_config_datas_remove_index_fast (l3_config_datas, idx2); + + idx2 = _l3_config_datas_find_next (l3_config_datas, idx2, tag, NULL); + if (idx2 < 0) + break; + } + } + + if (idx < 0) { + l3_config_data = nm_g_array_append_new (l3_config_datas, L3ConfigData); + *l3_config_data = (L3ConfigData) { + .tag = tag, + .merge_info.l3cfg = nm_l3_config_data_ref_and_seal (l3cfg), + .merge_info.merge_flags = merge_flags, + .merge_info.default_route_penalty_4 = default_route_penalty_4, + .merge_info.default_route_penalty_6 = default_route_penalty_6, + .priority = priority, + .pseudo_timestamp = ++self->priv.p->pseudo_timestamp_counter, + .dirty = FALSE, + }; + changed = TRUE; + } else { + l3_config_data = _l3_config_datas_at (l3_config_datas, idx); + l3_config_data->dirty = FALSE; + nm_assert (l3_config_data->tag == tag); + nm_assert (l3_config_data->merge_info.l3cfg == l3cfg); + if (l3_config_data->priority != priority) { + l3_config_data->priority = priority; + changed = TRUE; + } + if (l3_config_data->merge_info.merge_flags != merge_flags) { + l3_config_data->merge_info.merge_flags = merge_flags; + changed = TRUE; + } + if (l3_config_data->merge_info.default_route_penalty_4 != default_route_penalty_4) { + l3_config_data->merge_info.default_route_penalty_4 = default_route_penalty_4; + changed = TRUE; + } + if (l3_config_data->merge_info.default_route_penalty_6 != default_route_penalty_6) { + l3_config_data->merge_info.default_route_penalty_6 = default_route_penalty_6; + changed = TRUE; + } + } + + if (changed) + self->priv.changed_configs = TRUE; +} + +static void +_l3cfg_remove_config (NML3Cfg *self, + gconstpointer tag, + gboolean only_dirty, + const NML3ConfigData *l3cfg) +{ + GArray *l3_config_datas; + gssize idx; + + nm_assert (NM_IS_L3CFG (self)); + nm_assert (tag); + + l3_config_datas = self->priv.p->l3_config_datas; + if (!l3_config_datas) + return; + + idx = 0; + while (TRUE) { + idx = _l3_config_datas_find_next (l3_config_datas, + idx, + tag, + l3cfg); + if (idx < 0) + return; + + if ( only_dirty + && !_l3_config_datas_at (l3_config_datas, idx)->dirty) { + idx++; + continue; + } + + self->priv.changed_configs = TRUE; + _l3_config_datas_remove_index_fast (l3_config_datas, idx); + if (!l3cfg) + return; + } +} + +void +nm_l3cfg_remove_config (NML3Cfg *self, + gconstpointer tag, + const NML3ConfigData *ifcfg) +{ + nm_assert (ifcfg); + + _l3cfg_remove_config (self, tag, FALSE, ifcfg); +} + +void +nm_l3cfg_remove_config_all (NML3Cfg *self, + gconstpointer tag, + gboolean only_dirty) +{ + _l3cfg_remove_config (self, tag, only_dirty, NULL); +} + +/*****************************************************************************/ + +static int +_l3_config_combine_sort_fcn (gconstpointer p_a, + gconstpointer p_b, + gpointer user_data) +{ + const L3ConfigData *a = *((L3ConfigData **) p_a); + const L3ConfigData *b = *((L3ConfigData **) p_b); + + nm_assert (a); + nm_assert (b); + nm_assert (nm_l3_config_data_get_ifindex (a->merge_info.l3cfg) == nm_l3_config_data_get_ifindex (b->merge_info.l3cfg)); + + /* we sort the entries with higher priority (more important, lower numerical value) + * first. */ + NM_CMP_FIELD (a, b, priority); + + /* if the priority is not unique, we sort them in the order they were added, + * with the oldest first (lower numerical value). */ + NM_CMP_FIELD (a, b, pseudo_timestamp); + + return nm_assert_unreachable_val (0); +} + +static const NML3ConfigData * +_l3cfg_combine_config (GArray *l3_config_datas, + NMDedupMultiIndex *multi_idx, + int ifindex) +{ + gs_free L3ConfigData **infos_heap = NULL; + NML3ConfigData *l3cfg; + L3ConfigData **infos; + guint i; + + if ( !l3_config_datas + || l3_config_datas->len == 0) + return NULL; + + if (l3_config_datas->len == 1) + return nm_l3_config_data_ref (_l3_config_datas_at (l3_config_datas, 0)->merge_info.l3cfg); + + if (l3_config_datas->len < 300 / sizeof (infos[0])) + infos = g_alloca (l3_config_datas->len * sizeof (infos[0])); + else { + infos_heap = g_new (L3ConfigData *, l3_config_datas->len); + infos = infos_heap; + } + + for (i = 0; i < l3_config_datas->len; i++) + infos[i] = _l3_config_datas_at (l3_config_datas, i); + + g_qsort_with_data (infos, + l3_config_datas->len, + sizeof (infos[0]), + _l3_config_combine_sort_fcn, + NULL); + + nm_assert (&infos[0]->merge_info == (NML3ConfigDatMergeInfo *) infos[0]); + + l3cfg = nm_l3_config_data_new_combined (multi_idx, + ifindex, + (const NML3ConfigDatMergeInfo *const*) infos, + l3_config_datas->len); + + nm_assert (l3cfg); + nm_assert (nm_l3_config_data_get_ifindex (l3cfg) == ifindex); + + return nm_l3_config_data_seal (l3cfg); +} + +_nm_unused +static gboolean +_l3cfg_update_combined_config (NML3Cfg *self, + const NML3ConfigData **out_old /* transfer reference */) +{ + nm_auto_unref_l3cfg const NML3ConfigData *l3cfg_old = NULL; + nm_auto_unref_l3cfg const NML3ConfigData *l3cfg = NULL; + + nm_assert (NM_IS_L3CFG (self)); + nm_assert (!out_old || !*out_old); + + if (!self->priv.changed_configs) + return FALSE; + + self->priv.changed_configs = FALSE; + + l3cfg = _l3cfg_combine_config (self->priv.p->l3_config_datas, + nm_platform_get_multi_idx (self->priv.platform), + self->priv.ifindex); + + if (nm_l3_config_data_equal (l3cfg, self->priv.p->combined_l3cfg)) + return FALSE; + + l3cfg_old = g_steal_pointer (&self->priv.p->combined_l3cfg); + self->priv.p->combined_l3cfg = nm_l3_config_data_seal (g_steal_pointer (&l3cfg)); + NM_SET_OUT (out_old, nm_l3_config_data_ref (self->priv.p->combined_l3cfg)); + return TRUE; +} + +/*****************************************************************************/ + static void set_property (GObject *object, guint prop_id, @@ -304,6 +642,8 @@ finalize (GObject *object) g_clear_object (&self->priv.netns); g_clear_object (&self->priv.platform); + nm_clear_pointer (&self->priv.p->combined_l3cfg, nm_l3_config_data_unref); + nm_clear_pointer (&self->priv.pllink, nmp_object_unref); _LOGT ("finalized"); diff --git a/src/nm-l3cfg.h b/src/nm-l3cfg.h index 7fc4b4b7d2..0c2b9c814a 100644 --- a/src/nm-l3cfg.h +++ b/src/nm-l3cfg.h @@ -4,6 +4,7 @@ #define __NM_L3CFG_H__ #include "platform/nmp-object.h" +#include "nm-l3-config-data.h" #define NM_TYPE_L3CFG (nm_l3cfg_get_type ()) #define NM_L3CFG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_L3CFG, NML3Cfg)) @@ -20,11 +21,12 @@ struct _NML3CfgPrivate; struct _NML3Cfg { GObject parent; struct { + struct _NML3CfgPrivate *p; NMNetns *netns; NMPlatform *platform; - int ifindex; const NMPObject *pllink; - struct _NML3CfgPrivate *p; + int ifindex; + bool changed_configs:1; } priv; }; @@ -89,4 +91,27 @@ void nm_l3cfg_property_emit_unregister (NML3Cfg *self, GObject *target_obj, const GParamSpec *target_property); +/*****************************************************************************/ + +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 *l3cfg, + int priority, + guint32 default_route_penalty_4, + guint32 default_route_penalty_6, + NML3ConfigMergeFlags merge_flags); + +void nm_l3cfg_remove_config (NML3Cfg *self, + gconstpointer tag, + const NML3ConfigData *ifcfg); + +void nm_l3cfg_remove_config_all (NML3Cfg *self, + gconstpointer tag, + gboolean only_dirty); + #endif /* __NM_L3CFG_H__ */ From 695ce608bafb8a05d704b6705150cce10e869fe9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 27 Jul 2020 16:43:36 +0200 Subject: [PATCH 25/29] l3cfg: add nm_l3_config_data_add_dependent_routes() --- src/nm-l3-config-data.c | 334 +++++++++++++++++++++++++++++++++++++ src/nm-l3-config-data.h | 14 ++ src/platform/nm-platform.c | 2 + 3 files changed, 350 insertions(+) diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index 1dec11ed89..7adcf15ed7 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -6,6 +6,7 @@ #include #include +#include #include "nm-core-internal.h" #include "platform/nm-platform.h" @@ -460,6 +461,63 @@ nm_l3_config_data_unref (const NML3ConfigData *self) /*****************************************************************************/ +static const NMDedupMultiEntry * +_lookup_route (const NMDedupMultiIndex *multi_idx, + const DedupMultiIdxType *idx_type, + const NMPObject *needle) +{ + const NMDedupMultiEntry *entry; + + nm_assert (multi_idx); + nm_assert (idx_type); + nm_assert (NM_IN_SET (idx_type->obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (NMP_OBJECT_GET_TYPE (needle) == idx_type->obj_type); + + entry = nm_dedup_multi_index_lookup_obj (multi_idx, + &idx_type->parent, + needle); + nm_assert ( !entry + || ( NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP4_ROUTE + && nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (entry->obj), NMP_OBJECT_CAST_IP4_ROUTE (needle), NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0) + || ( NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP6_ROUTE + && nm_platform_ip6_route_cmp (NMP_OBJECT_CAST_IP6_ROUTE (entry->obj), NMP_OBJECT_CAST_IP6_ROUTE (needle), NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0)); + + return entry; +} + +const NMDedupMultiEntry * +nm_l3_config_data_lookup_route_obj (const NML3ConfigData *self, + const NMPObject *needle) +{ + gboolean IS_IPv4; + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (needle), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + + IS_IPv4 = NM_IS_IPv4 (NMP_OBJECT_GET_ADDR_FAMILY (needle)); + return _lookup_route (self->multi_idx, + &self->idx_routes_x[IS_IPv4], + needle); +} + +const NMDedupMultiEntry * +nm_l3_config_data_lookup_route (const NML3ConfigData *self, + int addr_family, + const NMPlatformIPRoute *needle) +{ + const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); + NMPObject obj_stack; + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert_addr_family (addr_family); + nm_assert (needle); + + return _lookup_route (self->multi_idx, + &self->idx_routes_x[IS_IPv4], + nmp_object_stackinit (&obj_stack, NMP_OBJECT_TYPE_IP_ROUTE (IS_IPv4), needle)); +} + const NMDedupMultiHeadEntry * nm_l3_config_data_lookup_objs (const NML3ConfigData *self, NMPObjectType obj_type) { @@ -1088,6 +1146,282 @@ nm_l3_config_data_cmp (const NML3ConfigData *a, const NML3ConfigData *b) /*****************************************************************************/ +static const NMPObject * +_data_get_direct_route_for_host (const NML3ConfigData *self, + int addr_family, + gconstpointer host, + guint32 route_table) +{ + const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); + const NMPObject *best_route_obj = NULL; + const NMPlatformIPXRoute *best_route = NULL; + const NMPObject *item_obj; + NMDedupMultiIter ipconf_iter; + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert_addr_family (addr_family); + nm_assert (host); + + if (nm_ip_addr_is_null (addr_family, host)) + return NULL; + + nm_l3_config_data_iter_obj_for_each (ipconf_iter, self, item_obj, NMP_OBJECT_TYPE_IP_ROUTE (IS_IPv4)) { + const NMPlatformIPXRoute *item = NMP_OBJECT_CAST_IPX_ROUTE (item_obj); + + if (nm_ip_addr_is_null (addr_family, + nm_platform_ip_route_get_gateway (addr_family, &item->rx))) + continue; + + if ( best_route + && best_route->rx.plen > item->rx.plen) + continue; + + if (nm_platform_route_table_uncoerce (item->rx.table_coerced, TRUE) != route_table) + continue; + + if (!nm_utils_ip_address_same_prefix (addr_family, host, item->rx.network_ptr, item->rx.plen)) + continue; + + if ( best_route + && best_route->rx.metric <= item->rx.metric) + continue; + + best_route_obj = item_obj; + best_route = item; + } + return best_route_obj; +} + +/*****************************************************************************/ + +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) +{ + 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; + NMPlatformIPXRoute rx; + NMDedupMultiIter iter; + in_addr_t network_4 = 0; + guint i; + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert_addr_family (addr_family); + + /* 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. */ + + if (!IS_IPv4) { + /* Pre-generate multicast route */ + rx.r6 = (NMPlatformIP6Route) { + .ifindex = self->ifindex, + .network.s6_addr[0] = 0xffu, + .plen = 8, + .table_coerced = nm_platform_route_table_coerce (RT_TABLE_LOCAL), + .type_coerced = nm_platform_route_type_coerce (RTN_UNICAST), + .metric = 256, + }; + nm_l3_config_data_add_route (self, addr_family, NULL, &rx.rx); + } + + nm_l3_config_data_iter_obj_for_each (iter, self, my_addr_obj, NMP_OBJECT_TYPE_IP_ADDRESS (IS_IPv4)) { + const NMPlatformIPXAddress *const my_addr = NMP_OBJECT_CAST_IPX_ADDRESS (my_addr_obj); + + if (my_addr->ax.external) + continue; + + if (IS_IPv4) { + nm_assert (my_addr->a4.plen <= 32); + if (my_addr->a4.plen == 0) + continue; + } + + if (IS_IPv4) { + rx.r4 = (NMPlatformIP4Route) { + .ifindex = self->ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_KERNEL, + .network = my_addr->a4.address, + .plen = 32, + .pref_src = my_addr->a4.address, + .type_coerced = nm_platform_route_type_coerce (RTN_LOCAL), + .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_HOST), + .table_coerced = nm_platform_route_table_coerce (is_vrf ? route_table : RT_TABLE_LOCAL), + }; + } else { + rx.r6 = (NMPlatformIP6Route) { + .ifindex = self->ifindex, + .network = my_addr->a6.address, + .plen = 128, + .type_coerced = nm_platform_route_type_coerce (RTN_LOCAL), + .metric = 0, + .table_coerced = nm_platform_route_table_coerce (is_vrf ? route_table : RT_TABLE_LOCAL), + }; + } + nm_l3_config_data_add_route (self, addr_family, NULL, &rx.rx); + + if (my_addr->ax.plen == 0) + continue; + + if (IS_IPv4) { + network_4 = nm_utils_ip4_address_clear_host_address (my_addr->a4.peer_address, + my_addr->a4.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->a4.plen == 32 + && my_addr->a4.address == my_addr->a4.peer_address) { + /* Kernel doesn't add device-routes for /32 addresses unless + * they have a peer. */ + continue; + } + } else { + if (NM_FLAGS_HAS (my_addr->a6.n_ifa_flags, IFA_F_NOPREFIXROUTE)) + continue; + } + + if (IS_IPv4) { + rx.r4 = (NMPlatformIP4Route) { + .ifindex = self->ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_KERNEL, + .network = network_4, + .plen = my_addr->a4.plen, + .pref_src = my_addr->a4.address, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + .scope_inv = nm_platform_route_scope_inv (NM_RT_SCOPE_LINK), + }; + 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; + + /* If we have an IPv6 peer, we add two /128 routes + * (unless, both addresses are identical). */ + for (routes_i = 0; routes_i < 2; routes_i++) { + struct in6_addr a6_stack; + const struct in6_addr *a6; + guint8 plen; + + if ( routes_i == 1 + && has_peer + && IN6_ARE_ADDR_EQUAL (&my_addr->a6.address, &my_addr->a6.peer_address)) + break; + + if (has_peer) { + if (routes_i == 0) + a6 = &my_addr->a6.address; + else + a6 = &my_addr->a6.peer_address; + plen = 128; + } else { + a6 = nm_utils_ip6_address_clear_host_address (&a6_stack, &my_addr->a6.address, my_addr->a6.plen); + plen = my_addr->a6.plen; + } + + rx.r6 = (NMPlatformIP6Route) { + .ifindex = self->ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_KERNEL, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + .network = *a6, + .plen = plen, + }; + nm_platform_ip_route_normalize (addr_family, &rx.rx); + nm_l3_config_data_add_route (self, addr_family, NULL, &rx.rx); + } + } + } + + nm_l3_config_data_iter_obj_for_each (iter, self, my_route_obj, NMP_OBJECT_TYPE_IP_ROUTE (IS_IPv4)) { + const NMPlatformIPXRoute *my_route = NMP_OBJECT_CAST_IPX_ROUTE (my_route_obj); + NMPObject *new_route; + NMPlatformIPXRoute *new_r; + const NMIPAddr *p_gateway; + + if ( !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (my_route) + || NM_IS_IP_CONFIG_SOURCE_RTPROT (my_route->rx.rt_source)) + continue; + + p_gateway = nm_platform_ip_route_get_gateway (addr_family, &my_route->rx); + + if (nm_ip_addr_is_null (addr_family, p_gateway)) + continue; + + if (_data_get_direct_route_for_host (self, + addr_family, + p_gateway, + nm_platform_route_table_uncoerce (my_route->rx.table_coerced, TRUE))) + continue; + + new_route = nmp_object_clone (my_route_obj, FALSE); + new_r = NMP_OBJECT_CAST_IPX_ROUTE (new_route); + if (IS_IPv4) { + new_r->r4.network = my_route->r4.gateway; + new_r->r4.plen = 32; + new_r->r4.gateway = 0; + } else { + new_r->r6.network = my_route->r6.gateway; + new_r->r6.plen = 128; + new_r->r6.gateway = in6addr_any; + } + + /* we cannot add the route right away, because that invalidates the iteration. */ + if (!extra_onlink_routes) + extra_onlink_routes = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + g_ptr_array_add (extra_onlink_routes, new_route); + } + if (extra_onlink_routes) { + for (i = 0; i < extra_onlink_routes->len; i++) { + nm_l3_config_data_add_route_full (self, + addr_family, + extra_onlink_routes->pdata[i], + NULL, + NM_L3_CONFIG_ADD_FLAGS_EXCLUSIVE, + NULL, + NULL); + } + } + + NM_SET_OUT (out_ip4_dev_route_blacklist, g_steal_pointer (&ip4_dev_route_blacklist)); +} + +/*****************************************************************************/ + static void _init_from_connection_ip (NML3ConfigData *self, int addr_family, diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index 09b46a2ce4..84c33db163 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -111,6 +111,13 @@ NML3ConfigData *nm_l3_config_data_new_combined (NMDedupMultiIndex *multi_idx, const NML3ConfigDatMergeInfo *const*merge_infos, guint merge_infos_len); +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); + /*****************************************************************************/ int nm_l3_config_data_get_ifindex (const NML3ConfigData *self); @@ -140,6 +147,13 @@ nm_l3_config_data_equal (const NML3ConfigData *a, const NML3ConfigData *b) /*****************************************************************************/ +const NMDedupMultiEntry *nm_l3_config_data_lookup_route_obj (const NML3ConfigData *self, + const NMPObject *needle); + +const NMDedupMultiEntry *nm_l3_config_data_lookup_route (const NML3ConfigData *self, + int addr_family, + const NMPlatformIPRoute *needle); + const NMDedupMultiHeadEntry *nm_l3_config_data_lookup_objs (const NML3ConfigData *self, NMPObjectType obj_type); static inline const NMDedupMultiHeadEntry * diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index f50d272746..5c041cf8f3 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -4936,6 +4936,8 @@ nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, nm_assert (NM_IS_PLATFORM (self)); nm_assert (ifindex > 0); + /* TODO: the blacklist should be maintained by NML3Cfg. */ + priv = NM_PLATFORM_GET_PRIVATE (self); /* first, expire all for current ifindex... */ From 1867746e6cb99cfa67c1bf76bc83a2999526003e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 28 Jul 2020 10:01:19 +0200 Subject: [PATCH 26/29] l3cfg: track route-table sync mode in NML3ConfigData Currently, we track the route table sync mode outside of the settings. But as it depends on configuration, we should track it in NML3ConfigData. --- src/nm-l3-config-data.c | 46 ++++++++++++++++++++++++++++++++++------- src/nm-l3-config-data.h | 4 ++++ src/nm-types.h | 2 ++ 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index 7adcf15ed7..b46e1fe2ad 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -96,6 +96,14 @@ struct _NML3ConfigData { int dns_priority_x[2]; }; + union { + struct { + NMIPRouteTableSyncMode route_table_sync_6; + NMIPRouteTableSyncMode route_table_sync_4; + }; + NMIPRouteTableSyncMode route_table_sync_x[2]; + }; + NMSettingConnectionMdns mdns; NMSettingConnectionLlmnr llmnr; @@ -359,13 +367,15 @@ nm_l3_config_data_new (NMDedupMultiIndex *multi_idx, self = g_slice_new (NML3ConfigData); *self = (NML3ConfigData) { - .ref_count = 1, - .ifindex = ifindex, - .multi_idx = nm_dedup_multi_index_ref (multi_idx), - .mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT, - .llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT, - .flags = NM_L3_CONFIG_DAT_FLAGS_NONE, - .metered = NM_TERNARY_DEFAULT, + .ref_count = 1, + .ifindex = ifindex, + .multi_idx = nm_dedup_multi_index_ref (multi_idx), + .mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT, + .llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT, + .flags = NM_L3_CONFIG_DAT_FLAGS_NONE, + .metered = NM_TERNARY_DEFAULT, + .route_table_sync_4 = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE, + .route_table_sync_6 = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE, }; _idx_type_init (&self->idx_addresses_4, NMP_OBJECT_TYPE_IP4_ADDRESS); @@ -1025,6 +1035,23 @@ nm_l3_config_data_set_llmnr (NML3ConfigData *self, return TRUE; } +gboolean +nm_l3_config_data_set_route_table_sync (NML3ConfigData *self, + int addr_family, + NMIPRouteTableSyncMode route_table_sync) +{ + const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert_addr_family (addr_family); + + if (self->route_table_sync_x[IS_IPv4] == route_table_sync) + return FALSE; + + self->route_table_sync_x [IS_IPv4] = route_table_sync; + return TRUE; +} + gboolean nm_l3_config_data_set_metered (NML3ConfigData *self, NMTernary metered) @@ -1123,6 +1150,8 @@ nm_l3_config_data_cmp (const NML3ConfigData *a, const NML3ConfigData *b) if (NM_FLAGS_ANY (a->flags, NM_L3_CONFIG_DAT_FLAGS_HAS_DNS_PRIORITY (IS_IPv4))) NM_CMP_DIRECT (a->dns_priority_x[IS_IPv4], b->dns_priority_x[IS_IPv4]); + + NM_CMP_DIRECT (a->route_table_sync_x[IS_IPv4], b->route_table_sync_x[IS_IPv4]); } NM_CMP_RETURN (_garray_inaddr_cmp (a->wins, b->wins, AF_INET)); @@ -1823,6 +1852,9 @@ _init_merge (NML3ConfigData *self, self->dns_priority_x[IS_IPv4] = src->dns_priority_x[IS_IPv4]; self->flags |= has_dns_priority_flag; } + + if (self->route_table_sync_x[IS_IPv4] == NM_IP_ROUTE_TABLE_SYNC_MODE_NONE) + self->route_table_sync_x[IS_IPv4] = src->route_table_sync_x[IS_IPv4]; } if (!NM_FLAGS_HAS (merge_flags, NM_L3_CONFIG_MERGE_FLAGS_NO_DNS)) { diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index 84c33db163..127f73e0d5 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -301,6 +301,10 @@ gboolean nm_l3_config_data_set_mdns (NML3ConfigData *self, gboolean nm_l3_config_data_set_llmnr (NML3ConfigData *self, NMSettingConnectionLlmnr llmnr); +gboolean nm_l3_config_data_set_route_table_sync (NML3ConfigData *self, + int addr_family, + NMIPRouteTableSyncMode route_table_sync); + gboolean nm_l3_config_data_set_metered (NML3ConfigData *self, NMTernary metered); diff --git a/src/nm-types.h b/src/nm-types.h index 9624bfa877..5d9eb67dc4 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -272,6 +272,7 @@ typedef enum { /** * NMIPRouteTableSyncMode: + * @NM_IP_ROUTE_TABLE_SYNC_MODE_NONE: indicate an invalid setting. * @NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN: only the main table is synced. For all * other tables, NM won't delete any extra routes. * @NM_IP_ROUTE_TABLE_SYNC_MODE_FULL: NM will sync all tables, except the @@ -280,6 +281,7 @@ typedef enum { * local table (255). */ typedef enum { + NM_IP_ROUTE_TABLE_SYNC_MODE_NONE = 0, NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN = 1, NM_IP_ROUTE_TABLE_SYNC_MODE_FULL = 2, NM_IP_ROUTE_TABLE_SYNC_MODE_ALL = 3, From c3798fbf4e70bc19f1da8f12fd6caba94c71f623 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 28 Jul 2020 10:43:33 +0200 Subject: [PATCH 27/29] l3cfg: add nm_l3cfg_platform_commit() --- src/nm-l3-config-data.c | 10 ++++++ src/nm-l3-config-data.h | 3 ++ src/nm-l3cfg.c | 68 ++++++++++++++++++++++++++++++++++++++++- src/nm-l3cfg.h | 5 +++ 4 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index b46e1fe2ad..f86c164bd3 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -1035,6 +1035,16 @@ nm_l3_config_data_set_llmnr (NML3ConfigData *self, return TRUE; } +NMIPRouteTableSyncMode +nm_l3_config_data_get_route_table_sync (const NML3ConfigData *self, + int addr_family) +{ + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert_addr_family (addr_family); + + return self->route_table_sync_x[NM_IS_IPv4 (addr_family)]; +} + gboolean nm_l3_config_data_set_route_table_sync (NML3ConfigData *self, int addr_family, diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index 127f73e0d5..856b55fa79 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -301,6 +301,9 @@ gboolean nm_l3_config_data_set_mdns (NML3ConfigData *self, gboolean nm_l3_config_data_set_llmnr (NML3ConfigData *self, NMSettingConnectionLlmnr llmnr); +NMIPRouteTableSyncMode nm_l3_config_data_get_route_table_sync (const NML3ConfigData *self, + int addr_family); + gboolean nm_l3_config_data_set_route_table_sync (NML3ConfigData *self, int addr_family, NMIPRouteTableSyncMode route_table_sync); diff --git a/src/nm-l3cfg.c b/src/nm-l3cfg.c index 3709ff7dbc..46b1c0f821 100644 --- a/src/nm-l3cfg.c +++ b/src/nm-l3cfg.c @@ -533,7 +533,6 @@ _l3cfg_combine_config (GArray *l3_config_datas, return nm_l3_config_data_seal (l3cfg); } -_nm_unused static gboolean _l3cfg_update_combined_config (NML3Cfg *self, const NML3ConfigData **out_old /* transfer reference */) @@ -556,6 +555,8 @@ _l3cfg_update_combined_config (NML3Cfg *self, if (nm_l3_config_data_equal (l3cfg, self->priv.p->combined_l3cfg)) return FALSE; + _LOGT ("desired IP configuration changed"); + l3cfg_old = g_steal_pointer (&self->priv.p->combined_l3cfg); self->priv.p->combined_l3cfg = nm_l3_config_data_seal (g_steal_pointer (&l3cfg)); NM_SET_OUT (out_old, nm_l3_config_data_ref (self->priv.p->combined_l3cfg)); @@ -564,6 +565,71 @@ _l3cfg_update_combined_config (NML3Cfg *self, /*****************************************************************************/ +gboolean +nm_l3cfg_platform_commit (NML3Cfg *self, + int addr_family) +{ + gs_unref_ptrarray GPtrArray *addresses = NULL; + gs_unref_ptrarray GPtrArray *routes = NULL; + gs_unref_ptrarray GPtrArray *routes_prune = NULL; + gs_unref_ptrarray GPtrArray *routes_temporary_not_available = NULL; + NMIPRouteTableSyncMode route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE; + gboolean success = TRUE; + int IS_IPv4; + + g_return_val_if_fail (NM_IS_L3CFG (self), FALSE); + + if (addr_family == AF_UNSPEC) { + if (!nm_l3cfg_platform_commit (self, AF_INET)) + success = FALSE; + if (!nm_l3cfg_platform_commit (self, AF_INET6)) + success = FALSE; + return success; + } + + _l3cfg_update_combined_config (self, NULL); + + IS_IPv4 = NM_IS_IPv4 (addr_family); + + _LOGT ("committing IPv%c configuration...", nm_utils_addr_family_to_char (addr_family)); + + if (self->priv.p->combined_l3cfg) { + addresses = nm_dedup_multi_objs_to_ptr_array_head (nm_l3_config_data_lookup_objs (self->priv.p->combined_l3cfg, + NMP_OBJECT_TYPE_IP_ADDRESS (IS_IPv4)), + NULL, NULL); + + routes = nm_dedup_multi_objs_to_ptr_array_head (nm_l3_config_data_lookup_objs (self->priv.p->combined_l3cfg, + NMP_OBJECT_TYPE_IP_ROUTE (IS_IPv4)), + NULL, NULL); + + route_table_sync = nm_l3_config_data_get_route_table_sync (self->priv.p->combined_l3cfg, addr_family); + } + + if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_NONE) + route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_ALL; + + routes_prune = nm_platform_ip_route_get_prune_list (self->priv.platform, + addr_family, + self->priv.ifindex, + route_table_sync); + + nm_platform_ip4_address_sync (self->priv.platform, + self->priv.ifindex, + addresses); + + if (!nm_platform_ip_route_sync (self->priv.platform, + addr_family, + self->priv.ifindex, + routes, + routes_prune, + &routes_temporary_not_available)) + success = FALSE; + + return success; +} + +/*****************************************************************************/ + static void set_property (GObject *object, guint prop_id, diff --git a/src/nm-l3cfg.h b/src/nm-l3cfg.h index 0c2b9c814a..7d77c50596 100644 --- a/src/nm-l3cfg.h +++ b/src/nm-l3cfg.h @@ -114,4 +114,9 @@ void nm_l3cfg_remove_config_all (NML3Cfg *self, gconstpointer tag, gboolean only_dirty); +/*****************************************************************************/ + +gboolean nm_l3cfg_platform_commit (NML3Cfg *self, + int addr_family); + #endif /* __NM_L3CFG_H__ */ From 3ac668bc1a4ae445e5eb86703c692bb0cace4fcd Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 28 Jul 2020 13:29:36 +0200 Subject: [PATCH 28/29] l3cfg: add support for tracking routes that are currently unavailable It doesn't yet do anything. More is needed. In particular, we need to keep track of routes that were externally deleted, then we can merge that with routes that are known to be currently unavailable. --- src/nm-l3cfg.c | 248 ++++++++++++++++++++++++++++++++++++++++++++++++- src/nm-l3cfg.h | 12 ++- 2 files changed, 254 insertions(+), 6 deletions(-) diff --git a/src/nm-l3cfg.c b/src/nm-l3cfg.c index 46b1c0f821..db4072f42d 100644 --- a/src/nm-l3cfg.c +++ b/src/nm-l3cfg.c @@ -25,11 +25,25 @@ NM_GOBJECT_PROPERTIES_DEFINE (NML3Cfg, PROP_IFINDEX, ); +enum { + SIGNAL_NOTIFY, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static GQuark signal_notify_quarks[_NM_L3_CONFIG_NOTIFY_TYPE_NUM]; + typedef struct _NML3CfgPrivate { GArray *property_emit_list; GArray *l3_config_datas; const NML3ConfigData *combined_l3cfg; + + GHashTable *routes_temporary_not_available_hash; + guint64 pseudo_timestamp_counter; + + guint routes_temporary_not_available_id; } NML3CfgPrivate; struct _NML3CfgClass { @@ -57,6 +71,23 @@ static void _property_emit_notify (NML3Cfg *self, NML3CfgPropertyEmitType emit_t /*****************************************************************************/ +static void +_l3cfg_emit_signal_notify (NML3Cfg *self, + NML3ConfigNotifyType notify_type, + gpointer pay_load) +{ + nm_assert (_NM_INT_NOT_NEGATIVE (notify_type)); + nm_assert (notify_type < G_N_ELEMENTS (signal_notify_quarks)); + + g_signal_emit (self, + signals[SIGNAL_NOTIFY], + signal_notify_quarks[notify_type], + (int) notify_type, + pay_load); +} + +/*****************************************************************************/ + static void _load_link (NML3Cfg *self, gboolean initial) { @@ -565,25 +596,209 @@ _l3cfg_update_combined_config (NML3Cfg *self, /*****************************************************************************/ +typedef struct { + const NMPObject *obj; + gint64 timestamp_msec; + bool dirty; +} RoutesTemporaryNotAvailableData; + +static void +_routes_temporary_not_available_data_free (gpointer user_data) +{ + RoutesTemporaryNotAvailableData *data = user_data; + + nmp_object_unref (data->obj); + nm_g_slice_free (data); +} + +#define ROUTES_TEMPORARY_NOT_AVAILABLE_MAX_AGE_MSEC ((gint64) 20000) + +static gboolean +_routes_temporary_not_available_timeout (gpointer user_data) +{ + RoutesTemporaryNotAvailableData *data; + NML3Cfg *self = NM_L3CFG (user_data); + GHashTableIter iter; + gint64 expiry_threshold_msec; + gboolean any_expired = FALSE; + gint64 now_msec; + gint64 oldest_msec; + + self->priv.p->routes_temporary_not_available_id = 0; + + if (!self->priv.p->routes_temporary_not_available_hash) + return G_SOURCE_REMOVE; + + /* we check the timeouts again. That is, because we allow to remove + * entries from routes_temporary_not_available_hash, without rescheduling + * out timeouts. */ + + now_msec = nm_utils_get_monotonic_timestamp_msec (); + + expiry_threshold_msec = now_msec - ROUTES_TEMPORARY_NOT_AVAILABLE_MAX_AGE_MSEC; + oldest_msec = G_MAXINT64; + + g_hash_table_iter_init (&iter, self->priv.p->routes_temporary_not_available_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &data, NULL)) { + if (data->timestamp_msec >= expiry_threshold_msec) { + any_expired = TRUE; + break; + } + if (data->timestamp_msec < oldest_msec) + oldest_msec = data->timestamp_msec; + } + + if (any_expired) { + /* a route expired. We emit a signal, but we don't schedule it again. That will + * only happen if the user calls nm_l3cfg_platform_commit() again. */ + _l3cfg_emit_signal_notify (self, NM_L3_CONFIG_NOTIFY_TYPE_ROUTES_TEMPORARY_NOT_AVAILABLE_EXPIRED, NULL); + return G_SOURCE_REMOVE; + } + + if (oldest_msec != G_MAXINT64) { + /* we have a timeout still. Reschedule. */ + self->priv.p->routes_temporary_not_available_id = g_timeout_add (oldest_msec + ROUTES_TEMPORARY_NOT_AVAILABLE_MAX_AGE_MSEC - now_msec, + _routes_temporary_not_available_timeout, + self); + } + return G_SOURCE_REMOVE; +} + +static gboolean +_routes_temporary_not_available_update (NML3Cfg *self, + int addr_family, + GPtrArray *routes_temporary_not_available_arr) +{ + + RoutesTemporaryNotAvailableData *data; + GHashTableIter iter; + gint64 oldest_msec; + gint64 now_msec; + gboolean prune_all = FALSE; + gboolean success = TRUE; + guint i; + + now_msec = nm_utils_get_monotonic_timestamp_msec (); + + if (nm_g_ptr_array_len (routes_temporary_not_available_arr) <= 0) { + prune_all = TRUE; + goto out_prune; + } + + if (self->priv.p->routes_temporary_not_available_hash) { + g_hash_table_iter_init (&iter, self->priv.p->routes_temporary_not_available_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &data, NULL)) { + if (NMP_OBJECT_GET_ADDR_FAMILY (data->obj) == addr_family) + data->dirty = TRUE; + } + } else { + self->priv.p->routes_temporary_not_available_hash = g_hash_table_new_full (nmp_object_indirect_id_hash, + nmp_object_indirect_id_equal, + _routes_temporary_not_available_data_free, + NULL); + } + + for (i = 0; i < routes_temporary_not_available_arr->len; i++) { + const NMPObject *o = routes_temporary_not_available_arr->pdata[i]; + char sbuf[1024]; + + nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_TYPE_IP_ROUTE (NM_IS_IPv4 (addr_family))); + + data = g_hash_table_lookup (self->priv.p->routes_temporary_not_available_hash, &o); + + if (data) { + if (!data->dirty) + continue; + + nm_assert ( data->timestamp_msec > 0 + && data->timestamp_msec <= now_msec); + + if (now_msec > data->timestamp_msec + ROUTES_TEMPORARY_NOT_AVAILABLE_MAX_AGE_MSEC) { + + /* timeout. Could not add this address. */ + _LOGW ("failure to add IPv%c route: %s", + nm_utils_addr_family_to_char (addr_family), + nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof (sbuf))); + success = FALSE; + continue; + } + + data->dirty = FALSE; + continue; + } + + _LOGT ("(temporarily) unable to add IPv%c route: %s", + nm_utils_addr_family_to_char (addr_family), + nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof (sbuf))); + + data = g_slice_new (RoutesTemporaryNotAvailableData); + *data = (RoutesTemporaryNotAvailableData) { + .obj = nmp_object_ref (o), + .timestamp_msec = now_msec, + .dirty = FALSE, + }; + g_hash_table_add (self->priv.p->routes_temporary_not_available_hash, data); + } + +out_prune: + oldest_msec = G_MAXINT64; + + if (self->priv.p->routes_temporary_not_available_hash) { + g_hash_table_iter_init (&iter, self->priv.p->routes_temporary_not_available_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &data, NULL)) { + nm_assert ( NMP_OBJECT_GET_ADDR_FAMILY (data->obj) == addr_family + || !data->dirty); + if ( !prune_all + && !data->dirty) { + if (data->timestamp_msec < oldest_msec) + oldest_msec = data->timestamp_msec; + continue; + } + g_hash_table_iter_remove (&iter); + } + if (oldest_msec != G_MAXINT64) + nm_clear_pointer (&self->priv.p->routes_temporary_not_available_hash, g_hash_table_unref); + } + + nm_clear_g_source (&self->priv.p->routes_temporary_not_available_id); + if (oldest_msec != G_MAXINT64) { + nm_assert (oldest_msec + ROUTES_TEMPORARY_NOT_AVAILABLE_MAX_AGE_MSEC < now_msec); + self->priv.p->routes_temporary_not_available_id = g_timeout_add (oldest_msec + ROUTES_TEMPORARY_NOT_AVAILABLE_MAX_AGE_MSEC - now_msec, + _routes_temporary_not_available_timeout, + self); + } + + return success; +} + +/*****************************************************************************/ + gboolean nm_l3cfg_platform_commit (NML3Cfg *self, - int addr_family) + int addr_family, + gboolean *out_final_failure_for_temporary_not_available) { gs_unref_ptrarray GPtrArray *addresses = NULL; gs_unref_ptrarray GPtrArray *routes = NULL; gs_unref_ptrarray GPtrArray *routes_prune = NULL; - gs_unref_ptrarray GPtrArray *routes_temporary_not_available = NULL; + gs_unref_ptrarray GPtrArray *routes_temporary_not_available_arr = NULL; NMIPRouteTableSyncMode route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE; + gboolean final_failure_for_temporary_not_available = FALSE; gboolean success = TRUE; int IS_IPv4; g_return_val_if_fail (NM_IS_L3CFG (self), FALSE); if (addr_family == AF_UNSPEC) { - if (!nm_l3cfg_platform_commit (self, AF_INET)) + gboolean final_failure_for_temporary_not_available_6 = FALSE; + + if (!nm_l3cfg_platform_commit (self, AF_INET, &final_failure_for_temporary_not_available)) success = FALSE; - if (!nm_l3cfg_platform_commit (self, AF_INET6)) + if (!nm_l3cfg_platform_commit (self, AF_INET6, &final_failure_for_temporary_not_available_6)) success = FALSE; + NM_SET_OUT (out_final_failure_for_temporary_not_available, + ( final_failure_for_temporary_not_available + || final_failure_for_temporary_not_available_6)); return success; } @@ -622,9 +837,16 @@ nm_l3cfg_platform_commit (NML3Cfg *self, self->priv.ifindex, routes, routes_prune, - &routes_temporary_not_available)) + &routes_temporary_not_available_arr)) success = FALSE; + final_failure_for_temporary_not_available = FALSE; + if (!_routes_temporary_not_available_update (self, + addr_family, + routes_temporary_not_available_arr)) + final_failure_for_temporary_not_available = TRUE; + + NM_SET_OUT (out_final_failure_for_temporary_not_available, final_failure_for_temporary_not_available); return success; } @@ -705,6 +927,9 @@ finalize (GObject *object) nm_assert (nm_g_array_len (self->priv.p->property_emit_list) == 0u); + nm_clear_g_source (&self->priv.p->routes_temporary_not_available_id); + nm_clear_pointer (&self->priv.p->routes_temporary_not_available_hash, g_hash_table_unref); + g_clear_object (&self->priv.netns); g_clear_object (&self->priv.platform); @@ -744,4 +969,17 @@ nm_l3cfg_class_init (NML3CfgClass *klass) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + signals[SIGNAL_NOTIFY] = + g_signal_new (NM_L3CFG_SIGNAL_NOTIFY, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_DETAILED + | G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, + 2, + G_TYPE_INT /* NML3ConfigNotifyType */, + G_TYPE_POINTER /* pay-load */ ); + + signal_notify_quarks[NM_L3_CONFIG_NOTIFY_TYPE_ROUTES_TEMPORARY_NOT_AVAILABLE_EXPIRED] = g_quark_from_static_string (NM_L3_CONFIG_NOTIFY_TYPE_ROUTES_TEMPORARY_NOT_AVAILABLE_EXPIRED_DETAIL); } diff --git a/src/nm-l3cfg.h b/src/nm-l3cfg.h index 7d77c50596..fefc3e5f0f 100644 --- a/src/nm-l3cfg.h +++ b/src/nm-l3cfg.h @@ -16,6 +16,15 @@ #define NM_L3CFG_NETNS "netns" #define NM_L3CFG_IFINDEX "ifindex" +#define NM_L3CFG_SIGNAL_NOTIFY "l3cfg-notify" + +typedef enum { + NM_L3_CONFIG_NOTIFY_TYPE_ROUTES_TEMPORARY_NOT_AVAILABLE_EXPIRED, + _NM_L3_CONFIG_NOTIFY_TYPE_NUM, +} NML3ConfigNotifyType; + +#define NM_L3_CONFIG_NOTIFY_TYPE_ROUTES_TEMPORARY_NOT_AVAILABLE_EXPIRED_DETAIL "routes-temporary-not-available" + struct _NML3CfgPrivate; struct _NML3Cfg { @@ -117,6 +126,7 @@ void nm_l3cfg_remove_config_all (NML3Cfg *self, /*****************************************************************************/ gboolean nm_l3cfg_platform_commit (NML3Cfg *self, - int addr_family); + int addr_family, + gboolean *out_final_failure_for_temporary_not_available); #endif /* __NM_L3CFG_H__ */ From e50597559b22ab5a16ab00924f7aba178d1c95f8 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 29 Jul 2020 08:39:12 +0200 Subject: [PATCH 29/29] l3cfg: track externally removed addresses/routes We want to allow the user to externally remove IP addresses and routes, and NetworkManager not re-adding them until a full reapply happens. For that, we need to keep track of IP addresses that were present, but no longer are. --- src/nm-l3-config-data.c | 50 +++++-- src/nm-l3-config-data.h | 8 + src/nm-l3cfg.c | 323 ++++++++++++++++++++++++++++++++++++++-- src/nm-l3cfg.h | 23 +++ src/nm-netns.c | 16 +- 5 files changed, 389 insertions(+), 31 deletions(-) diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c index f86c164bd3..f384ae2343 100644 --- a/src/nm-l3-config-data.c +++ b/src/nm-l3-config-data.c @@ -528,36 +528,60 @@ nm_l3_config_data_lookup_route (const NML3ConfigData *self, nmp_object_stackinit (&obj_stack, NMP_OBJECT_TYPE_IP_ROUTE (IS_IPv4), needle)); } -const NMDedupMultiHeadEntry * -nm_l3_config_data_lookup_objs (const NML3ConfigData *self, NMPObjectType obj_type) +const NMDedupMultiIdxType * +nm_l3_config_data_lookup_index (const NML3ConfigData *self, NMPObjectType obj_type) { - const DedupMultiIdxType *idx; - nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); switch (obj_type) { case NMP_OBJECT_TYPE_IP4_ADDRESS: - idx = &self->idx_addresses_4; - break; + return &self->idx_addresses_4.parent; case NMP_OBJECT_TYPE_IP6_ADDRESS: - idx = &self->idx_addresses_6; - break; + return &self->idx_addresses_6.parent; case NMP_OBJECT_TYPE_IP4_ROUTE: - idx = &self->idx_routes_4; - break; + return &self->idx_routes_4.parent; case NMP_OBJECT_TYPE_IP6_ROUTE: - idx = &self->idx_routes_6; - break; + return &self->idx_routes_6.parent; default: nm_assert_not_reached (); return NULL; } +} - return nm_dedup_multi_index_lookup_head (self->multi_idx, &idx->parent, NULL); +const NMDedupMultiHeadEntry * +nm_l3_config_data_lookup_objs (const NML3ConfigData *self, NMPObjectType obj_type) +{ + return nm_dedup_multi_index_lookup_head (self->multi_idx, + nm_l3_config_data_lookup_index (self, obj_type), + NULL); +} + +const NMDedupMultiEntry * +nm_l3_config_data_lookup_obj (const NML3ConfigData *self, + const NMPObject *obj) +{ + const NMDedupMultiIdxType *idx; + + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); + + idx = nm_l3_config_data_lookup_index (self, + NMP_OBJECT_GET_TYPE (obj)); + + return nm_dedup_multi_index_lookup_obj (self->multi_idx, + idx, + obj); } /*****************************************************************************/ +NMDedupMultiIndex * +nm_l3_config_data_get_multi_idx (const NML3ConfigData *self) +{ + nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE)); + + return self->multi_idx; +} + int nm_l3_config_data_get_ifindex (const NML3ConfigData *self) { diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h index 856b55fa79..59fbffce66 100644 --- a/src/nm-l3-config-data.h +++ b/src/nm-l3-config-data.h @@ -122,6 +122,8 @@ 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) { @@ -147,6 +149,12 @@ nm_l3_config_data_equal (const NML3ConfigData *a, const NML3ConfigData *b) /*****************************************************************************/ +const NMDedupMultiIdxType *nm_l3_config_data_lookup_index (const NML3ConfigData *self, + NMPObjectType obj_type); + +const NMDedupMultiEntry *nm_l3_config_data_lookup_obj (const NML3ConfigData *self, + const NMPObject *obj); + const NMDedupMultiEntry *nm_l3_config_data_lookup_route_obj (const NML3ConfigData *self, const NMPObject *needle); diff --git a/src/nm-l3cfg.c b/src/nm-l3cfg.c index db4072f42d..5e0890ded2 100644 --- a/src/nm-l3cfg.c +++ b/src/nm-l3cfg.c @@ -41,8 +41,26 @@ typedef struct _NML3CfgPrivate { GHashTable *routes_temporary_not_available_hash; + GHashTable *externally_removed_objs_hash; + guint64 pseudo_timestamp_counter; + union { + struct { + guint externally_removed_objs_cnt_addresses_6; + guint externally_removed_objs_cnt_addresses_4; + }; + guint externally_removed_objs_cnt_addresses_x[2]; + }; + + union { + struct { + guint externally_removed_objs_cnt_routes_6; + guint externally_removed_objs_cnt_routes_4; + }; + guint externally_removed_objs_cnt_routes_x[2]; + }; + guint routes_temporary_not_available_id; } NML3CfgPrivate; @@ -71,6 +89,15 @@ static void _property_emit_notify (NML3Cfg *self, NML3CfgPropertyEmitType emit_t /*****************************************************************************/ +static +NM_UTILS_ENUM2STR_DEFINE (_l3_cfg_commit_type_to_string, NML3CfgCommitType, + 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"), +); + +/*****************************************************************************/ + static void _l3cfg_emit_signal_notify (NML3Cfg *self, NML3ConfigNotifyType notify_type, @@ -88,6 +115,188 @@ _l3cfg_emit_signal_notify (NML3Cfg *self, /*****************************************************************************/ +static guint * +_l3cfg_externally_removed_objs_counter (NML3Cfg *self, + NMPObjectType obj_type) +{ + switch (obj_type) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + return &self->priv.p->externally_removed_objs_cnt_addresses_4; + case NMP_OBJECT_TYPE_IP6_ADDRESS: + return &self->priv.p->externally_removed_objs_cnt_addresses_6; + case NMP_OBJECT_TYPE_IP4_ROUTE: + return &self->priv.p->externally_removed_objs_cnt_routes_4; + case NMP_OBJECT_TYPE_IP6_ROUTE: + return &self->priv.p->externally_removed_objs_cnt_routes_6; + default: + return nm_assert_unreachable_val (NULL); + } +} + +static void +_l3cfg_externally_removed_objs_drop (NML3Cfg *self, + int addr_family) +{ + const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); + GHashTableIter iter; + const NMPObject *obj; + + nm_assert (NM_IS_L3CFG (self)); + nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + + if (addr_family == AF_UNSPEC) { + self->priv.p->externally_removed_objs_cnt_addresses_4 = 0; + self->priv.p->externally_removed_objs_cnt_addresses_6 = 0; + self->priv.p->externally_removed_objs_cnt_routes_4 = 0; + self->priv.p->externally_removed_objs_cnt_routes_6 = 0; + if (g_hash_table_size (self->priv.p->externally_removed_objs_hash) > 0) + _LOGD ("externally-removed: untrack all"); + nm_clear_pointer (&self->priv.p->externally_removed_objs_hash, g_hash_table_unref); + return; + } + + if ( self->priv.p->externally_removed_objs_cnt_addresses_x[IS_IPv4] == 0 + && self->priv.p->externally_removed_objs_cnt_routes_x[IS_IPv4] == 0) + return; + + _LOGD ("externally-removed: untrack IPv%c", + nm_utils_addr_family_to_char (addr_family)); + + g_hash_table_iter_init (&iter, self->priv.p->externally_removed_objs_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &obj, NULL)) { + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + if (NMP_OBJECT_GET_ADDR_FAMILY (obj) != addr_family) + g_hash_table_iter_remove (&iter); + } + self->priv.p->externally_removed_objs_cnt_addresses_x[IS_IPv4] = 0; + self->priv.p->externally_removed_objs_cnt_routes_x[IS_IPv4] = 0; + + if ( self->priv.p->externally_removed_objs_cnt_addresses_x[!IS_IPv4] == 0 + && self->priv.p->externally_removed_objs_cnt_routes_x[!IS_IPv4] == 0) + nm_clear_pointer (&self->priv.p->externally_removed_objs_hash, g_hash_table_unref); +} + +static void +_l3cfg_externally_removed_objs_drop_unused (NML3Cfg *self) +{ + GHashTableIter h_iter; + const NMPObject *obj; + char sbuf[sizeof (_nm_utils_to_string_buffer)]; + + nm_assert (NM_IS_L3CFG (self)); + + if (!self->priv.p->externally_removed_objs_hash) + return; + + if (!self->priv.p->combined_l3cfg) { + _l3cfg_externally_removed_objs_drop (self, AF_UNSPEC); + return; + } + + g_hash_table_iter_init (&h_iter, self->priv.p->externally_removed_objs_hash); + while (g_hash_table_iter_next (&h_iter, (gpointer *) &obj, NULL)) { + if (!nm_l3_config_data_lookup_route_obj (self->priv.p->combined_l3cfg, + obj)) { + /* The object is no longer tracked in the configuration. + * The externally_removed_objs_hash is to prevent adding entires that were + * removed externally, so if we don't plan to add the entry, we no longer need to track + * it. */ + (*(_l3cfg_externally_removed_objs_counter (self, NMP_OBJECT_GET_TYPE (obj))))--; + g_hash_table_iter_remove (&h_iter); + _LOGD ("externally-removed: untrack %s", + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof (sbuf))); + } + } +} + +static void +_l3cfg_externally_removed_objs_track (NML3Cfg *self, + const NMPObject *obj, + gboolean is_removed) +{ + char sbuf[1000]; + + nm_assert (NM_IS_L3CFG (self)); + + if (!self->priv.p->combined_l3cfg) + return; + + if (!is_removed) { + /* the object is still (or again) present. It no longer gets hidden. */ + if (self->priv.p->externally_removed_objs_hash) { + if (g_hash_table_remove (self->priv.p->externally_removed_objs_hash, + obj)) { + (*(_l3cfg_externally_removed_objs_counter (self, + NMP_OBJECT_GET_TYPE (obj))))--; + _LOGD ("externally-removed: untrack %s", + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof (sbuf))); + } + } + return; + } + + if (!nm_l3_config_data_lookup_route_obj (self->priv.p->combined_l3cfg, + obj)) { + /* we don't care about this object, so there is nothing to hide hide */ + return; + } + + if (G_UNLIKELY (!self->priv.p->externally_removed_objs_hash)) { + self->priv.p->externally_removed_objs_hash = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal, + (GDestroyNotify) nmp_object_unref, + NULL); + } + + if (g_hash_table_add (self->priv.p->externally_removed_objs_hash, + (gpointer) nmp_object_ref (obj))) { + (*(_l3cfg_externally_removed_objs_counter (self, + NMP_OBJECT_GET_TYPE (obj))))++; + _LOGD ("externally-removed: track %s", + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof (sbuf))); + } +} + +static void +_l3cfg_externally_removed_objs_pickup (NML3Cfg *self, + int addr_family) +{ + const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); + NMDedupMultiIter iter; + const NMPObject *obj; + + if (!self->priv.p->combined_l3cfg) + return; + + nm_l3_config_data_iter_obj_for_each (iter, self->priv.p->combined_l3cfg, obj, NMP_OBJECT_TYPE_IP_ADDRESS (IS_IPv4)) { + if (!nm_platform_lookup_entry (self->priv.platform, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + obj)) + _l3cfg_externally_removed_objs_track (self, obj, TRUE); + } + nm_l3_config_data_iter_obj_for_each (iter, self->priv.p->combined_l3cfg, obj, NMP_OBJECT_TYPE_IP_ROUTE (IS_IPv4)) { + if (!nm_platform_lookup_entry (self->priv.platform, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + obj)) + _l3cfg_externally_removed_objs_track (self, obj, TRUE); + } +} + +static gboolean +_l3cfg_externally_removed_objs_filter (/* const NMDedupMultiObj * */ gconstpointer o, + gpointer user_data) +{ + const NMPObject *obj = o; + GHashTable *externally_removed_objs_hash = user_data; + + return !g_hash_table_contains (externally_removed_objs_hash, obj); +} + +/*****************************************************************************/ + static void _load_link (NML3Cfg *self, gboolean initial) { @@ -131,6 +340,26 @@ _nm_l3cfg_notify_platform_change_on_idle (NML3Cfg *self, guint32 obj_type_flags) _property_emit_notify (self, NM_L3CFG_PROPERTY_EMIT_TYPE_IP6_ROUTE); } +void +_nm_l3cfg_notify_platform_change (NML3Cfg *self, + NMPlatformSignalChangeType change_type, + const NMPObject *obj) +{ + nm_assert (NMP_OBJECT_IS_VALID (obj)); + + switch (NMP_OBJECT_GET_TYPE (obj)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + _l3cfg_externally_removed_objs_track (self, + obj, + change_type == NM_PLATFORM_SIGNAL_REMOVED); + default: + break; + } +} + /*****************************************************************************/ typedef struct { @@ -775,26 +1004,37 @@ out_prune: gboolean nm_l3cfg_platform_commit (NML3Cfg *self, + NML3CfgCommitType commit_type, int addr_family, gboolean *out_final_failure_for_temporary_not_available) { + nm_auto_unref_l3cfg const NML3ConfigData *l3cfg_old = NULL; gs_unref_ptrarray GPtrArray *addresses = NULL; gs_unref_ptrarray GPtrArray *routes = NULL; + gs_unref_ptrarray GPtrArray *addresses_prune = NULL; gs_unref_ptrarray GPtrArray *routes_prune = NULL; gs_unref_ptrarray GPtrArray *routes_temporary_not_available_arr = NULL; NMIPRouteTableSyncMode route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_NONE; gboolean final_failure_for_temporary_not_available = FALSE; + char sbuf_commit_type[50]; + gboolean combined_changed; gboolean success = TRUE; int IS_IPv4; g_return_val_if_fail (NM_IS_L3CFG (self), FALSE); + nm_assert (NM_IN_SET (commit_type, NM_L3_CFG_COMMIT_TYPE_REAPPLY, + NM_L3_CFG_COMMIT_TYPE_UPDATE, + NM_L3_CFG_COMMIT_TYPE_ASSUME)); + + if (commit_type == NM_L3_CFG_COMMIT_TYPE_REAPPLY) + _l3cfg_externally_removed_objs_drop (self, addr_family); if (addr_family == AF_UNSPEC) { gboolean final_failure_for_temporary_not_available_6 = FALSE; - if (!nm_l3cfg_platform_commit (self, AF_INET, &final_failure_for_temporary_not_available)) + if (!nm_l3cfg_platform_commit (self, AF_INET, commit_type, &final_failure_for_temporary_not_available)) success = FALSE; - if (!nm_l3cfg_platform_commit (self, AF_INET6, &final_failure_for_temporary_not_available_6)) + if (!nm_l3cfg_platform_commit (self, AF_INET6, commit_type, &final_failure_for_temporary_not_available_6)) success = FALSE; NM_SET_OUT (out_final_failure_for_temporary_not_available, ( final_failure_for_temporary_not_available @@ -802,20 +1042,47 @@ nm_l3cfg_platform_commit (NML3Cfg *self, return success; } - _l3cfg_update_combined_config (self, NULL); + _LOGT ("committing IPv%c configuration (%s)", + nm_utils_addr_family_to_char (addr_family), + _l3_cfg_commit_type_to_string (commit_type, sbuf_commit_type, sizeof (sbuf_commit_type))); + + combined_changed = _l3cfg_update_combined_config (self, &l3cfg_old); IS_IPv4 = NM_IS_IPv4 (addr_family); - _LOGT ("committing IPv%c configuration...", nm_utils_addr_family_to_char (addr_family)); + if (combined_changed) { + /* our combined configuration changed. We may track entries in externally_removed_objs_hash, + * which are not longer to be considered by our configuration. We need to forget about them. */ + _l3cfg_externally_removed_objs_drop_unused (self); + } + + if (commit_type == NM_L3_CFG_COMMIT_TYPE_ASSUME) { + /* we need to artificially pre-populate the externally remove hash. */ + _l3cfg_externally_removed_objs_pickup (self, addr_family); + } if (self->priv.p->combined_l3cfg) { + NMDedupMultiFcnSelectPredicate predicate; + + if ( commit_type != NM_L3_CFG_COMMIT_TYPE_REAPPLY + && self->priv.p->externally_removed_objs_cnt_addresses_x[IS_IPv4] > 0) + predicate = _l3cfg_externally_removed_objs_filter; + else + predicate = NULL; addresses = nm_dedup_multi_objs_to_ptr_array_head (nm_l3_config_data_lookup_objs (self->priv.p->combined_l3cfg, NMP_OBJECT_TYPE_IP_ADDRESS (IS_IPv4)), - NULL, NULL); + predicate, + self->priv.p->externally_removed_objs_hash); + if ( commit_type != NM_L3_CFG_COMMIT_TYPE_REAPPLY + && self->priv.p->externally_removed_objs_cnt_routes_x[IS_IPv4] > 0) + predicate = _l3cfg_externally_removed_objs_filter; + else + predicate = NULL; routes = nm_dedup_multi_objs_to_ptr_array_head (nm_l3_config_data_lookup_objs (self->priv.p->combined_l3cfg, NMP_OBJECT_TYPE_IP_ROUTE (IS_IPv4)), - NULL, NULL); + predicate, + self->priv.p->externally_removed_objs_hash); route_table_sync = nm_l3_config_data_get_route_table_sync (self->priv.p->combined_l3cfg, addr_family); } @@ -823,14 +1090,42 @@ nm_l3cfg_platform_commit (NML3Cfg *self, if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_NONE) route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_ALL; - routes_prune = nm_platform_ip_route_get_prune_list (self->priv.platform, - addr_family, - self->priv.ifindex, - route_table_sync); + if (commit_type == NM_L3_CFG_COMMIT_TYPE_REAPPLY) { + addresses_prune = nm_platform_ip_address_get_prune_list (self->priv.platform, + addr_family, + self->priv.ifindex, + TRUE); + routes_prune = nm_platform_ip_route_get_prune_list (self->priv.platform, + addr_family, + self->priv.ifindex, + route_table_sync); + } else if (commit_type == NM_L3_CFG_COMMIT_TYPE_UPDATE) { + /* during update, we do a cross with the previous configuration. + * + * Of course, if an entry is both to be pruned and to be added, then + * the latter wins. So, this works just nicely. */ + if (l3cfg_old) { + const NMDedupMultiHeadEntry *head_entry; - nm_platform_ip4_address_sync (self->priv.platform, - self->priv.ifindex, - addresses); + head_entry = nm_l3_config_data_lookup_objs (l3cfg_old, + NMP_OBJECT_TYPE_IP_ADDRESS (IS_IPv4)); + addresses_prune = nm_dedup_multi_objs_to_ptr_array_head (head_entry, + NULL, + NULL); + + head_entry = nm_l3_config_data_lookup_objs (l3cfg_old, + NMP_OBJECT_TYPE_IP_ROUTE (IS_IPv4)); + addresses_prune = nm_dedup_multi_objs_to_ptr_array_head (head_entry, + NULL, + NULL); + } + } + + nm_platform_ip_address_sync (self->priv.platform, + addr_family, + self->priv.ifindex, + addresses, + addresses_prune); if (!nm_platform_ip_route_sync (self->priv.platform, addr_family, @@ -930,6 +1225,8 @@ finalize (GObject *object) nm_clear_g_source (&self->priv.p->routes_temporary_not_available_id); nm_clear_pointer (&self->priv.p->routes_temporary_not_available_hash, g_hash_table_unref); + nm_clear_pointer (&self->priv.p->externally_removed_objs_hash, g_hash_table_unref); + g_clear_object (&self->priv.netns); g_clear_object (&self->priv.platform); diff --git a/src/nm-l3cfg.h b/src/nm-l3cfg.h index fefc3e5f0f..55a062156d 100644 --- a/src/nm-l3cfg.h +++ b/src/nm-l3cfg.h @@ -49,6 +49,10 @@ NML3Cfg *nm_l3cfg_new (NMNetns *netns, int ifindex); void _nm_l3cfg_notify_platform_change_on_idle (NML3Cfg *self, guint32 obj_type_flags); +void _nm_l3cfg_notify_platform_change (NML3Cfg *self, + NMPlatformSignalChangeType change_type, + const NMPObject *obj); + /*****************************************************************************/ static inline int @@ -125,7 +129,26 @@ void nm_l3cfg_remove_config_all (NML3Cfg *self, /*****************************************************************************/ +typedef enum { + /* 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 + * should not change. */ + NM_L3_CFG_COMMIT_TYPE_ASSUME, + + /* UPDATE means to add new addresses/routes, while also removing addresses/routes + * that are no longer present (but were previously configured by NetworkManager). + * Routes/addresses that were removed externally won't be re-added, and routes/addresses + * that are added externally won't be removed. */ + NM_L3_CFG_COMMIT_TYPE_UPDATE, + + /* This is a full sync. It configures the IP addresses/routes that are indicated, + * while removing the existing ones from the interface. */ + NM_L3_CFG_COMMIT_TYPE_REAPPLY, +} NML3CfgCommitType; + gboolean nm_l3cfg_platform_commit (NML3Cfg *self, + NML3CfgCommitType commit_type, int addr_family, gboolean *out_final_failure_for_temporary_not_available); diff --git a/src/nm-netns.c b/src/nm-netns.c index 5b170c036b..cf0e5f0b6b 100644 --- a/src/nm-netns.c +++ b/src/nm-netns.c @@ -196,6 +196,7 @@ _platform_signal_cb (NMPlatform *platform, NMNetns *self = NM_NETNS (*p_self); NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE (self); const NMPObjectType obj_type = obj_type_i; + const NMPlatformSignalChangeType change_type = change_type_i; L3CfgData *l3cfg_data; l3cfg_data = g_hash_table_lookup (priv->l3cfgs, &ifindex); @@ -204,12 +205,15 @@ _platform_signal_cb (NMPlatform *platform, l3cfg_data->signal_pending_flag |= nmp_object_type_to_flags (obj_type); - if (!c_list_is_empty (&l3cfg_data->signal_pending_lst)) - return; + if (c_list_is_empty (&l3cfg_data->signal_pending_lst)) { + c_list_link_tail (&priv->l3cfg_signal_pending_lst_head, &l3cfg_data->signal_pending_lst); + if (priv->signal_pending_idle_id == 0) + priv->signal_pending_idle_id = g_idle_add (_platform_signal_on_idle_cb, self); + } - c_list_link_tail (&priv->l3cfg_signal_pending_lst_head, &l3cfg_data->signal_pending_lst); - if (priv->signal_pending_idle_id == 0) - priv->signal_pending_idle_id = g_idle_add (_platform_signal_on_idle_cb, self); + _nm_l3cfg_notify_platform_change (l3cfg_data->l3cfg, + change_type, + NMP_OBJECT_UP_CAST (platform_object)); } /*****************************************************************************/ @@ -288,6 +292,8 @@ constructed (GObject *object) g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (_platform_signal_cb), &priv->_self_signal_user_data); g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_platform_signal_cb), &priv->_self_signal_user_data); g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_platform_signal_cb), &priv->_self_signal_user_data); + g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (_platform_signal_cb), &priv->_self_signal_user_data); + g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_platform_signal_cb), &priv->_self_signal_user_data); } NMNetns *