mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-10 10:30:19 +01:00
l3cfg: merge branch 'th/l3cfg-3'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/591
This commit is contained in:
commit
e95b400d04
20 changed files with 2617 additions and 533 deletions
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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); \
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
|||
941
src/nm-l3cfg.c
941
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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... */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue