l3cfg: merge branch 'th/l3cfg-3'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/591
This commit is contained in:
Thomas Haller 2020-07-31 08:56:58 +02:00
commit e95b400d04
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
20 changed files with 2617 additions and 533 deletions

View file

@ -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. */

View file

@ -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);
}

View file

@ -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); \

View file

@ -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);

View file

@ -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) \

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

File diff suppressed because it is too large Load diff

View file

@ -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);

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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 *

View file

@ -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,

View file

@ -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 (known_addresses) {
const NMPObject *o;
if (!plat_addr)
continue;
o = g_hash_table_lookup (known_addresses_idx, plat_obj);
if (o) {
gboolean secondary;
plat_scope = ip6_address_scope (plat_addr);
if (cur_scope != plat_scope) {
nm_assert (cur_scope < plat_scope);
delete_remaining_addrs = FALSE;
cur_scope = plat_scope;
}
if (!known_subnets)
known_subnets = ip4_addr_subnets_build_index (known_addresses, FALSE, FALSE);
if (!delete_remaining_addrs) {
delete_remaining_addrs = TRUE;
for (; i_know < known_addresses_len; i_know++) {
const NMPlatformIP6Address *know_addr = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i_know]);
IP6AddrScope know_scope;
if (!know_addr)
continue;
know_scope = ip6_address_scope (know_addr);
if (know_scope < plat_scope)
continue;
if (IN6_ARE_ADDR_EQUAL (&plat_addr->address, &know_addr->address)) {
/* we have a match. Mark address as handled. */
i_know++;
delete_remaining_addrs = FALSE;
goto next_plat;
secondary = ip4_addr_subnets_is_secondary (o, known_subnets, known_addresses, NULL);
if (secondary == NM_FLAGS_HAS (plat_address->n_ifa_flags, IFA_F_SECONDARY)) {
/* if we have an existing known-address, with matching secondary role,
* do not delete the platform-address. */
continue;
}
}
}
/* plat_address has no match. Now delete_remaining_addrs is TRUE and we will
* delete all the remaining addresses with cur_scope. */
break;
nm_platform_ip4_address_delete (self,
ifindex,
plat_address->address,
plat_address->plen,
plat_address->peer_address);
if ( !ip4_addr_subnets_is_secondary (plat_obj, plat_subnets, plat_addresses, &addr_list)
&& addr_list) {
/* If we just deleted a primary addresses and there were
* secondary ones the kernel can do two things, depending on
* version and sysctl setting: delete also secondary addresses
* or promote a secondary to primary. Ensure that secondary
* addresses are deleted, so that we can start with a clean
* slate and add addresses in the right order. */
for (j = 1; j < addr_list->len; j++) {
const NMPObject **o;
o = ip4_addr_subnets_addr_list_get (addr_list, j);
nm_assert (o);
if (*o) {
const NMPlatformIP4Address *a;
a = NMP_OBJECT_CAST_IP4_ADDRESS (*o);
nm_platform_ip4_address_delete (self,
ifindex,
a->address,
a->plen,
a->peer_address);
nmp_object_unref (*o);
*o = NULL;
}
}
}
}
ip4_addr_subnets_destroy_index (plat_subnets, plat_addresses);
} else {
guint known_addresses_len;
IP6AddrScope cur_scope;
gboolean delete_remaining_addrs;
nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen);
g_ptr_array_sort_with_data (plat_addresses, ip6_address_scope_cmp, GINT_TO_POINTER (FALSE));
known_addresses_len = known_addresses ? known_addresses->len : 0;
/* First, compare every address whether it is still a "known address", that is, whether
* to keep it or to delete it.
*
* If we don't find a matching valid address in @known_addresses, we will delete
* plat_addr.
*
* Certain addresses, like temporary addresses, are ignored by this function
* if not run with full_sync. These addresses are usually not managed by NetworkManager
* directly, or at least, they are not managed via nm_platform_ip6_address_sync().
* Only in full_sync mode, we really want to get rid of them (usually, when we take
* the interface down).
*
* Note that we mark handled addresses by setting it to %NULL in @plat_addresses array. */
for (i_plat = 0; i_plat < plat_addresses->len; i_plat++) {
const NMPObject *plat_obj = plat_addresses->pdata[i_plat];
const NMPObject *know_obj;
const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_obj);
if (known_addresses_idx) {
know_obj = g_hash_table_lookup (known_addresses_idx, plat_obj);
if ( know_obj
&& plat_addr->plen == NMP_OBJECT_CAST_IP6_ADDRESS (know_obj)->plen) {
/* technically, plen is not part of the ID for IPv6 addresses and thus
* @plat_addr is essentially the same address as @know_addr (regrading
* its identity, not its other attributes).
* However, we cannot modify an existing addresses' plen without
* removing and readding it. Thus, only keep plat_addr, if the plen
* matches.
*
* keep this one, and continue */
continue;
}
}
nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen);
nmp_object_unref (g_steal_pointer (&plat_addresses->pdata[i_plat]));
}
/* Next, we must preserve the priority of the routes. That is, source address
* selection will choose addresses in the order as they are reported by kernel.
* Note that the order in @plat_addresses of the remaining matches is highest
* priority first.
* We need to compare this to the order of addresses with same scope in
* @known_addresses (which has lowest priority first).
*
* If we find a first discrepancy, we need to delete all remaining addresses
* with same scope from that point on, because below we must re-add all the
* addresses in the right order to get their priority right. */
cur_scope = IP6_ADDR_SCOPE_LOOPBACK;
delete_remaining_addrs = FALSE;
i_plat = plat_addresses->len;
i_know = 0;
while (i_plat > 0) {
const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[--i_plat]);
IP6AddrScope plat_scope;
if (!plat_addr)
continue;
plat_scope = ip6_address_scope (plat_addr);
if (cur_scope != plat_scope) {
nm_assert (cur_scope < plat_scope);
delete_remaining_addrs = FALSE;
cur_scope = plat_scope;
}
if (!delete_remaining_addrs) {
delete_remaining_addrs = TRUE;
for (; i_know < known_addresses_len; i_know++) {
const NMPlatformIP6Address *know_addr = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i_know]);
IP6AddrScope know_scope;
if (!know_addr)
continue;
know_scope = ip6_address_scope (know_addr);
if (know_scope < plat_scope)
continue;
if (IN6_ARE_ADDR_EQUAL (&plat_addr->address, &know_addr->address)) {
/* we have a match. Mark address as handled. */
i_know++;
delete_remaining_addrs = FALSE;
goto next_plat;
}
/* plat_address has no match. Now delete_remaining_addrs is TRUE and we will
* delete all the remaining addresses with cur_scope. */
break;
}
}
nm_platform_ip6_address_delete (self, ifindex, plat_addr->address, plat_addr->plen);
next_plat:
;
;
}
}
}
if (!known_addresses)
return TRUE;
if (IS_IPv4)
ip4_addr_subnets_destroy_index (known_subnets, known_addresses);
ifa_flags = nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS)
? IFA_F_NOPREFIXROUTE
: 0;
@ -4115,20 +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);
nm_assert (NMP_OBJECT_GET_TYPE (o) == NMP_OBJECT_TYPE_IP_ADDRESS (IS_IPv4));
if (!nm_platform_ip6_address_add (self, ifindex, known_address->address,
known_address->plen, known_address->peer_address,
lifetime, preferred,
ifa_flags | known_address->n_ifa_flags))
return FALSE;
known_address = NMP_OBJECT_CAST_IPX_ADDRESS (o);
lifetime = nm_utils_lifetime_get (known_address->ax.timestamp,
known_address->ax.lifetime,
known_address->ax.preferred,
now,
&preferred);
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;
@ -4192,6 +4149,49 @@ _err_inval_due_to_ipv6_tentative_pref_src (NMPlatform *self, const NMPObject *ob
return TRUE;
}
GPtrArray *
nm_platform_ip_address_get_prune_list (NMPlatform *self,
int addr_family,
int ifindex,
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,
@ -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... */

View file

@ -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);

View file

@ -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(),

View file

@ -556,19 +556,16 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMPObjectType obj_type)
#define NMP_OBJECT_CAST_TFILTER(obj) _NMP_OBJECT_CAST (obj, tfilter, NMP_OBJECT_TYPE_TFILTER)
#define NMP_OBJECT_CAST_LNK_WIREGUARD(obj) _NMP_OBJECT_CAST (obj, lnk_wireguard, NMP_OBJECT_TYPE_LNK_WIREGUARD)
static inline int
NMP_OBJECT_TYPE_TO_ADDR_FAMILY (NMPObjectType obj_type)
{
return nmp_class_from_type (obj_type)->addr_family;
}
static inline int
NMP_OBJECT_GET_ADDR_FAMILY (const NMPObject *obj)
{
switch (NMP_OBJECT_GET_TYPE (obj)) {
case NMP_OBJECT_TYPE_IP4_ADDRESS:
case NMP_OBJECT_TYPE_IP4_ROUTE:
return AF_INET;
case NMP_OBJECT_TYPE_IP6_ADDRESS:
case NMP_OBJECT_TYPE_IP6_ROUTE:
return AF_INET6;
default:
return AF_UNSPEC;
}
return NMP_OBJECT_GET_CLASS (obj)->addr_family;
}
static inline const NMPObject *
@ -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);

View file

@ -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)
{