summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2020-07-31 08:56:58 +0200
committerThomas Haller <thaller@redhat.com>2020-07-31 08:56:58 +0200
commite95b400d0408f0cdae80e91598eb38dbd90f5383 (patch)
treed676b0fd396740cb2e3b3d47b936585b47d2a8d4
parenta938f4f018f6fc9b35297b2320c5e295742ab087 (diff)
parente50597559b22ab5a16ab00924f7aba178d1c95f8 (diff)
downloadNetworkManager-e95b400d0408f0cdae80e91598eb38dbd90f5383.tar.gz
l3cfg: merge branch 'th/l3cfg-3'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/591
-rw-r--r--libnm-core/tests/test-general.c13
-rw-r--r--shared/nm-glib-aux/nm-dedup-multi.c7
-rw-r--r--shared/nm-glib-aux/nm-macros-internal.h10
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.h26
-rw-r--r--shared/nm-std-aux/nm-std-aux.h21
-rw-r--r--src/devices/nm-device.c5
-rw-r--r--src/nm-core-utils.h29
-rw-r--r--src/nm-ip4-config.c49
-rw-r--r--src/nm-ip6-config.c72
-rw-r--r--src/nm-l3-config-data.c1008
-rw-r--r--src/nm-l3-config-data.h205
-rw-r--r--src/nm-l3cfg.c941
-rw-r--r--src/nm-l3cfg.h67
-rw-r--r--src/nm-netns.c16
-rw-r--r--src/nm-types.h7
-rw-r--r--src/platform/nm-platform.c574
-rw-r--r--src/platform/nm-platform.h50
-rw-r--r--src/platform/nmp-object.c22
-rw-r--r--src/platform/nmp-object.h20
-rw-r--r--src/platform/tests/test-platform-general.c8
20 files changed, 2617 insertions, 533 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-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);
}
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-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h
index 2f6681cb70..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
@@ -1547,6 +1553,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,
@@ -1873,6 +1885,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);
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) \
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;
diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h
index 5d36bd593c..8b5e69916d 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)))
@@ -457,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);
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index 4a9026cdb3..a31b24c84f 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)
{
@@ -623,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,
@@ -684,9 +651,21 @@ 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 (_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;
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;
diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c
index 23d2dd0fae..f384ae2343 100644
--- a/src/nm-l3-config-data.c
+++ b/src/nm-l3-config-data.c
@@ -6,6 +6,7 @@
#include <linux/if.h>
#include <linux/if_addr.h>
+#include <linux/rtnetlink.h>
#include "nm-core-internal.h"
#include "platform/nm-platform.h"
@@ -46,7 +47,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 +84,10 @@ struct _NML3ConfigData {
GPtrArray *dns_options_x[2];
};
+ int ifindex;
+
+ int ref_count;
+
union {
struct {
int dns_priority_6;
@@ -88,14 +96,22 @@ 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;
- int ifindex;
+ NML3ConfigDatFlags flags;
- int ref_count;
+ guint32 mtu;
- NML3ConfigDatFlags flags;
+ NMTernary metered:3;
bool is_sealed:1;
};
@@ -118,7 +134,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);
@@ -136,6 +152,44 @@ _garray_inaddr_clone (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,
@@ -183,6 +237,50 @@ _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 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
@@ -216,7 +314,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
@@ -269,12 +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,
+ .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);
@@ -288,7 +389,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 +397,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 +406,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 +414,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 +426,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.
@@ -346,7 +447,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);
@@ -362,45 +464,128 @@ 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);
}
/*****************************************************************************/
-const NMDedupMultiHeadEntry *
-nm_l3_config_data_lookup_objs (const NML3ConfigData *self, NMPObjectType obj_type)
+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)
{
- const DedupMultiIdxType *idx;
+ gboolean IS_IPv4;
- nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE));
+ 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 NMDedupMultiIdxType *
+nm_l3_config_data_lookup_index (const NML3ConfigData *self, NMPObjectType obj_type)
+{
+ 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)
{
- nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE));
+ nm_assert (_NM_IS_L3_CONFIG_DATA (self, TRUE));
return self->ifindex;
}
@@ -410,7 +595,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,8 +605,9 @@ 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));
+ nm_assert (!NM_FLAGS_ANY (mask, ~NM_L3_CONFIG_DAT_FLAGS_IGNORE_MERGE_NO_DEFAULT_ROUTES));
self->flags = (self->flags & ~mask)
| (flags & mask);
@@ -435,8 +621,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 */)
{
@@ -444,6 +629,8 @@ _l3_config_data_add_obj (NMDedupMultiIndex *multi_idx,
const NMDedupMultiEntry *entry_old;
const NMDedupMultiEntry *entry_new;
+ 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);
@@ -474,14 +661,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:
@@ -532,7 +722,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,
@@ -551,7 +741,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;
}
@@ -608,16 +798,17 @@ _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;
- 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
@@ -630,8 +821,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));
@@ -647,12 +837,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;
@@ -660,7 +851,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
@@ -676,8 +867,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)) {
@@ -736,7 +926,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,19 +939,39 @@ 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,
+ 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,
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 +982,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 +995,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);
@@ -808,19 +1018,471 @@ 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);
+
+ if ( self->dns_priority_x[IS_IPv4] == dns_priority
+ && NM_FLAGS_ANY (self->flags, has_dns_priority_flag))
+ return FALSE;
+
+ 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;
- nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE));
+ self->llmnr = llmnr;
+ 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);
- p_val = &self->dns_priority_x[NM_IS_IPv4 (addr_family)];
- if (*p_val == dns_priority)
+ 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,
+ 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;
- *p_val = dns_priority;
+ self->route_table_sync_x [IS_IPv4] = route_table_sync;
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;
+}
+
+/*****************************************************************************/
+
+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);
+
+ 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);
+ _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]));
+
+ 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));
+ 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);
+ 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():
+ *
+ * - multi_idx
+ * - ref_count
+ * - is_sealed
+ */
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+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
@@ -841,7 +1503,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));
@@ -874,7 +1536,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);
@@ -914,7 +1576,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);
@@ -968,7 +1630,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);
@@ -1006,8 +1668,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)
{
@@ -1017,10 +1677,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;
}
@@ -1061,7 +1717,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,
@@ -1076,8 +1732,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 ();
@@ -1097,7 +1752,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 *
@@ -1126,15 +1781,148 @@ 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 (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)) {
+ _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));
+ 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. */
@@ -1143,32 +1931,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;
+}
+
+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 (multi_idx);
+ nm_assert (ifindex > 0);
- 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_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_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_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_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->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);
+
+ 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 5b81b57e19..59fbffce66 100644
--- a/src/nm-l3-config-data.h
+++ b/src/nm-l3-config-data.h
@@ -14,10 +14,69 @@ 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 {
+ 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;
+
+/**
+ * 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);
@@ -39,8 +98,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);
@@ -49,8 +106,62 @@ 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);
+
+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);
+
+NMDedupMultiIndex *nm_l3_config_data_get_multi_idx (const NML3ConfigData *self);
+
+static inline gboolean
+NM_IS_L3_CONFIG_DATA (const NML3ConfigData *self)
+{
+ /* NML3ConfigData is not an NMObject, so we cannot ask which type it has.
+ * 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;
+}
+
+/*****************************************************************************/
+
+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 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);
+
+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 *
@@ -102,10 +213,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,
@@ -128,43 +235,93 @@ 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_set_mdns (NML3ConfigData *self,
+ NMSettingConnectionMdns mdns);
+
+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);
+
+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);
@@ -172,6 +329,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);
diff --git a/src/nm-l3cfg.c b/src/nm-l3cfg.c
index aefaef1cd8..5e0890ded2 100644
--- a/src/nm-l3cfg.c
+++ b/src/nm-l3cfg.c
@@ -10,13 +10,58 @@
/*****************************************************************************/
+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,
);
+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;
+
+ 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;
struct _NML3CfgClass {
@@ -44,6 +89,214 @@ 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,
+ 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 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)
{
@@ -87,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 {
@@ -226,6 +499,654 @@ 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);
+}
+
+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;
+
+ _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));
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+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,
+ 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, commit_type, &final_failure_for_temporary_not_available))
+ success = FALSE;
+ 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
+ || final_failure_for_temporary_not_available_6));
+ return success;
+ }
+
+ _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);
+
+ 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)),
+ 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)),
+ 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);
+ }
+
+ if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_NONE)
+ route_table_sync = NM_IP_ROUTE_TABLE_SYNC_MODE_ALL;
+
+ 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;
+
+ 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,
+ self->priv.ifindex,
+ routes,
+ routes_prune,
+ &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;
+}
+
+/*****************************************************************************/
+
static void
set_property (GObject *object,
guint prop_id,
@@ -301,9 +1222,16 @@ 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);
+
+ 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);
+ nm_clear_pointer (&self->priv.p->combined_l3cfg, nm_l3_config_data_unref);
+
nm_clear_pointer (&self->priv.pllink, nmp_object_unref);
_LOGT ("finalized");
@@ -338,4 +1266,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 7fc4b4b7d2..55a062156d 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))
@@ -15,16 +16,26 @@
#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 {
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;
};
@@ -38,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
@@ -89,4 +104,52 @@ 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);
+
+/*****************************************************************************/
+
+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);
+
#endif /* __NM_L3CFG_H__ */
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 *
diff --git a/src/nm-types.h b/src/nm-types.h
index 6277e4d811..5d9eb67dc4 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,
@@ -267,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
@@ -275,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,
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index 7b1f87f189..5c041cf8f3 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) {
@@ -3743,168 +3746,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 +3782,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 +3794,13 @@ 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.
+ * @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.
*
* A convenience function to synchronize addresses for a specific interface
* with the least possible disturbance. It simply removes addresses that are
@@ -3961,152 +3809,230 @@ 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,
+ GPtrArray *addresses_prune)
{
- gs_unref_ptrarray GPtrArray *plat_addresses = NULL;
- gint32 now = nm_utils_get_monotonic_timestamp_sec ();
- guint i_plat, i_know;
+ const gint32 now = nm_utils_get_monotonic_timestamp_sec ();
+ const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family);
gs_unref_hashtable GHashTable *known_addresses_idx = NULL;
- NMPLookup lookup;
+ GPtrArray *plat_addresses;
+ GHashTable *known_subnets = NULL;
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
+ /* @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_IP6_ADDRESS,
- ifindex),
- NULL, NULL);
+ plat_addresses = addresses_prune;
- if (plat_addresses) {
- guint known_addresses_len;
- IP6AddrScope cur_scope;
- gboolean delete_remaining_addrs;
+ if (nm_g_ptr_array_len (plat_addresses) > 0) {
+ /* 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 (!plat_addr)
- continue;
+ 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);
- 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;
+ 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);
+ } else {
+ guint known_addresses_len;
+ IP6AddrScope cur_scope;
+ gboolean delete_remaining_addrs;
- 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;
+ g_ptr_array_sort_with_data (plat_addresses, ip6_address_scope_cmp, GINT_TO_POINTER (FALSE));
- if (!know_addr)
- continue;
+ known_addresses_len = known_addresses ? known_addresses->len : 0;
- know_scope = ip6_address_scope (know_addr);
- if (know_scope < plat_scope)
+ /* 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 (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;
-
- 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);
+ nmp_object_unref (g_steal_pointer (&plat_addresses->pdata[i_plat]));
}
- nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen);
+ /* 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 +4041,51 @@ 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);
-
- 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;
+ nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_TYPE_IP_ADDRESS (IS_IPv4));
+
+ 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);
+ nm_assert (lifetime > 0);
+
+ if (IS_IPv4) {
+ 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)) {
+ /* ignore error, for unclear reasons. */
+ }
+ } 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;
+ }
}
return TRUE;
@@ -4193,6 +4150,49 @@ _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,
+ gboolean exclude_ipv6_temporary_addrs)
+{
+ const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family);
+ const NMDedupMultiHeadEntry *head_entry;
+ NMPLookup lookup;
+ GPtrArray *result;
+ CList *iter;
+
+ 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 *
nm_platform_ip_route_get_prune_list (NMPlatform *self,
int addr_family,
int ifindex,
@@ -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... */
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 1a2e339100..84dcfbb4aa 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -1839,8 +1839,42 @@ 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,
+ GPtrArray *addresses_prune);
+
+GPtrArray *nm_platform_ip_address_get_prune_list (NMPlatform *self,
+ int addr_family,
+ 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)
+{
+ gs_unref_ptrarray GPtrArray *addresses_prune = NULL;
+
+ addresses_prune = nm_platform_ip_address_get_prune_list (self,
+ addr_family,
+ ifindex,
+ !full_sync);
+ return nm_platform_ip_address_sync (self, addr_family, ifindex, known_addresses, addresses_prune);
+}
+
+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);
@@ -1848,6 +1882,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);
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index 1cd61a8e7d..615c458ab7 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)
{
@@ -3027,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 c31548cb18..bf4ff42379 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -557,18 +557,15 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMPObjectType obj_type)
#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 *
@@ -674,6 +671,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);
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)
{